/* $OpenBSD$ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, 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.
*/
%{
#include <sys/types.h>
#include <ctype.h>
#include <errno.h>
#include <pwd.h>
#include <string.h>
#include <unistd.h>
#include "tmux.h"
static int yylex(void);
static int yyparse(void);
static int printflike(1,2) yyerror(const char *, ...);
static char *yylex_token(int);
static char *yylex_format(void);
struct cmd_parse_scope {
int flag;
TAILQ_ENTRY (cmd_parse_scope) entry;
};
struct cmd_parse_command {
char *name;
u_int line;
int argc;
char **argv;
TAILQ_ENTRY(cmd_parse_command) entry;
};
TAILQ_HEAD(cmd_parse_commands, cmd_parse_command);
struct cmd_parse_state {
FILE *f;
const char *buf;
size_t len;
size_t off;
int eof;
struct cmd_parse_input *input;
u_int escapes;
char *error;
struct cmd_parse_commands commands;
struct cmd_parse_scope *scope;
TAILQ_HEAD(, cmd_parse_scope) stack;
};
static struct cmd_parse_state parse_state;
static char *cmd_parse_get_error(const char *, u_int, const char *);
static void cmd_parse_free_command(struct cmd_parse_command *);
static void cmd_parse_free_commands(struct cmd_parse_commands *);
%}
%union
{
char *token;
struct {
int argc;
char **argv;
} arguments;
int flag;
struct {
int flag;
struct cmd_parse_commands commands;
} elif;
struct cmd_parse_commands commands;
struct cmd_parse_command *command;
}
%token ERROR
%token IF
%token ELSE
%token ELIF
%token ENDIF
%token <token> FORMAT TOKEN EQUALS
%type <token> argument expanded
%type <arguments> arguments
%type <flag> if_open if_elif
%type <elif> elif elif1
%type <commands> statements statement commands condition condition1
%type <command> command
%%
lines : /* empty */
| statements
{
struct cmd_parse_state *ps = &parse_state;
TAILQ_CONCAT(&ps->commands, &$1, entry);
}
statements : statement '\n'
{
TAILQ_INIT(&$$);
TAILQ_CONCAT(&$$, &$1, entry);
}
| statements statement '\n'
{
TAILQ_INIT(&$$);
TAILQ_CONCAT(&$$, &$1, entry);
TAILQ_CONCAT(&$$, &$2, entry);
}
statement : condition
{
struct cmd_parse_state *ps = &parse_state;
TAILQ_INIT(&$$);
if (ps->scope == NULL || ps->scope->flag)
TAILQ_CONCAT(&$$, &$1, entry);
else
cmd_parse_free_commands(&$1);
}
| assignment
{
TAILQ_INIT(&$$);
}
| commands
{
struct cmd_parse_state *ps = &parse_state;
TAILQ_INIT(&$$);
if (ps->scope == NULL || ps->scope->flag)
TAILQ_CONCAT(&$$, &$1, entry);
else
cmd_parse_free_commands(&$1);
}
expanded : FORMAT
{
struct cmd_parse_state *ps = &parse_state;
struct cmd_parse_input *pi = ps->input;
struct format_tree *ft;
struct client *c = pi->c;
struct cmd_find_state *fs;
int flags = FORMAT_NOJOBS;
if (cmd_find_valid_state(&pi->fs))
fs = &pi->fs;
else
fs = NULL;
ft = format_create(NULL, pi->item, FORMAT_NONE, flags);
if (fs != NULL)
format_defaults(ft, c, fs->s, fs->wl, fs->wp);
else
format_defaults(ft, c, NULL, NULL, NULL);
$$ = format_expand(ft, $1);
format_free(ft);
free($1);
}
assignment : /* empty */
| EQUALS
{
struct cmd_parse_state *ps = &parse_state;
int flags = ps->input->flags;
if ((~flags & CMD_PARSE_PARSEONLY) &&
(ps->scope == NULL || ps->scope->flag))
environ_put(global_environ, $1);
free($1);
}
if_open : IF expanded
{
struct cmd_parse_state *ps = &parse_state;
struct cmd_parse_scope *scope;
scope = xmalloc(sizeof *scope);
$$ = scope->flag = format_true($2);
free($2);
if (ps->scope != NULL)
TAILQ_INSERT_HEAD(&ps->stack, ps->scope, entry);
ps->scope = scope;
}
if_else : ELSE
{
struct cmd_parse_state *ps = &parse_state;
struct cmd_parse_scope *scope;
scope = xmalloc(sizeof *scope);
scope->flag = !ps->scope->flag;
free(ps->scope);
ps->scope = scope;
}
if_elif : ELIF expanded
{
struct cmd_parse_state *ps = &parse_state;
struct cmd_parse_scope *scope;
scope = xmalloc(sizeof *scope);
$$ = scope->flag = format_true($2);
free($2);
free(ps->scope);
ps->scope = scope;
}
if_close : ENDIF
{
struct cmd_parse_state *ps = &parse_state;
free(ps->scope);
ps->scope = TAILQ_FIRST(&ps->stack);
if (ps->scope != NULL)
TAILQ_REMOVE(&ps->stack, ps->scope, entry);
}
condition : if_open '\n' statements if_close
{
TAILQ_INIT(&$$);
if ($1)
TAILQ_CONCAT(&$$, &$3, entry);
else
cmd_parse_free_commands(&$3);
}
| if_open '\n' statements if_else '\n' statements if_close
{
TAILQ_INIT(&$$);
if ($1) {
TAILQ_CONCAT(&$$, &$3, entry);
cmd_parse_free_commands(&$6);
} else {
TAILQ_CONCAT(&$$, &$6, entry);
cmd_parse_free_commands(&$3);
}
}
| if_open '\n' statements elif if_close
{
TAILQ_INIT(&$$);
if ($1) {
TAILQ_CONCAT(&$$, &$3, entry);
cmd_parse_free_commands(&$4.commands);
} else if ($4.flag) {
TAILQ_CONCAT(&$$, &$4.commands, entry);
cmd_parse_free_commands(&$3);
} else {
cmd_parse_free_commands(&$3);
cmd_parse_free_commands(&$4.commands);
}
}
| if_open '\n' statements elif if_else '\n' statements if_close
{
TAILQ_INIT(&$$);
if ($1) {
TAILQ_CONCAT(&$$, &$3, entry);
cmd_parse_free_commands(&$4.commands);
cmd_parse_free_commands(&$7);
} else if ($4.flag) {
TAILQ_CONCAT(&$$, &$4.commands, entry);
cmd_parse_free_commands(&$3);
cmd_parse_free_commands(&$7);
} else {
TAILQ_CONCAT(&$$, &$7, entry);
cmd_parse_free_commands(&$3);
cmd_parse_free_commands(&$4.commands);
}
}
elif : if_elif '\n' statements
{
TAILQ_INIT(&$$.commands);
if ($1)
TAILQ_CONCAT(&$$.commands, &$3, entry);
else
cmd_parse_free_commands(&$3);
$$.flag = $1;
}
| if_elif '\n' statements elif
{
TAILQ_INIT(&$$.commands);
if ($1) {
$$.flag = 1;
TAILQ_CONCAT(&$$.commands, &$3, entry);
cmd_parse_free_commands(&$4.commands);
} else {
$$.flag = $4.flag;
TAILQ_CONCAT(&$$.commands, &$4.commands, entry);
cmd_parse_f