Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit25f9372

Browse files
committed
Fix assorted bugs in ecpg's macro mechanism.
The code associated with EXEC SQL DEFINE was unreadable and full ofbugs, notably:* It'd attempt to free a non-malloced string if the ecpg programtries to redefine a macro that was defined on the command line.* Possible memory stomp if user writes "-D=foo".* Undef'ing or redefining a macro defined on the command line wouldchange the state visible to the next file, when multiple files arespecified on the command line. (While possibly that could have beenan intentional choice, the code clearly intends to revert to theoriginal macro state; it's just failing to consider this interaction.)* Missing "break" in defining a new macro meant that redefinitionof an existing name would cause an extra entry to be added to thedefinition list. While not immediately harmful, a subsequent undefwould result in the prior entry becoming visible again.* The interactions with input buffering are subtle and were entirelyundocumented.It's not that surprising that we hadn't noticed these bugs,because there was no test coverage at all of either the -Dcommand line switch or multiple input files. This patch addssuch coverage (in a rather hacky way I guess).In addition to the code bugs, the user documentation was confusedabout whether the -D switch defines a C macro or an ecpg one, andit failed to mention that you can write "-Dsymbol=value".These problems are old, so back-patch to all supported branches.Discussion:https://postgr.es/m/998011.1713217712@sss.pgh.pa.us
1 parent5aacfa6 commit25f9372

File tree

11 files changed

+288
-80
lines changed

11 files changed

+288
-80
lines changed

‎doc/src/sgml/ecpg.sgml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5753,6 +5753,14 @@ EXEC SQL UPDATE Tbl SET col = MYNUMBER;
57535753
embedded SQL query because in this case the embedded SQL precompiler is not
57545754
able to see this declaration.
57555755
</para>
5756+
5757+
<para>
5758+
If multiple input files are named on the <command>ecpg</command>
5759+
preprocessor's command line, the effects of <literal>EXEC SQL
5760+
DEFINE</literal> and <literal>EXEC SQL UNDEF</literal> do not carry
5761+
across files: each file starts with only the symbols defined
5762+
by <option>-D</option> switches on the command line.
5763+
</para>
57565764
</sect2>
57575765

57585766
<sect2 id="ecpg-ifdef">

‎doc/src/sgml/ref/ecpg-ref.sgml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,12 @@ PostgreSQL documentation
9393
</varlistentry>
9494

9595
<varlistentry>
96-
<term><option>-D <replaceable>symbol</replaceable></option></term>
96+
<term><option>-D <replaceable>symbol</replaceable>[=<replaceable>value</replaceable>]</option></term>
9797
<listitem>
9898
<para>
99-
Define a C preprocessor symbol.
99+
Define a preprocessor symbol, equivalently to the <command>EXEC SQL
100+
DEFINE</command> directive. If no <replaceable>value</replaceable> is
101+
specified, the symbol is defined with the value <literal>1</literal>.
100102
</para>
101103
</listitem>
102104
</varlistentry>

‎src/interfaces/ecpg/preproc/ecpg.c

Lines changed: 46 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -82,35 +82,46 @@ add_include_path(char *path)
8282
}
8383
}
8484

85+
/*
86+
* Process a command line -D switch
87+
*/
8588
staticvoid
8689
add_preprocessor_define(char*define)
8790
{
88-
struct_defines*pd=defines;
89-
char*ptr,
90-
*define_copy=mm_strdup(define);
91+
/* copy the argument to avoid relying on argv storage */
92+
char*define_copy=mm_strdup(define);
93+
char*ptr;
94+
struct_defines*newdef;
9195

92-
defines=mm_alloc(sizeof(struct_defines));
96+
newdef=mm_alloc(sizeof(struct_defines));
9397

9498
/* look for = sign */
9599
ptr=strchr(define_copy,'=');
96100
if (ptr!=NULL)
97101
{
102+
/* symbol has a value */
98103
char*tmp;
99104

100-
/*symbol has a value */
101-
for (tmp=ptr-1;*tmp==' ';tmp--);
105+
/*strip any spaces between name and '=' */
106+
for (tmp=ptr-1;tmp >=define_copy&&*tmp==' ';tmp--);
102107
tmp[1]='\0';
103-
defines->olddef=define_copy;
104-
defines->newdef=ptr+1;
108+
109+
/*
110+
* Note we don't bother to separately malloc cmdvalue; it will never
111+
* be freed so that's not necessary.
112+
*/
113+
newdef->cmdvalue=ptr+1;
105114
}
106115
else
107116
{
108-
defines->olddef=define_copy;
109-
defines->newdef=mm_strdup("1");
117+
/* define it as "1"; again no need to malloc it */
118+
newdef->cmdvalue="1";
110119
}
111-
defines->pertinent= true;
112-
defines->used=NULL;
113-
defines->next=pd;
120+
newdef->name=define_copy;
121+
newdef->value=mm_strdup(newdef->cmdvalue);
122+
newdef->used=NULL;
123+
newdef->next=defines;
124+
defines=newdef;
114125
}
115126

116127
#defineECPG_GETOPT_LONG_REGRESSION1
@@ -347,6 +358,8 @@ main(int argc, char *const argv[])
347358
{
348359
structcursor*ptr;
349360
struct_defines*defptr;
361+
struct_defines*prevdefptr;
362+
struct_defines*nextdefptr;
350363
structtypedefs*typeptr;
351364
structdeclared_list*list;
352365

@@ -384,28 +397,28 @@ main(int argc, char *const argv[])
384397
free(this);
385398
}
386399

387-
/* remove non-pertinent old defines as well */
388-
while (defines&& !defines->pertinent)
400+
/* restore defines to their command-line state */
401+
prevdefptr=NULL;
402+
for (defptr=defines;defptr!=NULL;defptr=nextdefptr)
389403
{
390-
defptr=defines;
391-
defines=defines->next;
392-
393-
free(defptr->newdef);
394-
free(defptr->olddef);
395-
free(defptr);
396-
}
397-
398-
for (defptr=defines;defptr!=NULL;defptr=defptr->next)
399-
{
400-
struct_defines*this=defptr->next;
401-
402-
if (this&& !this->pertinent)
404+
nextdefptr=defptr->next;
405+
if (defptr->cmdvalue!=NULL)
403406
{
404-
defptr->next=this->next;
405-
406-
free(this->newdef);
407-
free(this->olddef);
408-
free(this);
407+
/* keep it, resetting the value */
408+
free(defptr->value);
409+
defptr->value=mm_strdup(defptr->cmdvalue);
410+
prevdefptr=defptr;
411+
}
412+
else
413+
{
414+
/* remove it */
415+
if (prevdefptr!=NULL)
416+
prevdefptr->next=nextdefptr;
417+
else
418+
defines=nextdefptr;
419+
free(defptr->name);
420+
free(defptr->value);
421+
free(defptr);
409422
}
410423
}
411424

‎src/interfaces/ecpg/preproc/pgc.l

Lines changed: 89 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,21 @@ char *token_start;
6969
staticint state_before_str_start;
7070
staticint state_before_str_stop;
7171

72-
struct_yy_buffer
72+
/*
73+
* State for handling include files and macro expansion. We use a new
74+
* flex input buffer for each level of include or macro, and create a
75+
* struct _yy_buffer to remember the previous level. There is not a struct
76+
* for the currently active input source; that state is kept in the global
77+
* variables YY_CURRENT_BUFFER, yylineno, and input_filename.
78+
*/
79+
staticstruct_yy_buffer
7380
{
7481
YY_BUFFER_STATEbuffer;
7582
longlineno;
7683
char *filename;
7784
struct_yy_buffer *next;
7885
} *yy_buffer =NULL;
7986

80-
staticchar *old;
81-
8287
/*
8388
* Vars for handling ifdef/elif/endif constructs. preproc_tos is the current
8489
* nesting depth of such constructs, and stacked_if_value[preproc_tos] is the
@@ -432,6 +437,8 @@ cppline{space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
432437

433438
%{
434439
/* code to execute during start of each call of yylex()*/
440+
char *newdefsymbol =NULL;
441+
435442
token_start =NULL;
436443
%}
437444

@@ -979,6 +986,7 @@ cppline{space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
979986
}
980987

981988
{identifier}{
989+
/* First check to see if it's a define symbol to expand */
982990
if (!isdefine())
983991
{
984992
intkwvalue;
@@ -1171,17 +1179,23 @@ cppline{space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
11711179
yytext[i+1] ='\0';
11721180

11731181

1174-
for (ptr = defines; ptr !=NULL; ptr2 = ptr, ptr = ptr->next)
1182+
/* Find and unset any matching define; should be only 1 */
1183+
for (ptr = defines; ptr; ptr2 = ptr, ptr = ptr->next)
11751184
{
1176-
if (strcmp(yytext, ptr->olddef) ==0)
1185+
if (strcmp(yytext, ptr->name) ==0)
11771186
{
1178-
if (ptr2 ==NULL)
1179-
defines = ptr->next;
1180-
else
1181-
ptr2->next = ptr->next;
1182-
free(ptr->newdef);
1183-
free(ptr->olddef);
1184-
free(ptr);
1187+
free(ptr->value);
1188+
ptr->value =NULL;
1189+
/* We cannot forget it if there's a cmdvalue */
1190+
if (ptr->cmdvalue ==NULL)
1191+
{
1192+
if (ptr2 ==NULL)
1193+
defines = ptr->next;
1194+
else
1195+
ptr2->next = ptr->next;
1196+
free(ptr->name);
1197+
free(ptr);
1198+
}
11851199
break;
11861200
}
11871201
}
@@ -1386,11 +1400,17 @@ cppline{space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
13861400
;
13871401
yytext[i+1] ='\0';
13881402

1389-
for (defptr = defines;
1390-
defptr !=NULL &&
1391-
strcmp(yytext, defptr->olddef) !=0;
1392-
defptr = defptr->next)
1393-
/* skip */ ;
1403+
/* Does a definition exist? */
1404+
for (defptr = defines; defptr; defptr = defptr->next)
1405+
{
1406+
if (strcmp(yytext, defptr->name) ==0)
1407+
{
1408+
/* Found it, but is it currently undefined? */
1409+
if (defptr->value ==NULL)
1410+
defptr =NULL;/* pretend it's not found */
1411+
break;
1412+
}
1413+
}
13941414

13951415
this_active = (defptr ? ifcond : !ifcond);
13961416
stacked_if_value[preproc_tos].active =
@@ -1411,7 +1431,7 @@ cppline{space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
14111431
yyterminate();
14121432
}
14131433
<def_ident>{identifier} {
1414-
old =mm_strdup(yytext);
1434+
newdefsymbol =mm_strdup(yytext);
14151435
BEGIN(def);
14161436
startlit();
14171437
}
@@ -1420,26 +1440,31 @@ cppline{space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
14201440
yyterminate();
14211441
}
14221442
<def>{space}*";"{
1423-
struct_defines *ptr, *this;
1443+
struct_defines *ptr;
14241444

1445+
/* Does it already exist? */
14251446
for (ptr = defines; ptr !=NULL; ptr = ptr->next)
14261447
{
1427-
if (strcmp(old, ptr->olddef) ==0)
1428-
{
1429-
free(ptr->newdef);
1430-
ptr->newdef =mm_strdup(literalbuf);
1431-
}
1448+
if (strcmp(newdefsymbol, ptr->name) ==0)
1449+
{
1450+
free(ptr->value);
1451+
ptr->value =mm_strdup(literalbuf);
1452+
/* Don't leak newdefsymbol */
1453+
free(newdefsymbol);
1454+
break;
1455+
}
14321456
}
14331457
if (ptr ==NULL)
14341458
{
1435-
this = (struct_defines *)mm_alloc(sizeof(struct_defines));
1436-
1437-
/* initial definition */
1438-
this->olddef = old;
1439-
this->newdef =mm_strdup(literalbuf);
1440-
this->next = defines;
1441-
this->used =NULL;
1442-
defines =this;
1459+
/* Not present, make a new entry */
1460+
ptr = (struct_defines *)mm_alloc(sizeof(struct_defines));
1461+
1462+
ptr->name = newdefsymbol;
1463+
ptr->value =mm_strdup(literalbuf);
1464+
ptr->cmdvalue =NULL;
1465+
ptr->used =NULL;
1466+
ptr->next = defines;
1467+
defines = ptr;
14431468
}
14441469

14451470
BEGIN(C);
@@ -1456,6 +1481,7 @@ cppline{space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
14561481
<<EOF>>{
14571482
if (yy_buffer ==NULL)
14581483
{
1484+
/* No more input */
14591485
if (preproc_tos >0)
14601486
{
14611487
preproc_tos =0;
@@ -1465,16 +1491,20 @@ cppline{space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
14651491
}
14661492
else
14671493
{
1494+
/* Revert to previous input source */
14681495
struct_yy_buffer *yb = yy_buffer;
14691496
int i;
14701497
struct_defines *ptr;
14711498

1499+
/* Check to see if we are exiting a macro value */
14721500
for (ptr = defines; ptr; ptr = ptr->next)
1501+
{
14731502
if (ptr->used == yy_buffer)
14741503
{
14751504
ptr->used =NULL;
1476-
break;
1505+
break;/* there can't be multiple matches */
14771506
}
1507+
}
14781508

14791509
if (yyin !=NULL)
14801510
fclose(yyin);
@@ -1699,15 +1729,24 @@ ecpg_isspace(char ch)
16991729
returnfalse;
17001730
}
17011731

1702-
staticboolisdefine(void)
1732+
/*
1733+
* If yytext matches a define symbol, begin scanning the symbol's value
1734+
* and return true
1735+
*/
1736+
staticbool
1737+
isdefine(void)
17031738
{
17041739
struct_defines *ptr;
17051740

17061741
/* is it a define? */
17071742
for (ptr = defines; ptr; ptr = ptr->next)
17081743
{
1709-
if (strcmp(yytext, ptr->olddef) ==0 && ptr->used ==NULL)
1744+
/* notice we do not match anything being actively expanded */
1745+
if (strcmp(yytext, ptr->name) ==0 &&
1746+
ptr->value !=NULL &&
1747+
ptr->used ==NULL)
17101748
{
1749+
/* Save state associated with the current buffer */
17111750
struct_yy_buffer *yb;
17121751

17131752
yb =mm_alloc(sizeof(struct_yy_buffer));
@@ -1716,18 +1755,30 @@ static bool isdefine(void)
17161755
yb->lineno = yylineno;
17171756
yb->filename =mm_strdup(input_filename);
17181757
yb->next = yy_buffer;
1758+
yy_buffer = yb;
17191759

1720-
ptr->used = yy_buffer = yb;
1760+
/* Mark symbol as being actively expanded */
1761+
ptr->used = yb;
17211762

1722-
yy_scan_string(ptr->newdef);
1763+
/*
1764+
* We use yy_scan_string which will copy the value, so there's
1765+
* no need to worry about a possible undef happening while we
1766+
* are still scanning it.
1767+
*/
1768+
yy_scan_string(ptr->value);
17231769
returntrue;
17241770
}
17251771
}
17261772

17271773
returnfalse;
17281774
}
17291775

1730-
staticboolisinformixdefine(void)
1776+
/*
1777+
* Handle replacement of INFORMIX built-in defines. This works just
1778+
* like isdefine() except for the source of the string to scan.
1779+
*/
1780+
staticbool
1781+
isinformixdefine(void)
17311782
{
17321783
constchar *new =NULL;
17331784

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp