/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
*/
/*
* Copyright 2014 Joyent, Inc.
*/
/*
* This program implements a small domain-specific language (DSL) for the
* generation of nvlists, and subsequent printing in JSON-formatted output.
* The test suite uses this tool to drive the JSON formatting routines in
* libnvpair(3LIB) for testing.
*/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <locale.h>
#include <libnvpair.h>
/*
* As we are parsing a language that allows the creation of arbitrarily nested
* state, i.e. both nested nvlists and arrays of nested nvlists, we store that
* state in a stack. The top frame in the stack represents the nested nvlist
* (or nvlists, for an array) that we are currently building.
*
* When creating an array, the "next" directive advances lw_pos and allocates a
* new nvlist. The "end" directive commits either the nvlist, or array of
* nvlists, into the parent nvlist. It then pops and frees the stack frame
* before returning control to the parser.
*/
typedef struct list_wrap {
char *lw_name;
int lw_pos;
} list_wrap_t;
int
{
int d = 0;
d++;
}
return (d);
}
{
abort();
return (out);
}
{
return (next);
}
/*
* Generic integer and floating point parsing routines:
*/
int
{
int64_t t;
errno = 0;
return (-1);
}
return (-1);
}
return (-1);
}
*val = t;
return (0);
}
int
{
uint64_t t;
errno = 0;
return (-1);
}
return (-1);
}
return (-1);
}
*val = t;
return (0);
}
int
{
double t;
errno = 0;
"double\n", in);
return (-1);
}
*val = t;
return (0);
}
/*
* Command-specific handlers for directives specified in the DSL input:
*/
char **);
static int
{
if (array) {
argc - 1) != 0) {
"nvlist_add_string_array\n");
return (-1);
}
} else {
return (-1);
}
}
return (0);
}
static int
{
if (array)
abort();
return (-1);
}
return (0);
}
static int
{
int i;
for (i = 1; i < argc; i++) {
} else {
argv[i]);
return (-1);
}
}
if (array) {
argc - 1) != 0) {
"nvlist_add_boolean_array\n");
return (-1);
}
} else {
"nvlist_add_boolean_value\n");
return (-1);
}
}
return (0);
}
/*
* The confluence of a strongly typed C API for libnvpair(3LIB) and the
* combinatorial explosion of both sizes and signedness is unfortunate. Rather
* than reproduce the same code over and over, this macro parses an integer,
* checks applicable bounds based on size and signedness, and stores the value
* (or array of values).
*/
int i; \
for (i = 1; i < argc; i++) { \
return (-1); \
} \
} \
if (array) { \
return (-1); \
} \
} else { \
arrval[0]) == -1) { \
return (-1); \
} \
} \
return (0);
static int
{
}
static int
{
}
static int
{
}
static int
{
}
static int
{
}
static int
{
}
static int
{
}
static int
{
}
static int
{
}
static int
{
double val;
if (array)
abort();
return (-1);
}
return (-1);
}
return (0);
}
static int
{
char *name;
return (-1);
}
/*
* This was an array of objects.
*/
nelems) != 0) {
"nvlist_add_nvlist_array\n");
return (-1);
}
} else {
/*
* This was a single object.
*/
abort();
return (-1);
}
}
return (0);
}
static int
{
"object array.\n");
return (-1);
}
return (-1);
}
0) != 0) {
return (-1);
}
return (0);
}
static int
{
return (-1);
}
return (0);
}
typedef struct command {
int cmd_min_args;
int cmd_max_args;
} command_t;
/*
* These are the commands we support in the testing DSL, and their
* handling functions:
*/
{ 0 }
};
/*
* This function determines which command we are executing, checks argument
* counts, and dispatches to the appropriate handler:
*/
static int
{
int ch;
continue;
" expects between %d and %d arguments,"
" but %d were provided.\n", command,
argc);
return (-1);
}
}
return (-1);
}
/*
* The primary state machine for parsing the input DSL is implemented in
* this function:
*/
typedef enum state {
} state_t;
int
{
char b[8192];
int bp;
int argc = 0;
int nhex = 0;
b[0] = '\0';
bp = 0;
for (;;) {
/*
* Signal an error if the file ends part way through a
* construct:
*/
"file\n");
return (-1);
} else if (c == EOF) {
return (0);
}
if (c == '\n')
line++;
switch (st) {
case STATE_REST:
if (isalpha(c) || c == '_') {
argc = 0;
bp = 0;
b[bp++] = c;
b[bp] = '\0';
st = STATE_COMMAND;
continue;
} else if (c == ' ' || c == '\t' || c == '\n') {
/*
* Ignore whitespace.
*/
continue;
} else if (c == '/') {
continue;
} else {
goto unexpected;
}
case STATE_C_COMMENT_0:
if (c != '*') {
goto unexpected;
}
continue;
case STATE_C_COMMENT_1:
if (c == '*') {
}
continue;
case STATE_C_COMMENT_2:
if (c == '/') {
st = STATE_REST;
} else if (c != '*') {
}
continue;
case STATE_COMMAND:
if (isalnum(c) || c == '_') {
b[bp++] = c;
b[bp] = '\0';
st = STATE_COMMAND;
continue;
} else if (isspace(c)) {
/*
* Start collecting arguments into 'b'
* after the command.
*/
st = STATE_ARG_FIND;
bp++;
continue;
} else if (c == ';') {
/*
* This line was _just_ a command,
* so break out and process now:
*/
goto execute;
} else {
goto unexpected;
}
case STATE_ARG_FIND:
if (isspace(c)) {
/*
* Whitespace, ignore.
*/
continue;
} else if (c == ';') {
/*
* Break out to process command.
*/
goto execute;
} else if (c == '"') {
b[bp] = '\0';
continue;
} else {
goto unexpected;
}
case STATE_ARG:
if (c == '"') {
"many args\n");
return (-1);
}
st = STATE_ARG_FIND;
continue;
} else if (c == '\n') {
"finished\n");
return (-1);
} else if (c == '\\') {
continue;
} else {
b[bp++] = c;
b[bp] = '\0';
continue;
}
case STATE_ARG_ESCAPE:
if (c == 'a') {
c = '\a';
} else if (c == 'b') {
c = '\b';
} else if (c == 'f') {
c = '\f';
} else if (c == 'n') {
c = '\n';
} else if (c == 'r') {
c = '\r';
} else if (c == 't') {
c = '\t';
} else if (c == 'v') {
c = '\v';
} else if (c == 'x') {
nhex = 0;
continue;
} else if (c != '\\' && c != '"') {
goto unexpected;
}
b[bp++] = c;
b[bp] = '\0';
continue;
case STATE_ARG_ESCAPE_HEX:
if (!isxdigit(c)) {
goto unexpected;
}
if (nhex++ >= 1) {
/*
* The hex escape pair is complete, parse
* the integer and insert it as a character:
*/
int x;
errno = 0;
errno != 0) {
goto unexpected;
}
b[bp++] = (char)x;
b[bp] = '\0';
}
continue;
}
/*
* We do not ever expect to break out of the switch block
* above. If we do, it's a programmer error.
*/
abort();
return (-1);
st = STATE_REST;
continue;
"character: %c\n", line, c);
return (-1);
}
}
/*
* Entry point:
*/
int
{
/*
* Be locale-aware. The JSON output functions will process multibyte
* characters in the current locale, and emit a correct JSON encoding
* for unprintable characters.
*/
goto out;
}
goto out;
/*
* Generate the list from the commands passed to us on stdin:
*/
goto out;
/*
* Print the resultant list, and a terminating newline:
*/
goto out;
rc = EXIT_SUCCESS;
out:
(void) list_wrap_pop_and_free(lw);
return (rc);
}