summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/ref/psql-ref.sgml15
-rw-r--r--src/backend/optimizer/util/predtest.c191
-rw-r--r--src/backend/parser/parse_expr.c26
-rw-r--r--src/backend/utils/cache/lsyscache.c97
-rw-r--r--src/bin/psql/command.c37
-rw-r--r--src/bin/psql/command.h2
-rw-r--r--src/bin/psql/describe.c48
-rw-r--r--src/bin/psql/help.c3
-rw-r--r--src/bin/psql/settings.h2
-rw-r--r--src/bin/psql/startup.c6
-rw-r--r--src/bin/psql/tab-complete.c3
-rw-r--r--src/include/port.h1
-rw-r--r--src/include/utils/lsyscache.h12
-rw-r--r--src/port/path.c11
-rwxr-xr-xsrc/tools/msvc/pgbison.bat54
-rw-r--r--src/tools/msvc/pgbison.pl48
-rwxr-xr-xsrc/tools/msvc/pgflex.bat48
-rw-r--r--src/tools/msvc/pgflex.pl76
18 files changed, 371 insertions, 309 deletions
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 74b26b2486..1b4831179a 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1633,6 +1633,21 @@ Tue Oct 26 21:40:57 CEST 1999
<varlistentry>
+ <term><literal>\ir <replaceable class="parameter">filename</replaceable></literal></term>
+ <listitem>
+ <para>
+ The <literal>\ir</> command is similar to <literal>\i</>, but resolves
+ relative pathnames differently. When executing in interactive mode,
+ the two commands behave identically. However, when invoked from a
+ script, <literal>\ir</literal> interprets pathnames relative to the
+ directory in which the script is located, rather than the current
+ working directory.
+ </para>
+ </listitem>
+ </varlistentry>
+
+
+ <varlistentry>
<term><literal>\l</literal> (or <literal>\list</literal>)</term>
<term><literal>\l+</literal> (or <literal>\list+</literal>)</term>
<listitem>
diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c
index 6c3dfb7eb5..beabafb5a8 100644
--- a/src/backend/optimizer/util/predtest.c
+++ b/src/backend/optimizer/util/predtest.c
@@ -1250,6 +1250,7 @@ list_member_strip(List *list, Expr *datum)
* and in addition we use (6) to represent <>. <> is not a btree-indexable
* operator, but we assume here that if an equality operator of a btree
* opfamily has a negator operator, the negator behaves as <> for the opfamily.
+ * (This convention is also known to get_op_btree_interpretation().)
*
* The interpretation of:
*
@@ -1286,7 +1287,7 @@ list_member_strip(List *list, Expr *datum)
#define BTEQ BTEqualStrategyNumber
#define BTGE BTGreaterEqualStrategyNumber
#define BTGT BTGreaterStrategyNumber
-#define BTNE 6
+#define BTNE ROWCOMPARE_NE
static const StrategyNumber BT_implic_table[6][6] = {
/*
@@ -1557,18 +1558,12 @@ get_btree_test_op(Oid pred_op, Oid clause_op, bool refute_it)
OprProofCacheKey key;
OprProofCacheEntry *cache_entry;
bool cfound;
- bool pred_op_negated;
- Oid pred_op_negator,
- clause_op_negator,
- test_op = InvalidOid;
- Oid opfamily_id;
+ Oid test_op = InvalidOid;
bool found = false;
- StrategyNumber pred_strategy,
- clause_strategy,
- test_strategy;
- Oid clause_righttype;
- CatCList *catlist;
- int i;
+ List *pred_op_infos,
+ *clause_op_infos;
+ ListCell *lcp,
+ *lcc;
/*
* Find or make a cache entry for this pair of operators.
@@ -1629,135 +1624,71 @@ get_btree_test_op(Oid pred_op, Oid clause_op, bool refute_it)
* corresponding test operator. This should work for any logically
* consistent opfamilies.
*/
- catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(pred_op));
+ clause_op_infos = get_op_btree_interpretation(clause_op);
+ if (clause_op_infos)
+ pred_op_infos = get_op_btree_interpretation(pred_op);
+ else /* no point in looking */
+ pred_op_infos = NIL;
- /*
- * If we couldn't find any opfamily containing the pred_op, perhaps it is
- * a <> operator. See if it has a negator that is in an opfamily.
- */
- pred_op_negated = false;
- if (catlist->n_members == 0)
+ foreach(lcp, pred_op_infos)
{
- pred_op_negator = get_negator(pred_op);
- if (OidIsValid(pred_op_negator))
- {
- pred_op_negated = true;
- ReleaseSysCacheList(catlist);
- catlist = SearchSysCacheList1(AMOPOPID,
- ObjectIdGetDatum(pred_op_negator));
- }
- }
+ OpBtreeInterpretation *pred_op_info = lfirst(lcp);
+ Oid opfamily_id = pred_op_info->opfamily_id;
- /* Also may need the clause_op's negator */
- clause_op_negator = get_negator(clause_op);
+ foreach(lcc, clause_op_infos)
+ {
+ OpBtreeInterpretation *clause_op_info = lfirst(lcc);
+ StrategyNumber pred_strategy,
+ clause_strategy,
+ test_strategy;
- /* Now search the opfamilies */
- for (i = 0; i < catlist->n_members; i++)
- {
- HeapTuple pred_tuple = &catlist->members[i]->tuple;
- Form_pg_amop pred_form = (Form_pg_amop) GETSTRUCT(pred_tuple);
- HeapTuple clause_tuple;
+ /* Must find them in same opfamily */
+ if (opfamily_id != clause_op_info->opfamily_id)
+ continue;
+ /* Lefttypes should match */
+ Assert(clause_op_info->oplefttype == pred_op_info->oplefttype);
- /* Must be btree */
- if (pred_form->amopmethod != BTREE_AM_OID)
- continue;
+ pred_strategy = pred_op_info->strategy;
+ clause_strategy = clause_op_info->strategy;
- /* Get the predicate operator's btree strategy number */
- opfamily_id = pred_form->amopfamily;
- pred_strategy = (StrategyNumber) pred_form->amopstrategy;
- Assert(pred_strategy >= 1 && pred_strategy <= 5);
+ /*
+ * Look up the "test" strategy number in the implication table
+ */
+ if (refute_it)
+ test_strategy = BT_refute_table[clause_strategy - 1][pred_strategy - 1];
+ else
+ test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1];
- if (pred_op_negated)
- {
- /* Only consider negators that are = */
- if (pred_strategy != BTEqualStrategyNumber)
+ if (test_strategy == 0)
+ {
+ /* Can't determine implication using this interpretation */
continue;
- pred_strategy = BTNE;
- }
+ }
- /*
- * From the same opfamily, find a strategy number for the clause_op,
- * if possible
- */
- clause_tuple = SearchSysCache3(AMOPOPID,
- ObjectIdGetDatum(clause_op),
- CharGetDatum(AMOP_SEARCH),
- ObjectIdGetDatum(opfamily_id));
- if (HeapTupleIsValid(clause_tuple))
- {
- Form_pg_amop clause_form = (Form_pg_amop) GETSTRUCT(clause_tuple);
-
- /* Get the restriction clause operator's strategy/datatype */
- clause_strategy = (StrategyNumber) clause_form->amopstrategy;
- Assert(clause_strategy >= 1 && clause_strategy <= 5);
- Assert(clause_form->amoplefttype == pred_form->amoplefttype);
- clause_righttype = clause_form->amoprighttype;
- ReleaseSysCache(clause_tuple);
- }
- else if (OidIsValid(clause_op_negator))
- {
- clause_tuple = SearchSysCache3(AMOPOPID,
- ObjectIdGetDatum(clause_op_negator),
- CharGetDatum(AMOP_SEARCH),
- ObjectIdGetDatum(opfamily_id));
- if (HeapTupleIsValid(clause_tuple))
+ /*
+ * See if opfamily has an operator for the test strategy and the
+ * datatypes.
+ */
+ if (test_strategy == BTNE)
{
- Form_pg_amop clause_form = (Form_pg_amop) GETSTRUCT(clause_tuple);
-
- /* Get the restriction clause operator's strategy/datatype */
- clause_strategy = (StrategyNumber) clause_form->amopstrategy;
- Assert(clause_strategy >= 1 && clause_strategy <= 5);
- Assert(clause_form->amoplefttype == pred_form->amoplefttype);
- clause_righttype = clause_form->amoprighttype;
- ReleaseSysCache(clause_tuple);
-
- /* Only consider negators that are = */
- if (clause_strategy != BTEqualStrategyNumber)
- continue;
- clause_strategy = BTNE;
+ test_op = get_opfamily_member(opfamily_id,
+ pred_op_info->oprighttype,
+ clause_op_info->oprighttype,
+ BTEqualStrategyNumber);
+ if (OidIsValid(test_op))
+ test_op = get_negator(test_op);
}
else
- continue;
- }
- else
- continue;
-
- /*
- * Look up the "test" strategy number in the implication table
- */
- if (refute_it)
- test_strategy = BT_refute_table[clause_strategy - 1][pred_strategy - 1];
- else
- test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1];
+ {
+ test_op = get_opfamily_member(opfamily_id,
+ pred_op_info->oprighttype,
+ clause_op_info->oprighttype,
+ test_strategy);
+ }
- if (test_strategy == 0)
- {
- /* Can't determine implication using this interpretation */
- continue;
- }
+ if (!OidIsValid(test_op))
+ continue;
- /*
- * See if opfamily has an operator for the test strategy and the
- * datatypes.
- */
- if (test_strategy == BTNE)
- {
- test_op = get_opfamily_member(opfamily_id,
- pred_form->amoprighttype,
- clause_righttype,
- BTEqualStrategyNumber);
- if (OidIsValid(test_op))
- test_op = get_negator(test_op);
- }
- else
- {
- test_op = get_opfamily_member(opfamily_id,
- pred_form->amoprighttype,
- clause_righttype,
- test_strategy);
- }
- if (OidIsValid(test_op))
- {
/*
* Last check: test_op must be immutable.
*
@@ -1773,9 +1704,13 @@ get_btree_test_op(Oid pred_op, Oid clause_op, bool refute_it)
break;
}
}
+
+ if (found)
+ break;
}
- ReleaseSysCacheList(catlist);
+ list_free_deep(pred_op_infos);
+ list_free_deep(clause_op_infos);
if (!found)
{
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 08f0439e7e..65d03adc49 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -2170,8 +2170,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
List *opfamilies;
ListCell *l,
*r;
- List **opfamily_lists;
- List **opstrat_lists;
+ List **opinfo_lists;
Bitmapset *strats;
int nopers;
int i;
@@ -2241,8 +2240,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
* containing the operators, and see which interpretations (strategy
* numbers) exist for each operator.
*/
- opfamily_lists = (List **) palloc(nopers * sizeof(List *));
- opstrat_lists = (List **) palloc(nopers * sizeof(List *));
+ opinfo_lists = (List **) palloc(nopers * sizeof(List *));
strats = NULL;
i = 0;
foreach(l, opexprs)
@@ -2251,17 +2249,18 @@ make_row_comparison_op(ParseState *pstate, List *opname,
Bitmapset *this_strats;
ListCell *j;
- get_op_btree_interpretation(opno,
- &opfamily_lists[i], &opstrat_lists[i]);
+ opinfo_lists[i] = get_op_btree_interpretation(opno);
/*
- * convert strategy number list to a Bitmapset to make the
+ * convert strategy numbers into a Bitmapset to make the
* intersection calculation easy.
*/
this_strats = NULL;
- foreach(j, opstrat_lists[i])
+ foreach(j, opinfo_lists[i])
{
- this_strats = bms_add_member(this_strats, lfirst_int(j));
+ OpBtreeInterpretation *opinfo = lfirst(j);
+
+ this_strats = bms_add_member(this_strats, opinfo->strategy);
}
if (i == 0)
strats = this_strats;
@@ -2309,14 +2308,15 @@ make_row_comparison_op(ParseState *pstate, List *opname,
for (i = 0; i < nopers; i++)
{
Oid opfamily = InvalidOid;
+ ListCell *j;
- forboth(l, opfamily_lists[i], r, opstrat_lists[i])
+ foreach(j, opinfo_lists[i])
{
- int opstrat = lfirst_int(r);
+ OpBtreeInterpretation *opinfo = lfirst(j);
- if (opstrat == rctype)
+ if (opinfo->strategy == rctype)
{
- opfamily = lfirst_oid(l);
+ opfamily = opinfo->opfamily_id;
break;
}
}
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 69ec513634..326f1eee92 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -623,52 +623,30 @@ get_op_hash_functions(Oid opno,
/*
* get_op_btree_interpretation
* Given an operator's OID, find out which btree opfamilies it belongs to,
- * and what strategy number it has within each one. The results are
- * returned as an OID list and a parallel integer list.
+ * and what properties it has within each one. The results are returned
+ * as a palloc'd list of OpBtreeInterpretation structs.
*
* In addition to the normal btree operators, we consider a <> operator to be
* a "member" of an opfamily if its negator is an equality operator of the
* opfamily. ROWCOMPARE_NE is returned as the strategy number for this case.
*/
-void
-get_op_btree_interpretation(Oid opno, List **opfamilies, List **opstrats)
+List *
+get_op_btree_interpretation(Oid opno)
{
+ List *result = NIL;
+ OpBtreeInterpretation *thisresult;
CatCList *catlist;
- bool op_negated;
int i;
- *opfamilies = NIL;
- *opstrats = NIL;
-
/*
* Find all the pg_amop entries containing the operator.
*/
catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
- /*
- * If we can't find any opfamily containing the op, perhaps it is a <>
- * operator. See if it has a negator that is in an opfamily.
- */
- op_negated = false;
- if (catlist->n_members == 0)
- {
- Oid op_negator = get_negator(opno);
-
- if (OidIsValid(op_negator))
- {
- op_negated = true;
- ReleaseSysCacheList(catlist);
- catlist = SearchSysCacheList1(AMOPOPID,
- ObjectIdGetDatum(op_negator));
- }
- }
-
- /* Now search the opfamilies */
for (i = 0; i < catlist->n_members; i++)
{
HeapTuple op_tuple = &catlist->members[i]->tuple;
Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple);
- Oid opfamily_id;
StrategyNumber op_strategy;
/* must be btree */
@@ -676,23 +654,66 @@ get_op_btree_interpretation(Oid opno, List **opfamilies, List **opstrats)
continue;
/* Get the operator's btree strategy number */
- opfamily_id = op_form->amopfamily;
op_strategy = (StrategyNumber) op_form->amopstrategy;
Assert(op_strategy >= 1 && op_strategy <= 5);
- if (op_negated)
+ thisresult = (OpBtreeInterpretation *)
+ palloc(sizeof(OpBtreeInterpretation));
+ thisresult->opfamily_id = op_form->amopfamily;
+ thisresult->strategy = op_strategy;
+ thisresult->oplefttype = op_form->amoplefttype;
+ thisresult->oprighttype = op_form->amoprighttype;
+ result = lappend(result, thisresult);
+ }
+
+ ReleaseSysCacheList(catlist);
+
+ /*
+ * If we didn't find any btree opfamily containing the operator, perhaps
+ * it is a <> operator. See if it has a negator that is in an opfamily.
+ */
+ if (result == NIL)
+ {
+ Oid op_negator = get_negator(opno);
+
+ if (OidIsValid(op_negator))
{
- /* Only consider negators that are = */
- if (op_strategy != BTEqualStrategyNumber)
- continue;
- op_strategy = ROWCOMPARE_NE;
- }
+ catlist = SearchSysCacheList1(AMOPOPID,
+ ObjectIdGetDatum(op_negator));
- *opfamilies = lappend_oid(*opfamilies, opfamily_id);
- *opstrats = lappend_int(*opstrats, op_strategy);
+ for (i = 0; i < catlist->n_members; i++)
+ {
+ HeapTuple op_tuple = &catlist->members[i]->tuple;
+ Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple);
+ StrategyNumber op_strategy;
+
+ /* must be btree */
+ if (op_form->amopmethod != BTREE_AM_OID)
+ continue;
+
+ /* Get the operator's btree strategy number */
+ op_strategy = (StrategyNumber) op_form->amopstrategy;
+ Assert(op_strategy >= 1 && op_strategy <= 5);
+
+ /* Only consider negators that are = */
+ if (op_strategy != BTEqualStrategyNumber)
+ continue;
+
+ /* OK, report it with "strategy" ROWCOMPARE_NE */
+ thisresult = (OpBtreeInterpretation *)
+ palloc(sizeof(OpBtreeInterpretation));
+ thisresult->opfamily_id = op_form->amopfamily;
+ thisresult->strategy = ROWCOMPARE_NE;
+ thisresult->oplefttype = op_form->amoplefttype;
+ thisresult->oprighttype = op_form->amoprighttype;
+ result = lappend(result, thisresult);
+ }
+
+ ReleaseSysCacheList(catlist);
+ }
}
- ReleaseSysCacheList(catlist);
+ return result;
}
/*
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 378330b96a..16ff9e91e5 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -784,8 +784,9 @@ exec_command(const char *cmd,
}
- /* \i is include file */
- else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0)
+ /* \i and \ir include files */
+ else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0
+ || strcmp(cmd, "ir") == 0 || strcmp(cmd, "include_relative") == 0)
{
char *fname = psql_scan_slash_option(scan_state,
OT_NORMAL, NULL, true);
@@ -797,8 +798,12 @@ exec_command(const char *cmd,
}
else
{
+ bool include_relative;
+
+ include_relative = (strcmp(cmd, "ir") == 0
+ || strcmp(cmd, "include_relative") == 0);
expand_tilde(&fname);
- success = (process_file(fname, false) == EXIT_SUCCESS);
+ success = (process_file(fname, false, include_relative) == EXIT_SUCCESS);
free(fname);
}
}
@@ -1969,15 +1974,19 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf,
* process_file
*
* Read commands from filename and then them to the main processing loop
- * Handler for \i, but can be used for other things as well. Returns
+ * Handler for \i and \ir, but can be used for other things as well. Returns
* MainLoop() error code.
+ *
+ * If use_relative_path is true and filename is not an absolute path, then open
+ * the file from where the currently processed file (if any) is located.
*/
int
-process_file(char *filename, bool single_txn)
+process_file(char *filename, bool single_txn, bool use_relative_path)
{
FILE *fd;
int result;
char *oldfilename;
+ char relpath[MAXPGPATH];
PGresult *res;
if (!filename)
@@ -1986,6 +1995,24 @@ process_file(char *filename, bool single_txn)
if (strcmp(filename, "-") != 0)
{
canonicalize_path(filename);
+
+ /*
+ * If we were asked to resolve the pathname relative to the location
+ * of the currently executing script, and there is one, and this is
+ * a relative pathname, then prepend all but the last pathname
+ * component of the current script to this pathname.
+ */
+ if (use_relative_path && pset.inputfile && !is_absolute_path(filename)
+ && !has_drive_prefix(filename))
+ {
+ snprintf(relpath, MAXPGPATH, "%s", pset.inputfile);
+ get_parent_directory(relpath);
+ join_path_components(relpath, relpath, filename);
+ canonicalize_path(relpath);
+
+ filename = relpath;
+ }
+
fd = fopen(filename, PG_BINARY_R);
}
else
diff --git a/src/bin/psql/command.h b/src/bin/psql/command.h
index 852d645cfd..9d0c31c103 100644
--- a/src/bin/psql/command.h
+++ b/src/bin/psql/command.h
@@ -27,7 +27,7 @@ typedef enum _backslashResult
extern backslashResult HandleSlashCmds(PsqlScanState scan_state,
PQExpBuffer query_buf);
-extern int process_file(char *filename, bool single_txn);
+extern int process_file(char *filename, bool single_txn, bool use_relative_path);
extern bool do_pset(const char *param,
const char *value,
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 381150c75a..101d7a5a56 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1159,7 +1159,7 @@ describeOneTableDetails(const char *schemaname,
"c.relpersistence\n"
"FROM pg_catalog.pg_class c\n "
"LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
- "WHERE c.oid = '%s'\n",
+ "WHERE c.oid = '%s';",
(verbose ?
"pg_catalog.array_to_string(c.reloptions || "
"array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
@@ -1175,7 +1175,7 @@ describeOneTableDetails(const char *schemaname,
"CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END\n"
"FROM pg_catalog.pg_class c\n "
"LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
- "WHERE c.oid = '%s'\n",
+ "WHERE c.oid = '%s';",
(verbose ?
"pg_catalog.array_to_string(c.reloptions || "
"array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
@@ -1190,7 +1190,7 @@ describeOneTableDetails(const char *schemaname,
"%s, c.reltablespace\n"
"FROM pg_catalog.pg_class c\n "
"LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
- "WHERE c.oid = '%s'\n",
+ "WHERE c.oid = '%s';",
(verbose ?
"pg_catalog.array_to_string(c.reloptions || "
"array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
@@ -1203,7 +1203,7 @@ describeOneTableDetails(const char *schemaname,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
"reltriggers <> 0, relhasoids, "
"%s, reltablespace\n"
- "FROM pg_catalog.pg_class WHERE oid = '%s'",
+ "FROM pg_catalog.pg_class WHERE oid = '%s';",
(verbose ?
"pg_catalog.array_to_string(reloptions, E', ')" : "''"),
oid);
@@ -1214,7 +1214,7 @@ describeOneTableDetails(const char *schemaname,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
"reltriggers <> 0, relhasoids, "
"'', reltablespace\n"
- "FROM pg_catalog.pg_class WHERE oid = '%s'",
+ "FROM pg_catalog.pg_class WHERE oid = '%s';",
oid);
}
else
@@ -1223,7 +1223,7 @@ describeOneTableDetails(const char *schemaname,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
"reltriggers <> 0, relhasoids, "
"'', ''\n"
- "FROM pg_catalog.pg_class WHERE oid = '%s'",
+ "FROM pg_catalog.pg_class WHERE oid = '%s';",
oid);
}
@@ -1265,7 +1265,7 @@ describeOneTableDetails(const char *schemaname,
{
printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
/* must be separate because fmtId isn't reentrant */
- appendPQExpBuffer(&buf, ".%s", fmtId(relationname));
+ appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
res = PSQLexec(buf.data, false);
if (!res)
@@ -1310,7 +1310,7 @@ describeOneTableDetails(const char *schemaname,
appendPQExpBuffer(&buf, ",\n a.attstorage, pg_catalog.col_description(a.attrelid, a.attnum)");
appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_attribute a");
appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
- appendPQExpBuffer(&buf, "\nORDER BY a.attnum");
+ appendPQExpBuffer(&buf, "\nORDER BY a.attnum;");
res = PSQLexec(buf.data, false);
if (!res)
@@ -1408,7 +1408,7 @@ describeOneTableDetails(const char *schemaname,
PGresult *result;
printfPQExpBuffer(&buf,
- "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true)",
+ "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true);",
oid);
result = PSQLexec(buf.data, false);
if (!result)
@@ -1527,7 +1527,7 @@ describeOneTableDetails(const char *schemaname,
"pg_catalog.pg_get_expr(i.indpred, i.indrelid, true)\n"
"FROM pg_catalog.pg_index i, pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_am a\n"
"WHERE i.indexrelid = c.oid AND c.oid = '%s' AND c.relam = a.oid\n"
- "AND i.indrelid = c2.oid",
+ "AND i.indrelid = c2.oid;",
oid);
result = PSQLexec(buf.data, false);
@@ -1598,7 +1598,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true))\n"
"FROM pg_catalog.pg_rewrite r\n"
- "WHERE r.ev_class = '%s' AND r.rulename != '_RETURN' ORDER BY 1",
+ "WHERE r.ev_class = '%s' AND r.rulename != '_RETURN' ORDER BY 1;",
oid);
result = PSQLexec(buf.data, false);
if (!result)
@@ -1655,7 +1655,7 @@ describeOneTableDetails(const char *schemaname,
" LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ('p','u','x'))\n");
appendPQExpBuffer(&buf,
"WHERE c.oid = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n"
- "ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname",
+ "ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname;",
oid);
result = PSQLexec(buf.data, false);
if (!result)
@@ -1735,7 +1735,7 @@ describeOneTableDetails(const char *schemaname,
"SELECT r.conname, "
"pg_catalog.pg_get_constraintdef(r.oid, true)\n"
"FROM pg_catalog.pg_constraint r\n"
- "WHERE r.conrelid = '%s' AND r.contype = 'c'\nORDER BY 1",
+ "WHERE r.conrelid = '%s' AND r.contype = 'c'\nORDER BY 1;",
oid);
result = PSQLexec(buf.data, false);
if (!result)
@@ -1766,7 +1766,7 @@ describeOneTableDetails(const char *schemaname,
"SELECT conname,\n"
" pg_catalog.pg_get_constraintdef(r.oid, true) as condef\n"
"FROM pg_catalog.pg_constraint r\n"
- "WHERE r.conrelid = '%s' AND r.contype = 'f' ORDER BY 1",
+ "WHERE r.conrelid = '%s' AND r.contype = 'f' ORDER BY 1;",
oid);
result = PSQLexec(buf.data, false);
if (!result)
@@ -1797,7 +1797,7 @@ describeOneTableDetails(const char *schemaname,
"SELECT conname, conrelid::pg_catalog.regclass,\n"
" pg_catalog.pg_get_constraintdef(c.oid, true) as condef\n"
"FROM pg_catalog.pg_constraint c\n"
- "WHERE c.confrelid = '%s' AND c.contype = 'f' ORDER BY 1",
+ "WHERE c.confrelid = '%s' AND c.contype = 'f' ORDER BY 1;",
oid);
result = PSQLexec(buf.data, false);
if (!result)
@@ -1830,7 +1830,7 @@ describeOneTableDetails(const char *schemaname,
"SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
"ev_enabled\n"
"FROM pg_catalog.pg_rewrite r\n"
- "WHERE r.ev_class = '%s' ORDER BY 1",
+ "WHERE r.ev_class = '%s' ORDER BY 1;",
oid);
}
else
@@ -1839,7 +1839,7 @@ describeOneTableDetails(const char *schemaname,
"SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
"'O'::char AS ev_enabled\n"
"FROM pg_catalog.pg_rewrite r\n"
- "WHERE r.ev_class = '%s' ORDER BY 1",
+ "WHERE r.ev_class = '%s' ORDER BY 1;",
oid);
}
result = PSQLexec(buf.data, false);
@@ -1945,7 +1945,7 @@ describeOneTableDetails(const char *schemaname,
" (SELECT 1 FROM pg_catalog.pg_depend d "
" JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
" WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))");
- appendPQExpBuffer(&buf, "\nORDER BY 1");
+ appendPQExpBuffer(&buf, "\nORDER BY 1;");
result = PSQLexec(buf.data, false);
if (!result)
@@ -2053,7 +2053,7 @@ describeOneTableDetails(const char *schemaname,
"SELECT s.srvname\n"
"FROM pg_catalog.pg_foreign_table f,\n"
" pg_catalog.pg_foreign_server s\n"
- "WHERE f.ftrelid = %s AND s.oid = f.ftserver",
+ "WHERE f.ftrelid = %s AND s.oid = f.ftserver;",
oid);
result = PSQLexec(buf.data, false);
if (!result)
@@ -2071,7 +2071,7 @@ describeOneTableDetails(const char *schemaname,
}
/* print inherited tables */
- printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhparent AND i.inhrelid = '%s' ORDER BY inhseqno", oid);
+ printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhparent AND i.inhrelid = '%s' ORDER BY inhseqno;", oid);
result = PSQLexec(buf.data, false);
if (!result)
@@ -2233,7 +2233,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind,
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT spcname FROM pg_catalog.pg_tablespace\n"
- "WHERE oid = '%u'", tablespace);
+ "WHERE oid = '%u';", tablespace);
result = PSQLexec(buf.data, false);
if (!result)
return;
@@ -2442,7 +2442,7 @@ listDbRoleSettings(const char *pattern, const char *pattern2)
NULL, "pg_roles.rolname", NULL, NULL);
processSQLNamePattern(pset.db, &buf, pattern2, havewhere, false,
NULL, "pg_database.datname", NULL, NULL);
- appendPQExpBufferStr(&buf, "ORDER BY role, database");
+ appendPQExpBufferStr(&buf, "ORDER BY role, database;");
}
else
{
@@ -3188,7 +3188,7 @@ describeOneTSParser(const char *oid, const char *nspname, const char *prsname)
" p.prslextype::pg_catalog.regproc, \n"
" pg_catalog.obj_description(p.prslextype, 'pg_proc') \n"
" FROM pg_catalog.pg_ts_parser p \n"
- " WHERE p.oid = '%s' \n",
+ " WHERE p.oid = '%s';",
gettext_noop("Start parse"),
gettext_noop("Method"),
gettext_noop("Function"),
@@ -3549,7 +3549,7 @@ describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname,
"FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m \n"
"WHERE c.oid = '%s' AND m.mapcfg = c.oid \n"
"GROUP BY m.mapcfg, m.maptokentype, c.cfgparser \n"
- "ORDER BY 1",
+ "ORDER BY 1;",
gettext_noop("Token"),
gettext_noop("Dictionaries"),
oid);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index ac5edca65d..e56ab61ac6 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -158,7 +158,7 @@ slashUsage(unsigned short int pager)
{
FILE *output;
- output = PageOutput(92, pager);
+ output = PageOutput(93, pager);
/* if you add/remove a line here, change the row count above */
@@ -184,6 +184,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\copy ... perform SQL COPY with data stream to the client host\n"));
fprintf(output, _(" \\echo [STRING] write string to standard output\n"));
fprintf(output, _(" \\i FILE execute commands from file\n"));
+ fprintf(output, _(" \\ir FILE as \\i, but relative to location of current script\n"));
fprintf(output, _(" \\o [FILE] send all query results to file or |pipe\n"));
fprintf(output, _(" \\qecho [STRING] write string to query output stream (see \\o)\n"));
fprintf(output, "\n");
diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h
index 7228f9d0ee..3aebf53299 100644
--- a/src/bin/psql/settings.h
+++ b/src/bin/psql/settings.h
@@ -81,7 +81,7 @@ typedef struct _psqlSettings
bool cur_cmd_interactive;
int sversion; /* backend server version */
const char *progname; /* in case you renamed psql */
- char *inputfile; /* for error reporting */
+ char *inputfile; /* file being currently processed, if any */
char *dirname; /* current directory for \s display */
uint64 lineno; /* also for error reporting */
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 7b8078c21e..3c17eece7b 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -256,7 +256,7 @@ main(int argc, char *argv[])
if (!options.no_psqlrc)
process_psqlrc(argv[0]);
- successResult = process_file(options.action_string, options.single_txn);
+ successResult = process_file(options.action_string, options.single_txn, false);
}
/*
@@ -604,9 +604,9 @@ process_psqlrc_file(char *filename)
sprintf(psqlrc, "%s-%s", filename, PG_VERSION);
if (access(psqlrc, R_OK) == 0)
- (void) process_file(psqlrc, false);
+ (void) process_file(psqlrc, false, false);
else if (access(filename, R_OK) == 0)
- (void) process_file(filename, false);
+ (void) process_file(filename, false, false);
free(psqlrc);
}
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 32f418306c..4f7df367e5 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -735,7 +735,7 @@ psql_completion(char *text, int start, int end)
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
"\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du",
"\\e", "\\echo", "\\ef", "\\encoding",
- "\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l",
+ "\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\ir", "\\l",
"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
"\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
"\\set", "\\sf", "\\t", "\\T",
@@ -2874,6 +2874,7 @@ psql_completion(char *text, int start, int end)
strcmp(prev_wd, "\\e") == 0 || strcmp(prev_wd, "\\edit") == 0 ||
strcmp(prev_wd, "\\g") == 0 ||
strcmp(prev_wd, "\\i") == 0 || strcmp(prev_wd, "\\include") == 0 ||
+ strcmp(prev_wd, "\\ir") == 0 || strcmp(prev_wd, "\\include_relative") == 0 ||
strcmp(prev_wd, "\\o") == 0 || strcmp(prev_wd, "\\out") == 0 ||
strcmp(prev_wd, "\\s") == 0 ||
strcmp(prev_wd, "\\w") == 0 || strcmp(prev_wd, "\\write") == 0
diff --git a/src/include/port.h b/src/include/port.h
index 4c7ed64317..2cab65fbde 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -34,6 +34,7 @@ extern bool pg_set_block(pgsocket sock);
/* Portable path handling for Unix/Win32 (in path.c) */
+extern bool has_drive_prefix(const char *filename);
extern char *first_dir_separator(const char *filename);
extern char *last_dir_separator(const char *filename);
extern char *first_path_var_separator(const char *pathlist);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 0a419dcf65..f4490adc31 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -17,6 +17,15 @@
#include "access/htup.h"
#include "nodes/pg_list.h"
+/* Result list element for get_op_btree_interpretation */
+typedef struct OpBtreeInterpretation
+{
+ Oid opfamily_id; /* btree opfamily containing operator */
+ int strategy; /* its strategy number */
+ Oid oplefttype; /* declared left input datatype */
+ Oid oprighttype; /* declared right input datatype */
+} OpBtreeInterpretation;
+
/* I/O function selector for get_type_io_data */
typedef enum IOFuncSelector
{
@@ -50,8 +59,7 @@ extern bool get_compatible_hash_operators(Oid opno,
Oid *lhs_opno, Oid *rhs_opno);
extern bool get_op_hash_functions(Oid opno,
RegProcedure *lhs_procno, RegProcedure *rhs_procno);
-extern void get_op_btree_interpretation(Oid opno,
- List **opfamilies, List **opstrats);
+extern List *get_op_btree_interpretation(Oid opno);
extern bool equality_ops_are_compatible(Oid opno1, Oid opno2);
extern Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype,
int16 procnum);
diff --git a/src/port/path.c b/src/port/path.c
index 6991bc7247..13ca4f3f1c 100644
--- a/src/port/path.c
+++ b/src/port/path.c
@@ -75,6 +75,17 @@ skip_drive(const char *path)
#endif
/*
+ * has_drive_prefix
+ *
+ * Return true if the given pathname has a drive prefix.
+ */
+bool
+has_drive_prefix(const char *path)
+{
+ return skip_drive(path) != path;
+}
+
+/*
* first_dir_separator
*
* Find the location of the first directory separator, return
diff --git a/src/tools/msvc/pgbison.bat b/src/tools/msvc/pgbison.bat
index c5d355e5ab..27b5353a55 100755
--- a/src/tools/msvc/pgbison.bat
+++ b/src/tools/msvc/pgbison.bat
@@ -1,51 +1,7 @@
@echo off
-REM src/tools/msvc/pgbison.bat
-
-IF NOT EXIST src\tools\msvc\buildenv.pl goto nobuildenv
-perl -e "require 'src/tools/msvc/buildenv.pl'; while(($k,$v) = each %%ENV) { print qq[\@SET $k=$v\n]; }" > bldenv.bat
-CALL bldenv.bat
-del bldenv.bat
-:nobuildenv
-
-SET BV=
-for /F "tokens=4 usebackq" %%f in (`bison -V`) do if "!BV!"=="" SET BV=%%f
-if "%BV%"=="" goto novarexp
-if %BV% EQU 1.875 goto bisonok
-if %BV% GEQ 2.2 goto bisonok
-goto nobison
-:bisonok
-
-if "%1" == "src\backend\parser\gram.y" call :generate %1 src\backend\parser\gram.c src\backend\parser\gram.h
-if "%1" == "src\backend\bootstrap\bootparse.y" call :generate %1 src\backend\bootstrap\bootparse.c
-if "%1" == "src\backend\replication\repl_gram.y" call :generate %1 src\backend\replication\repl_gram.c
-if "%1" == "src\pl\plpgsql\src\gram.y" call :generate %1 src\pl\plpgsql\src\pl_gram.c src\pl\plpgsql\src\pl_gram.h
-if "%1" == "src\test\isolation\specparse.y" call :generate %1 src\test\isolation\specparse.c
-if "%1" == "src\interfaces\ecpg\preproc\preproc.y" call :generate %1 src\interfaces\ecpg\preproc\preproc.c src\interfaces\ecpg\preproc\preproc.h
-if "%1" == "contrib\cube\cubeparse.y" call :generate %1 contrib\cube\cubeparse.c
-if "%1" == "contrib\seg\segparse.y" call :generate %1 contrib\seg\segparse.c
-
-echo Unknown bison input: %1
-exit 1
-:generate
-SET fn=%1
-SET cf=%2
-bison.exe -d %fn% -o %cf%
-if errorlevel 1 exit 1
-SET hf=%cf:~0,-2%.h
-if not "%hf%"=="%3" (
- copy /y %hf% %3
- if errorlevel 1 exit 1
- del %hf%
-)
-exit 0
-
-
-:novarexp
-echo pgbison must be called with cmd /V:ON /C pgbison to work!
-exit 1
-
-:nobison
-echo WARNING! Bison install not found, or unsupported Bison version.
-echo Attempting to build without.
-exit 0
+REM src/tools/msvc/pgbison.bat
+REM all the logic for this now belongs in builddoc.pl. This file really
+REM only exists so you don't have to type "perl src/tools/msvc/pgbison.pl"
+REM Resist any temptation to add any logic here.
+@perl src/tools/msvc/pgbison.pl %*
diff --git a/src/tools/msvc/pgbison.pl b/src/tools/msvc/pgbison.pl
new file mode 100644
index 0000000000..c48863aff6
--- /dev/null
+++ b/src/tools/msvc/pgbison.pl
@@ -0,0 +1,48 @@
+# -*-perl-*- hey - emacs - this is a perl file
+
+# src/tools/msvc/pgbison.pl
+
+use strict;
+use File::Basename;
+
+# assume we are in the postgres source root
+
+require 'src/tools/msvc/buildenv.pl' if -e 'src/tools/msvc/buildenv.pl';
+
+my ($bisonver) = `bison -V`; # grab first line
+$bisonver=(split(/\s+/,$bisonver))[3]; # grab version number
+
+unless ($bisonver eq '1.875' || $bisonver ge '2.2')
+{
+ print "WARNING! Bison install not found, or unsupported Bison version.\n";
+ print "echo Attempting to build without.\n";
+ exit 0;
+}
+
+my $input = shift;
+if ($input !~ /\.y$/)
+{
+ print "Input must be a .y file\n";
+ exit 1;
+}
+elsif (!-e $input)
+{
+ print "Input file $input not found\n";
+ exit 1;
+}
+
+(my $output = $input) =~ s/\.y$/.c/;
+
+# plpgsql just has to be different
+$output =~ s/gram\.c$/pl_gram.c/ if $input =~ /src.pl.plpgsql.src.gram\.y$/;
+
+my $makefile = dirname($input) . "/Makefile";
+my ($mf, $make);
+open($mf,$makefile);
+local $/ = undef;
+$make=<$mf>;
+close($mf);
+my $headerflag = ($make =~ /\$\(BISON\)\s+-d/ ? '-d' : '');
+
+system("bison $headerflag $input -o $output");
+exit $? >> 8;
diff --git a/src/tools/msvc/pgflex.bat b/src/tools/msvc/pgflex.bat
index e26724cc5f..144422d313 100755
--- a/src/tools/msvc/pgflex.bat
+++ b/src/tools/msvc/pgflex.bat
@@ -1,45 +1,7 @@
@echo off
-REM src/tools/msvc/pgflex.bat
-
-REM silence flex bleatings about file path style
-SET CYGWIN=nodosfilewarning
-
-IF NOT EXIST src\tools\msvc\buildenv.pl goto nobuildenv
-perl -e "require 'src/tools/msvc/buildenv.pl'; while(($k,$v) = each %%ENV) { print qq[\@SET $k=$v\n]; }" > bldenv.bat
-CALL bldenv.bat
-del bldenv.bat
-:nobuildenv
-
-flex -V > NUL
-if errorlevel 1 goto noflex
-if "%1" == "src\backend\parser\scan.l" call :generate %1 src\backend\parser\scan.c -CF
-if "%1" == "src\backend\bootstrap\bootscanner.l" call :generate %1 src\backend\bootstrap\bootscanner.c
-if "%1" == "src\backend\utils\misc\guc-file.l" call :generate %1 src\backend\utils\misc\guc-file.c
-if "%1" == "src\backend\replication\repl_scanner.l" call :generate %1 src\backend\replication\repl_scanner.c
-if "%1" == "src\test\isolation\specscanner.l" call :generate %1 src\test\isolation\specscanner.c
-if "%1" == "src\interfaces\ecpg\preproc\pgc.l" call :generate %1 src\interfaces\ecpg\preproc\pgc.c
-if "%1" == "src\bin\psql\psqlscan.l" call :generate %1 src\bin\psql\psqlscan.c
-if "%1" == "contrib\cube\cubescan.l" call :generate %1 contrib\cube\cubescan.c
-if "%1" == "contrib\seg\segscan.l" call :generate %1 contrib\seg\segscan.c
-
-echo Unknown flex input: %1
-exit 1
-
-REM For non-reentrant scanners we need to fix up the yywrap macro definition
-REM to keep the MS compiler happy.
-REM For reentrant scanners (like the core scanner) we do not
-REM need to (and must not) change the yywrap definition.
-:generate
-flex %3 -o%2 %1
-if errorlevel 1 exit %errorlevel%
-perl -n -e "exit 1 if /^\%%option\s+reentrant/;" %1
-if errorlevel 1 exit 0
-perl -pi.bak -e "s/yywrap\(n\)/yywrap()/;" %2
-if errorlevel 1 exit %errorlevel%
-del %2.bak
-exit 0
-
-:noflex
-echo WARNING! flex install not found, attempting to build without
-exit 0
+REM src/tools/msvc/pgflex.bat
+REM all the logic for this now belongs in builddoc.pl. This file really
+REM only exists so you don't have to type "perl src/tools/msvc/pgflex.pl"
+REM Resist any temptation to add any logic here.
+@perl src/tools/msvc/pgflex.pl %*
diff --git a/src/tools/msvc/pgflex.pl b/src/tools/msvc/pgflex.pl
new file mode 100644
index 0000000000..332d422f64
--- /dev/null
+++ b/src/tools/msvc/pgflex.pl
@@ -0,0 +1,76 @@
+# -*-perl-*- hey - emacs - this is a perl file
+
+# src/tools/msvc/pgflex.pl
+
+# silence flex bleatings about file path style
+$ENV{CYGWIN} = 'nodosfilewarning';
+
+use strict;
+use File::Basename;
+
+# assume we are in the postgres source root
+
+require 'src/tools/msvc/buildenv.pl' if -e 'src/tools/msvc/buildenv.pl';
+
+system('flex -V > NUL');
+if ($? != 0)
+{
+ print "WARNING! flex install not found, attempting to build without\n";
+ exit 0;
+}
+
+my $input = shift;
+if ($input !~ /\.l$/)
+{
+ print "Input must be a .l file\n";
+ exit 1;
+}
+elsif (!-e $input)
+{
+ print "Input file $input not found\n";
+ exit 1;
+}
+
+(my $output = $input) =~ s/\.l$/.c/;
+
+# get flex flags from make file
+my $makefile = dirname($input) . "/Makefile";
+my ($mf, $make);
+open($mf,$makefile);
+local $/ = undef;
+$make=<$mf>;
+close($mf);
+my $flexflags = ($make =~ /^\s*FLEXFLAGS\s*=\s*(\S.*)/m ? $1 : '');
+
+system("flex $flexflags -o$output $input");
+if ($? == 0)
+{
+
+ # For non-reentrant scanners we need to fix up the yywrap macro definition
+ # to keep the MS compiler happy.
+ # For reentrant scanners (like the core scanner) we do not
+ # need to (and must not) change the yywrap definition.
+ my $lfile;
+ open($lfile,$input) || die "opening $input for reading: $!";
+ my $lcode = <$lfile>;
+ close($lfile);
+ if ($lcode !~ /\%option\sreentrant/)
+ {
+ my $cfile;
+ open($cfile,$output) || die "opening $output for reading: $!";
+ my $ccode = <$cfile>;
+ close($cfile);
+ $ccode =~ s/yywrap\(n\)/yywrap()/;
+ open($cfile,">$output") || die "opening $output for reading: $!";
+ print $cfile $ccode;
+ close($cfile);
+ }
+
+ exit 0;
+
+}
+else
+{
+ exit $? >> 8;
+}
+