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

Commit2b6a74a

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 parentf502849 commit2b6a74a

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
@@ -5687,6 +5687,14 @@ EXEC SQL UPDATE Tbl SET col = MYNUMBER;
56875687
embedded SQL query because in this case the embedded SQL precompiler is not
56885688
able to see this declaration.
56895689
</para>
5690+
5691+
<para>
5692+
If multiple input files are named on the <command>ecpg</command>
5693+
preprocessor's command line, the effects of <literal>EXEC SQL
5694+
DEFINE</literal> and <literal>EXEC SQL UNDEF</literal> do not carry
5695+
across files: each file starts with only the symbols defined
5696+
by <option>-D</option> switches on the command line.
5697+
</para>
56905698
</sect2>
56915699

56925700
<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
@@ -80,35 +80,46 @@ add_include_path(char *path)
8080
}
8181
}
8282

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

90-
defines=mm_alloc(sizeof(struct_defines));
94+
newdef=mm_alloc(sizeof(struct_defines));
9195

9296
/* look for = sign */
9397
ptr=strchr(define_copy,'=');
9498
if (ptr!=NULL)
9599
{
100+
/* symbol has a value */
96101
char*tmp;
97102

98-
/*symbol has a value */
99-
for (tmp=ptr-1;*tmp==' ';tmp--);
103+
/*strip any spaces between name and '=' */
104+
for (tmp=ptr-1;tmp >=define_copy&&*tmp==' ';tmp--);
100105
tmp[1]='\0';
101-
defines->olddef=define_copy;
102-
defines->newdef=ptr+1;
106+
107+
/*
108+
* Note we don't bother to separately malloc cmdvalue; it will never
109+
* be freed so that's not necessary.
110+
*/
111+
newdef->cmdvalue=ptr+1;
103112
}
104113
else
105114
{
106-
defines->olddef=define_copy;
107-
defines->newdef=mm_strdup("1");
115+
/* define it as "1"; again no need to malloc it */
116+
newdef->cmdvalue="1";
108117
}
109-
defines->pertinent= true;
110-
defines->used=NULL;
111-
defines->next=pd;
118+
newdef->name=define_copy;
119+
newdef->value=mm_strdup(newdef->cmdvalue);
120+
newdef->used=NULL;
121+
newdef->next=defines;
122+
defines=newdef;
112123
}
113124

114125
#defineECPG_GETOPT_LONG_REGRESSION1
@@ -345,6 +356,8 @@ main(int argc, char *const argv[])
345356
{
346357
structcursor*ptr;
347358
struct_defines*defptr;
359+
struct_defines*prevdefptr;
360+
struct_defines*nextdefptr;
348361
structtypedefs*typeptr;
349362

350363
/* remove old cursor definitions if any are still there */
@@ -372,28 +385,28 @@ main(int argc, char *const argv[])
372385
}
373386
cur=NULL;
374387

375-
/* remove non-pertinent old defines as well */
376-
while (defines&& !defines->pertinent)
388+
/* restore defines to their command-line state */
389+
prevdefptr=NULL;
390+
for (defptr=defines;defptr!=NULL;defptr=nextdefptr)
377391
{
378-
defptr=defines;
379-
defines=defines->next;
380-
381-
free(defptr->newdef);
382-
free(defptr->olddef);
383-
free(defptr);
384-
}
385-
386-
for (defptr=defines;defptr!=NULL;defptr=defptr->next)
387-
{
388-
struct_defines*this=defptr->next;
389-
390-
if (this&& !this->pertinent)
392+
nextdefptr=defptr->next;
393+
if (defptr->cmdvalue!=NULL)
391394
{
392-
defptr->next=this->next;
393-
394-
free(this->newdef);
395-
free(this->olddef);
396-
free(this);
395+
/* keep it, resetting the value */
396+
free(defptr->value);
397+
defptr->value=mm_strdup(defptr->cmdvalue);
398+
prevdefptr=defptr;
399+
}
400+
else
401+
{
402+
/* remove it */
403+
if (prevdefptr!=NULL)
404+
prevdefptr->next=nextdefptr;
405+
else
406+
defines=nextdefptr;
407+
free(defptr->name);
408+
free(defptr->value);
409+
free(defptr);
397410
}
398411
}
399412

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

Lines changed: 89 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,21 @@ static bool isinformixdefine(void);
6363
char *token_start;
6464
staticint state_before;
6565

66-
struct_yy_buffer
66+
/*
67+
* State for handling include files and macro expansion. We use a new
68+
* flex input buffer for each level of include or macro, and create a
69+
* struct _yy_buffer to remember the previous level. There is not a struct
70+
* for the currently active input source; that state is kept in the global
71+
* variables YY_CURRENT_BUFFER, yylineno, and input_filename.
72+
*/
73+
staticstruct_yy_buffer
6774
{
6875
YY_BUFFER_STATEbuffer;
6976
longlineno;
7077
char *filename;
7178
struct_yy_buffer *next;
7279
} *yy_buffer =NULL;
7380

74-
staticchar *old;
75-
7681
#defineMAX_NESTED_IF128
7782
staticshort preproc_tos;
7883
staticshort ifcond;
@@ -401,6 +406,8 @@ cppline{space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
401406

402407
%{
403408
/* code to execute during start of each call of yylex()*/
409+
char *newdefsymbol =NULL;
410+
404411
token_start =NULL;
405412
%}
406413

@@ -920,6 +927,7 @@ cppline{space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
920927
}
921928

922929
{identifier}{
930+
/* First check to see if it's a define symbol to expand */
923931
if (!isdefine())
924932
{
925933
intkwvalue;
@@ -1112,17 +1120,23 @@ cppline{space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
11121120
yytext[i+1] ='\0';
11131121

11141122

1115-
for (ptr = defines; ptr !=NULL; ptr2 = ptr, ptr = ptr->next)
1123+
/* Find and unset any matching define; should be only 1 */
1124+
for (ptr = defines; ptr; ptr2 = ptr, ptr = ptr->next)
11161125
{
1117-
if (strcmp(yytext, ptr->olddef) ==0)
1126+
if (strcmp(yytext, ptr->name) ==0)
11181127
{
1119-
if (ptr2 ==NULL)
1120-
defines = ptr->next;
1121-
else
1122-
ptr2->next = ptr->next;
1123-
free(ptr->newdef);
1124-
free(ptr->olddef);
1125-
free(ptr);
1128+
free(ptr->value);
1129+
ptr->value =NULL;
1130+
/* We cannot forget it if there's a cmdvalue */
1131+
if (ptr->cmdvalue ==NULL)
1132+
{
1133+
if (ptr2 ==NULL)
1134+
defines = ptr->next;
1135+
else
1136+
ptr2->next = ptr->next;
1137+
free(ptr->name);
1138+
free(ptr);
1139+
}
11261140
break;
11271141
}
11281142
}
@@ -1299,11 +1313,17 @@ cppline{space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
12991313
;
13001314
yytext[i+1] ='\0';
13011315

1302-
for (defptr = defines;
1303-
defptr !=NULL &&
1304-
strcmp(yytext, defptr->olddef) !=0;
1305-
defptr = defptr->next)
1306-
/* skip */ ;
1316+
/* Does a definition exist? */
1317+
for (defptr = defines; defptr; defptr = defptr->next)
1318+
{
1319+
if (strcmp(yytext, defptr->name) ==0)
1320+
{
1321+
/* Found it, but is it currently undefined? */
1322+
if (defptr->value ==NULL)
1323+
defptr =NULL;/* pretend it's not found */
1324+
break;
1325+
}
1326+
}
13071327

13081328
preproc_tos++;
13091329
stacked_if_value[preproc_tos].else_branch =false;
@@ -1322,7 +1342,7 @@ cppline{space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
13221342
yyterminate();
13231343
}
13241344
<def_ident>{identifier} {
1325-
old =mm_strdup(yytext);
1345+
newdefsymbol =mm_strdup(yytext);
13261346
BEGIN(def);
13271347
startlit();
13281348
}
@@ -1331,26 +1351,31 @@ cppline{space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
13311351
yyterminate();
13321352
}
13331353
<def>{space}*";"{
1334-
struct_defines *ptr, *this;
1354+
struct_defines *ptr;
13351355

1356+
/* Does it already exist? */
13361357
for (ptr = defines; ptr !=NULL; ptr = ptr->next)
13371358
{
1338-
if (strcmp(old, ptr->olddef) ==0)
1339-
{
1340-
free(ptr->newdef);
1341-
ptr->newdef =mm_strdup(literalbuf);
1342-
}
1359+
if (strcmp(newdefsymbol, ptr->name) ==0)
1360+
{
1361+
free(ptr->value);
1362+
ptr->value =mm_strdup(literalbuf);
1363+
/* Don't leak newdefsymbol */
1364+
free(newdefsymbol);
1365+
break;
1366+
}
13431367
}
13441368
if (ptr ==NULL)
13451369
{
1346-
this = (struct_defines *)mm_alloc(sizeof(struct_defines));
1347-
1348-
/* initial definition */
1349-
this->olddef = old;
1350-
this->newdef =mm_strdup(literalbuf);
1351-
this->next = defines;
1352-
this->used =NULL;
1353-
defines =this;
1370+
/* Not present, make a new entry */
1371+
ptr = (struct_defines *)mm_alloc(sizeof(struct_defines));
1372+
1373+
ptr->name = newdefsymbol;
1374+
ptr->value =mm_strdup(literalbuf);
1375+
ptr->cmdvalue =NULL;
1376+
ptr->used =NULL;
1377+
ptr->next = defines;
1378+
defines = ptr;
13541379
}
13551380

13561381
BEGIN(C);
@@ -1367,6 +1392,7 @@ cppline{space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
13671392
<<EOF>>{
13681393
if (yy_buffer ==NULL)
13691394
{
1395+
/* No more input */
13701396
if ( preproc_tos >0 )
13711397
{
13721398
preproc_tos =0;
@@ -1376,16 +1402,20 @@ cppline{space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
13761402
}
13771403
else
13781404
{
1405+
/* Revert to previous input source */
13791406
struct_yy_buffer *yb = yy_buffer;
13801407
int i;
13811408
struct_defines *ptr;
13821409

1410+
/* Check to see if we are exiting a macro value */
13831411
for (ptr = defines; ptr; ptr = ptr->next)
1412+
{
13841413
if (ptr->used == yy_buffer)
13851414
{
13861415
ptr->used =NULL;
1387-
break;
1416+
break;/* there can't be multiple matches */
13881417
}
1418+
}
13891419

13901420
if (yyin !=NULL)
13911421
fclose(yyin);
@@ -1608,15 +1638,24 @@ ecpg_isspace(char ch)
16081638
returnfalse;
16091639
}
16101640

1611-
staticboolisdefine(void)
1641+
/*
1642+
* If yytext matches a define symbol, begin scanning the symbol's value
1643+
* and return true
1644+
*/
1645+
staticbool
1646+
isdefine(void)
16121647
{
16131648
struct_defines *ptr;
16141649

16151650
/* is it a define? */
16161651
for (ptr = defines; ptr; ptr = ptr->next)
16171652
{
1618-
if (strcmp(yytext, ptr->olddef) ==0 && ptr->used ==NULL)
1653+
/* notice we do not match anything being actively expanded */
1654+
if (strcmp(yytext, ptr->name) ==0 &&
1655+
ptr->value !=NULL &&
1656+
ptr->used ==NULL)
16191657
{
1658+
/* Save state associated with the current buffer */
16201659
struct_yy_buffer *yb;
16211660

16221661
yb =mm_alloc(sizeof(struct_yy_buffer));
@@ -1625,18 +1664,30 @@ static bool isdefine(void)
16251664
yb->lineno = yylineno;
16261665
yb->filename =mm_strdup(input_filename);
16271666
yb->next = yy_buffer;
1667+
yy_buffer = yb;
16281668

1629-
ptr->used = yy_buffer = yb;
1669+
/* Mark symbol as being actively expanded */
1670+
ptr->used = yb;
16301671

1631-
yy_scan_string(ptr->newdef);
1672+
/*
1673+
* We use yy_scan_string which will copy the value, so there's
1674+
* no need to worry about a possible undef happening while we
1675+
* are still scanning it.
1676+
*/
1677+
yy_scan_string(ptr->value);
16321678
returntrue;
16331679
}
16341680
}
16351681

16361682
returnfalse;
16371683
}
16381684

1639-
staticboolisinformixdefine(void)
1685+
/*
1686+
* Handle replacement of INFORMIX built-in defines. This works just
1687+
* like isdefine() except for the source of the string to scan.
1688+
*/
1689+
staticbool
1690+
isinformixdefine(void)
16401691
{
16411692
constchar *new =NULL;
16421693

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp