/*
* aegis - project change supervisor
* Copyright (C) 1997-1999, 2001-2008, 2011, 2012, 2014 Peter Miller
* Copyright (C) 2007 Walter Franzini
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
%{
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef DEBUG
#define YYDEBUG 1
#define YYERROR_VERBOSE 1
#endif
%}
%token AMIN
%token ANDAND
%token ATIME
%token BASELINE
%token BASE_REL
%token BIT_AND
%token BIT_OR
%token BIT_XOR
%token BRANCH
%token CHANGE
%token CMIN
%token COLON
%token COMMA
%token CTIME
%token CUR_REL
%token DEBUG_keyword
%token DELETE
%token DEVDIR
%token DIV
%token EQ
%token EXECUTE
%token FALSE_keyword
%token GE
%token GRANDPARENT
%token GT
%token HELP
%token JOIN
%token JUNK
%token LE
%token LIBRARY
%token LPAREN
%token LT
%token MINUS
%token MMIN
%token MOD
%token MTIME
%token MUL
%token NAME
%token NE
%token NEWER
%token NOT
%token NOW
%token NUMBER
%token OROR
%token PATH
%token PERM
%token PLUS
%token PRINT
%token PROJECT
%token QUESTION
%token REAL
%token RESOLVE
%token RESOLVE_NOT
%token RPAREN
%token SEMICOLON
%token SHIFT_LEFT
%token SHIFT_RIGHT
%token SSIZE
%token STRING
%token STRINGIZE
%token THIS
%token TILDE
%token TRACE
%token TRUE_keyword
%token TRUNK
%token TYPE
%token VERSION
%union
{
struct string_ty *lv_string;
struct string_list_ty *lv_string_list;
tree::pointer *lv_tree;
class tree_list *lv_tree_list;
long lv_number;
double lv_real;
diadic_t comparator;
}
%type comparator
%type NUMBER THIS
%type REAL
%type STRING
%type number_or_string
%type strings strings_or_dot
%type tree1 tree2 tree3 tree4 tree5 tree6 tree7
%type tree8 tree9 tree10 tree11 tree12 tree13 tree14
%type list
%type list_opt
%type exec_list
%left COMMA
%right QUESTION COLON
%left OROR
%left ANDAND
%left BIT_OR
%left BIT_XOR
%left BIT_AND
%left LT LE GT GE
%left SHIFT_LEFT SHIFT_RIGHT
%left PLUS MINUS JOIN
%left MUL DIV MOD TILDE
%right NOT unary
%right LPAREN RPAREN
%{
static int number_of_ops;
static string_list_ty *path;
static tree::pointer filter;
static string_ty *project_name;
static long change_number;
static int grandparent;
static string_ty *branch;
static int trunk;
static int baseline;
static int resolve;
static int debug;
static void
report_error(const rpt_value::pointer &vp)
{
trace(("%s\n", __PRETTY_FUNCTION__));
const rpt_value_error *rve =
dynamic_cast(vp.get());
if (!rve)
return;
sub_context_ty sc;
sc.var_set_string("MeSsaGe", rve->query());
sc.fatal_intl(i18n("$message"));
/* NOTREACHED */
}
static void
walker(void *, descend_message_ty msg, string_ty *path_unres,
string_ty *path_maybe, string_ty *path_res, struct stat *st)
{
trace(("%s\n", __PRETTY_FUNCTION__));
switch (msg)
{
case descend_message_file:
case descend_message_dir_before:
{
rpt_value::pointer vp =
filter->evaluate(path_unres, path_maybe, path_res, st);
if (vp->is_an_error())
report_error(vp);
}
break;
case descend_message_dir_after:
break;
}
}
static string_list_ty *stack;
static project *pp;
static change::pointer cp;
string_ty *
stack_relative(string_ty *fn)
{
trace(("%s\n", __PRETTY_FUNCTION__));
assert(stack);
os_become_orig();
string_ty *s1 = os_pathname(fn, 1);
os_become_undo();
string_ty *s2 = 0;
for (size_t k = 0; k < stack->size(); ++k)
{
s2 = os_below_dir((*stack)[k], s1);
if (s2)
break;
}
str_free(s1);
if (!s2)
{
sub_context_ty sc;
sc.var_set_string("File_Name", s1);
if (cp)
change_fatal(cp, &sc, i18n("$filename unrelated"));
project_fatal(pp, &sc, i18n("$filename unrelated"));
/* NOTREACHED */
}
if (s2->str_length == 0)
{
str_free(s2);
s2 = str_from_c(".");
}
return s2;
}
string_ty *
stack_nth(int n)
{
trace(("%s\n", __PRETTY_FUNCTION__));
assert(n >= 0);
assert(stack);
assert(stack->size());
if (!stack)
return 0;
if (n < 0 || n >= (int)stack->size())
return 0;
return (*stack)[n];
}
int
stack_eliminate(string_ty *filename)
{
trace(("%s\n", __PRETTY_FUNCTION__));
fstate_src_ty *src = pp->file_find(filename, view_path_simple);
if (!src)
return 0;
switch (src->action)
{
case file_action_create:
case file_action_modify:
break;
case file_action_remove:
return 1;
case file_action_insulate:
case file_action_transparent:
break;
}
return 0;
}
void
cmdline_grammar(int argc, char **argv)
{
trace(("%s\n", __PRETTY_FUNCTION__));
extern int yyparse(void);
size_t j;
cstate_ty *cstate_data;
int based;
/*
* parse the command line
*/
cmdline_lex_open(argc, argv);
number_of_ops = 0;
resolve = -1;
yyparse();
cmdline_lex_close();
/*
* reject illegal combinations of options
*/
if (grandparent)
{
if (branch)
{
mutually_exclusive_options
(
arglex_token_branch,
arglex_token_grandparent,
usage
);
}
if (trunk)
{
mutually_exclusive_options
(
arglex_token_trunk,
arglex_token_grandparent,
usage
);
}
branch = str_from_c("..");
}
if (trunk)
{
if (branch)
{
mutually_exclusive_options
(
arglex_token_branch,
arglex_token_trunk,
usage
);
}
branch = str_from_c("");
}
/*
* locate project data
*/
if (!project_name)
{
nstring n = user_ty::create()->default_project();
project_name = str_copy(n.get_ref());
}
pp = project_alloc(project_name);
str_free(project_name);
pp->bind_existing();
stack = new string_list_ty();
user_ty::pointer up = user_ty::create();
if (baseline)
{
if (change_number)
{
mutually_exclusive_options
(
arglex_token_branch,
arglex_token_change,
usage
);
}
/*
* Get the search path from the project.
*/
pp->search_path_get(stack, true);
cp.reset();
cstate_data = 0;
}
else
{
/*
* locate change data
*/
if (!change_number)
change_number = up->default_change(pp);
cp = change::create(pp, change_number);
cp->bind_existing();
cstate_data = cp->cstate_get();
if (cstate_data->state == cstate_state_completed)
{
/*
* Get the search path from the project.
*/
pp->search_path_get(stack, true);
cp.reset();
cstate_data = 0;
}
else
{
/*
* It is an error if the change is not in the
* being_developed state (if it does not have a
* directory).
*/
if (cstate_data->state < cstate_state_being_developed)
change_fatal(cp, 0, i18n("bad aefind state"));
/*
* Get the search path from the change.
*/
cp->search_path_get(stack, true);
}
}
/*
* resolve the path of each path
* 1. the absolute path of the file name is obtained
* 2. if the file is inside the development directory, ok
* 3. if the file is inside the baseline, ok
* 4. if neither, error
*/
assert(up);
based =
(
stack->size() >= 1
&&
(
up->relative_filename_preference
(
uconf_relative_filename_preference_current
)
==
uconf_relative_filename_preference_base
)
);
for (j = 0; j < path->size(); ++j)
{
string_ty *s0;
string_ty *s1;
string_ty *s2;
s0 = (*path)[j];
if (s0->str_text[0] == '/' || !based)
s1 = str_copy(s0);
else
s1 = os_path_join(stack->front(), s0);
str_free(s0);
s2 = stack_relative(s1);
assert(s2);
str_free(s1);
(*path)[j] = s2;
}
/*
* Optimize the tree.
*
* We can't do this as we go, because we don't know the search
* path until we finish parsing the command line.
*/
if (debug > 1)
{
filter->print();
printf("\n");
fflush(stdout);
}
filter = filter->optimize();
/*
* walk each of the directories in turn
*/
if (debug)
{
filter->print();
printf("\n");
fflush(stdout);
}
for (j = 0; j < path->size(); ++j)
descend((*path)[j], resolve, walker, 0);
/*
* None of the stuff is deleted.
* Assume program exits shortly.
*/
}
static tree::pointer
make_sure_has_side_effects(const tree::pointer &x)
{
trace(("%s\n", __PRETTY_FUNCTION__));
if (x->useful())
return x;
#if 0
/*
* Most of the time, this is exactly what users want, a file list.
* So we don't whine at them.
*/
error_intl
(
0,
i18n("warning: expression has no side effects, assuming you meant -PRint")
);
#endif
return tree_and::create(x, shorthand_print());
}
%}
%%
find
: HELP
{
help(0, usage);
quit(0);
}
| generic_options op generic_options
;
op
: strings_or_dot tree14
{
path = $1;
filter = make_sure_has_side_effects(*$2);
delete $2;
}
;
strings_or_dot
: strings
| /* empty */
{
string_ty *dot;
/*
* Default the path list to "." (the current directory).
*/
$$ = new string_list_ty();
dot = str_from_c(".");
$$->push_back(dot);
str_free(dot);
}
;
strings
: STRING
{
$$ = new string_list_ty();
$$->push_back($1);
str_free($1);
}
| strings STRING
{
$$ = $1;
$$->push_back($2);
str_free($2);
}
;
/*
* The fundamental building blocks of expressions.
*/
tree1
: THIS
{
tree::pointer tp = tree_this::create($1);
$$ = new tree::pointer(tp);
}
| NOW
{
tree::pointer tp = tree_now_new();
$$ = new tree::pointer(tp);
}
| STRING
%prec LPAREN
{
rpt_value::pointer vp = rpt_value_string::create(nstring($1));
str_free($1);
tree::pointer tp = tree_constant::create(vp);
$$ = new tree::pointer(tp);
}
| TRUE_keyword
{
rpt_value::pointer vp = rpt_value_boolean::create(true);
tree::pointer tp = tree_constant::create(vp);
$$ = new tree::pointer(tp);
}
| FALSE_keyword
{
rpt_value::pointer vp = rpt_value_boolean::create(false);
tree::pointer tp = tree_constant::create(vp);
$$ = new tree::pointer(tp);
}
| NUMBER
{
rpt_value::pointer vp = rpt_value_integer::create($1);
tree::pointer tp = tree_constant::create(vp);
$$ = new tree::pointer(tp);
}
| REAL
{
rpt_value::pointer vp = rpt_value_real::create($1);
tree::pointer tp = tree_constant::create(vp);
$$ = new tree::pointer(tp);
}
| LPAREN tree14 RPAREN
{
$$ = $2;
}
;
/*
* The simple tests and actions.
*/
tree1
: PRINT
{
tree::pointer tp = shorthand_print();
$$ = new tree::pointer(tp);
}
| DELETE
{
tree::pointer tp = shorthand_delete();
$$ = new tree::pointer(tp);
}
| EXECUTE exec_list SEMICOLON
{
tree::pointer tp = tree_execute::create(*$2);
delete $2;
$$ = new tree::pointer(tp);
}
| NAME STRING
{
tree::pointer tp = shorthand_name(nstring($2));
str_free($2);
$$ = new tree::pointer(tp);
}
| PATH STRING
{
tree::pointer tp = shorthand_path(nstring($2));
str_free($2);
$$ = new tree::pointer(tp);
}
| TYPE STRING
{
tree::pointer tp = shorthand_type(nstring($2));
str_free($2);
$$ = new tree::pointer(tp);
}
;
/*
* These next few deal with comparing various inode aspects. They are
* all shorthand for various function invocations and a comparison.
*/
tree1
: NEWER STRING
{
tree::pointer tp = shorthand_newer(nstring($2));
str_free($2);
$$ = new tree::pointer(tp);
}
| AMIN comparator NUMBER
{
tree::pointer tp = shorthand_atime($2, $3, 60);
$$ = new tree::pointer(tp);
}
| ATIME comparator NUMBER
{
tree::pointer tp = shorthand_atime($2, $3, 24*60*60);
$$ = new tree::pointer(tp);
}
| CMIN comparator NUMBER
{
tree::pointer tp = shorthand_ctime($2, $3, 60);
$$ = new tree::pointer(tp);
}
| CTIME comparator NUMBER
{
tree::pointer tp = shorthand_ctime($2, $3, 24*60*60);
$$ = new tree::pointer(tp);
}
| MMIN comparator NUMBER
{
tree::pointer tp = shorthand_mtime($2, $3, 60);
$$ = new tree::pointer(tp);
}
| MTIME comparator NUMBER
{
tree::pointer tp = shorthand_mtime($2, $3, 24*60*60);
$$ = new tree::pointer(tp);
}
| SSIZE comparator NUMBER
{
tree::pointer tp = shorthand_size($2, $3);
$$ = new tree::pointer(tp);
}
;
comparator
: /* empty */
{ $$ = &tree_eq::create; }
| EQ
{ $$ = &tree_eq::create; }
| NE
{ $$ = &tree_ne::create; }
| LT
{ $$ = &tree_lt::create; }
| LE
{ $$ = &tree_le::create; }
| GT
{ $$ = &tree_gt::create; }
| GE
{ $$ = &tree_ge::create; }
;
tree1
: STRING LPAREN list_opt RPAREN
{
tree::pointer tp = function_indirection($1, *$3);
str_free($1);
delete $3;
$$ = new tree::pointer(tp);
}
;
list_opt
: /* empty */
{
$$ = new tree_list();
}
| list
{
$$ = $1;
}
;
list
: tree13
{
$$ = new tree_list();
$$->append(*$1);
delete $1;
}
| list COMMA tree13
{
$$ = $1;
$$->append(*$3);
delete $3;
}
;
exec_list
: tree10
{
$$ = $1;
}
| exec_list tree10
%prec ANDAND
{
static tree::pointer t1;
if (!t1)
{
rpt_value::pointer vp = rpt_value_string::create(" ");
t1 = tree_constant::create(vp);
}
tree::pointer t2 = tree_join::create(*$1, t1);
delete $1;
tree::pointer tp = tree_join::create(t2, *$2);
delete $2;
$$ = new tree::pointer(tp);
}
;
tree2
: tree1
{
$$ = $1;
}
| NOT tree2
{
tree::pointer tp = tree_not::create(*$2);
delete $2;
$$ = new tree::pointer(tp);
}
| PLUS tree2
%prec unary
{
tree::pointer tp = tree_pos::create(*$2);
delete $2;
$$ = new tree::pointer(tp);
}
| MINUS tree2
%prec unary
{
tree::pointer tp = tree_neg::create(*$2);
delete $2;
$$ = new tree::pointer(tp);
}
| TILDE tree2
%prec unary
{
tree::pointer tp = tree_bitwise_not::create(*$2);
delete $2;
$$ = new tree::pointer(tp);
}
;
tree3
: tree2
{
$$ = $1;
}
| tree3 MUL tree2
{
tree::pointer tp = tree_mul::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
| tree3 DIV tree2
{
tree::pointer tp = tree_divide::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
| tree3 MOD tree2
{
tree::pointer tp = tree_mod::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
| tree3 TILDE tree2
{
tree::pointer tp = tree_match::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
;
tree4
: tree3
{
$$ = $1;
}
| tree4 PLUS tree3
{
tree::pointer tp = tree_plus::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
| tree4 MINUS tree3
{
tree::pointer tp = tree_subtract::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
| tree4 JOIN tree3
{
tree::pointer tp = tree_join::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
;
tree5
: tree4
{
$$ = $1;
}
| tree5 SHIFT_LEFT tree4
{
tree::pointer tp = tree_shift_left::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
| tree5 SHIFT_RIGHT tree4
{
tree::pointer tp = tree_shift_right::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
;
tree6
: tree5
{
$$ = $1;
}
| tree6 LT tree5
{
tree::pointer tp = tree_lt::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
| tree6 LE tree5
{
tree::pointer tp = tree_le::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
| tree6 GT tree5
{
tree::pointer tp = tree_gt::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
| tree6 GE tree5
{
tree::pointer tp = tree_ge::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
;
tree7
: tree6
{
$$ = $1;
}
| tree7 EQ tree6
{
tree::pointer tp = tree_eq::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
| tree7 NE tree6
{
tree::pointer tp = tree_ne::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
;
tree8
: tree7
{
$$ = $1;
}
| tree8 BIT_AND tree7
{
tree::pointer tp = tree_bitwise_and::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
;
tree9
: tree8
{
$$ = $1;
}
| tree9 BIT_XOR tree8
{
tree::pointer tp = tree_bitwise_xor::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
;
tree10
: tree9
{
$$ = $1;
}
| tree10 BIT_OR tree9
{
tree::pointer tp = tree_bitwise_or::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
;
tree11
: tree10
{
$$ = $1;
}
| tree11 ANDAND tree10
{
tree::pointer tp = tree_and::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
| tree11 tree10
%prec ANDAND
{
tree::pointer tp = tree_and::create(*$1, *$2);
delete $1;
delete $2;
$$ = new tree::pointer(tp);
}
;
tree12
: tree11
{
$$ = $1;
}
| tree12 OROR tree11
{
tree::pointer tp = tree_or::create(*$1, *$3);
delete $1;
delete $3;
$$ = new tree::pointer(tp);
}
;
tree13
: tree12
{
$$ = $1;
}
| tree13 QUESTION tree13 COLON tree12
%prec QUESTION
{
tree::pointer tp = tree_triadic::create(*$1, *$3, *$5);
delete $1;
delete $3;
delete $5;
$$ = new tree::pointer(tp);
}
;
tree14
: tree13
{
$$ = $1;
}
| tree14 COMMA tree13
{
tree::pointer tp2 = make_sure_has_side_effects(*$1);
delete $1;
tree::pointer tp = tree_comma::create(tp2, *$3);
delete $3;
$$ = new tree::pointer(tp);
}
;
generic_options
: /* empty */
| generic_options generic_option
;
generic_option
: LIBRARY STRING
{
gonzo_library_append($2->str_text);
str_free($2);
}
| RESOLVE
{
if (resolve > 0)
{
duplicate_option_by_name(arglex_token_resolve, usage);
}
if (resolve >= 0)
{
mutually_exclusive_options
(
arglex_token_resolve,
arglex_token_resolve_not,
usage
);
}
resolve = 1;
}
| RESOLVE_NOT
{
if (resolve == 0)
{
duplicate_option_by_name(arglex_token_resolve_not, usage);
}
if (resolve >= 0)
{
mutually_exclusive_options
(
arglex_token_resolve,
arglex_token_resolve_not,
usage
);
}
resolve = 0;
}
| TRACE trace_strings
{
#ifndef DEBUG
error_intl(0, i18n("-TRace needs DEBUG"));
#endif
}
| VERSION
{
version();
quit(0);
}
;
trace_strings
: trace_string
| trace_strings trace_string
;
trace_string
: STRING
{
#ifdef DEBUG
trace_enable(arglex_value.alv_string);
yydebug = trace_pretest_;
#endif
}
;
generic_option
: BASELINE
{
if (baseline)
{
duplicate_option_by_name(arglex_token_baseline, usage);
}
baseline = 1;
}
| CHANGE NUMBER
{
if (change_number)
{
duplicate_option_by_name(arglex_token_change, usage);
}
change_number = $2;
if (change_number == 0)
change_number = MAGIC_ZERO;
else if (change_number < 1)
{
sub_context_ty *scp;
scp = sub_context_new();
sub_var_set_long(scp, "Number", change_number);
fatal_intl(scp, i18n("change $number out of range"));
/* NOTREACHED */
sub_context_delete(scp);
}
}
| PROJECT STRING
{
if (project_name)
duplicate_option_by_name(arglex_token_project, usage);
project_name = str_from_c(arglex_value.alv_string);
}
| BRANCH number_or_string
{
if (branch)
duplicate_option(usage);
branch = $2;
}
| TRUNK
{
if (trunk)
duplicate_option(usage);
++trunk;
}
| GRANDPARENT
{
if (grandparent)
duplicate_option(usage);
++grandparent;
}
| DEBUG_keyword
{
++debug;
}
| BASE_REL
{
user_ty::relative_filename_preference_argument(usage);
}
| CUR_REL
{
user_ty::relative_filename_preference_argument(usage);
}
;
number_or_string
: NUMBER
{
$$ = str_format("%ld", $1);
}
| STRING
{
$$ = $1;
}
;
// vim: set ts=8 sw=4 et :