Skip to content

Integration of EXEC preprocessor beyond baseline. - #294

Open
utam-1 wants to merge 25 commits into
OCamlPro:gitside-gnucobol-3.xfrom
utam-1:exec-preprocessor-beyond-baseline
Open

Integration of EXEC preprocessor beyond baseline.#294
utam-1 wants to merge 25 commits into
OCamlPro:gitside-gnucobol-3.xfrom
utam-1:exec-preprocessor-beyond-baseline

Conversation

@utam-1

@utam-1 utam-1 commented Jun 14, 2026

Copy link
Copy Markdown

Description of PR

This is a follow - up to #279.

Approach and basic idea

cb_preparser_entry is added to cobc.c, which handles the associated pre-parser metadata. cb_preparser_list and cb_active_preparser are created, one being a list to registered preparsers, and other is a pointer for pplex.l to identify the tag.

cb_load_preparser_conf() and cb_find_preparser() are created based on similar mechanism of cb_config_entry().
In ppparse.y a token SUBSYSTEM_FOUND is declared which is used for identifying EOF, and for gracefully exiting the parsing.

So far only general tests have been performed by using a test.conf file and setting up a pseudo - preprocessor via bash, which shows the replacement works and flag-injection is performed via gcc.

@utam-1
utam-1 marked this pull request as draft June 14, 2026 02:38
@utam-1
utam-1 marked this pull request as ready for review June 29, 2026 14:40

@GitMensch GitMensch left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • new changes need a new Changelog entry
  • we'll need a NEWS entry for the new --preparser, as well as help.c and gnucobol.texi addition; the later should also document a bit about the file format
  • please add your tests (you've seem to have done some) to used_binaries.at; we'd need at least:
    • --preparser for not existing file
    • --preparser for existing, but invalid file
    • --preparser for existing file
    • --preparser for two existing files
    • --preparser, where pseudo preparser runs fine
    • --preparser, where psuedo preparser runs in error

Comment thread cobc/cobc.h
Comment thread cobc/pplex.l Outdated
Comment thread cobc/cobc.c Outdated
Comment thread cobc/cobc.c Outdated
Comment thread cobc/cobc.c Outdated
Comment on lines +3125 to +3253
/* Resolve preparser config filename: explicit path (contains separator
* or exists as-is) is used directly; otherwise look up
* COB_CONFIG_DIR/<name>.conf, mirroring cb_load_conf_file(). */
static FILE *
open_preparser_conf (const char *name, char *resolved, size_t resolved_size)
{
FILE *fp;
size_t i;

for (i = 0; name[i] != 0 && name[i] != SLASH_CHAR; i++);

if (name[i] != 0 || access (name, F_OK) == 0) {
/* contains a path separator, or exists as given */
snprintf (resolved, resolved_size, "%s", name);
} else {
/* plain name: look in COB_CONFIG_DIR/<name>.conf */
snprintf (resolved, resolved_size, "%s%c%s.conf",
cob_config_dir, SLASH_CHAR, name);
}

fp = fopen (resolved, "r");
return fp;
}

/* Parse a single "key: value" or "key value" line.
* Leading/trailing whitespace and comments (#) are stripped. */
static int
parse_preparser_line (char *buff, char **key, char **val)
{
char *p, *colon;

/* strip comment */
if ((p = strchr (buff, '#')) != NULL) {
*p = '\0';
}
/* strip trailing whitespace/newline */
for (p = buff + strlen (buff); p > buff && isspace ((unsigned char)p[-1]); p--);
*p = '\0';
/* strip leading whitespace */
for (p = buff; isspace ((unsigned char)*p); p++);
if (*p == '\0') {
return 1; /* blank line */
}

*key = p;
if ((colon = strchr (p, ':')) != NULL) {
*colon = '\0';
p = colon + 1;
} else {
/* space-separated: key value */
for (; *p && !isspace ((unsigned char)*p); p++);
if (*p) {
*p++ = '\0';
}
}
for (; isspace ((unsigned char)*p); p++);
*val = p;

/* trim trailing whitespace of key */
for (p = *key + strlen (*key); p > *key && isspace ((unsigned char)p[-1]); p--);
*p = '\0';

return 0;
}

/* Load an external preparser configuration file and register it
* in cb_preparser_list. Returns 0 on success, non-zero on error. */
int
cb_load_preparser_conf (const char *name)
{
FILE *fp;
char resolved[COB_NORMAL_BUFF];
char buff[COB_SMALL_BUFF];
struct cb_preparser_entry *pe;

fp = open_preparser_conf (name, resolved, sizeof (resolved));
if (!fp) {
cb_error (_("preparser configuration '%s' not found"), name);
return 1;
}

pe = cobc_main_malloc (sizeof (struct cb_preparser_entry));
pe->tag = NULL;
pe->command = NULL;
pe->cflags = NULL;
pe->ldflags = NULL;
pe->on_error = 1; /* default: error */
pe->used = 0;
pe->next = NULL;

while (fgets (buff, sizeof (buff), fp)) {
char *key, *val;

if (parse_preparser_line (buff, &key, &val) != 0) {
continue;
}

if (strcasecmp (key, "tag") == 0) {
size_t i;
pe->tag = cobc_main_strdup (val);
for (i = 0; pe->tag[i]; i++) {
pe->tag[i] = (char)toupper ((unsigned char)pe->tag[i]);
}
} else if (strcasecmp (key, "command") == 0) {
pe->command = cobc_main_strdup (val);
} else if (strcasecmp (key, "cflags") == 0) {
pe->cflags = cobc_main_strdup (val);
} else if (strcasecmp (key, "ldflags") == 0) {
pe->ldflags = cobc_main_strdup (val);
} else if (strcasecmp (key, "on-error") == 0) {
pe->on_error = (strcasecmp (val, "warn") == 0) ? 0 : 1;
} else {
cb_warning (cb_warn_unsupported,
_("unknown preparser configuration key '%s' in '%s'"),
key, resolved);
}
}
fclose (fp);

if (!pe->tag || !pe->command) {
cb_error (_("preparser configuration '%s' is missing 'tag' or 'command'"),
resolved);
return 1;
}

pe->next = cb_preparser_list;
cb_preparser_list = pe;
return 0;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

those functions should be moved to config.c - and, where possible, the existing functions be refactored to not have duplicated code for how the lines are read/split

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept the implementation for the two differently as they follow different kinds of implementation ( config table and Linked list ) the coupling between these two may make it difficult to maintain in my opinion.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parse_preparser_line() should be replaced by re-using the code for the config lines; to do so move the code of cb_config_entry for the parsing out of the function (the new static function gets a pointer to name+value, just as your version) - in this new variant name and value will be parsed first before cb_config_entry() checks the name (which is now intermixed).

This way we have a single new function called for both compiler and preparser config, ensuring the format is exactly identical, by keeping the way it is looked up / used out of that code.

I agree that cb_load_preparser_conf() itself should be kept separate from the config file loading (though they will look very similar) because of the struct / array approach (which we don't need fo the preparser).

Comment thread cobc/cobc.h Outdated
Comment thread cobc/cobc.c Outdated
Comment thread cobc/cobc.c Outdated
Comment thread cobc/cobc.c Outdated

@GitMensch GitMensch left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

open from last review:

  • new changes need a new Changelog entry (you've added them, just in the wrong place)
  • gnucobol.texi should also document a bit about the file format

Comment thread doc/gnucobol.texi Outdated
User-defined dialect configuration.

@item --preparser=<file>
Register external preparser configuration.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a start, but similar to the note below you need to explain what this is used for and either document the structure here or add an example definition (for esqlOC and/or GixSQL) showing the format with explanations.

Comment thread cobc/cobc.c Outdated
Comment on lines +3125 to +3253
/* Resolve preparser config filename: explicit path (contains separator
* or exists as-is) is used directly; otherwise look up
* COB_CONFIG_DIR/<name>.conf, mirroring cb_load_conf_file(). */
static FILE *
open_preparser_conf (const char *name, char *resolved, size_t resolved_size)
{
FILE *fp;
size_t i;

for (i = 0; name[i] != 0 && name[i] != SLASH_CHAR; i++);

if (name[i] != 0 || access (name, F_OK) == 0) {
/* contains a path separator, or exists as given */
snprintf (resolved, resolved_size, "%s", name);
} else {
/* plain name: look in COB_CONFIG_DIR/<name>.conf */
snprintf (resolved, resolved_size, "%s%c%s.conf",
cob_config_dir, SLASH_CHAR, name);
}

fp = fopen (resolved, "r");
return fp;
}

/* Parse a single "key: value" or "key value" line.
* Leading/trailing whitespace and comments (#) are stripped. */
static int
parse_preparser_line (char *buff, char **key, char **val)
{
char *p, *colon;

/* strip comment */
if ((p = strchr (buff, '#')) != NULL) {
*p = '\0';
}
/* strip trailing whitespace/newline */
for (p = buff + strlen (buff); p > buff && isspace ((unsigned char)p[-1]); p--);
*p = '\0';
/* strip leading whitespace */
for (p = buff; isspace ((unsigned char)*p); p++);
if (*p == '\0') {
return 1; /* blank line */
}

*key = p;
if ((colon = strchr (p, ':')) != NULL) {
*colon = '\0';
p = colon + 1;
} else {
/* space-separated: key value */
for (; *p && !isspace ((unsigned char)*p); p++);
if (*p) {
*p++ = '\0';
}
}
for (; isspace ((unsigned char)*p); p++);
*val = p;

/* trim trailing whitespace of key */
for (p = *key + strlen (*key); p > *key && isspace ((unsigned char)p[-1]); p--);
*p = '\0';

return 0;
}

/* Load an external preparser configuration file and register it
* in cb_preparser_list. Returns 0 on success, non-zero on error. */
int
cb_load_preparser_conf (const char *name)
{
FILE *fp;
char resolved[COB_NORMAL_BUFF];
char buff[COB_SMALL_BUFF];
struct cb_preparser_entry *pe;

fp = open_preparser_conf (name, resolved, sizeof (resolved));
if (!fp) {
cb_error (_("preparser configuration '%s' not found"), name);
return 1;
}

pe = cobc_main_malloc (sizeof (struct cb_preparser_entry));
pe->tag = NULL;
pe->command = NULL;
pe->cflags = NULL;
pe->ldflags = NULL;
pe->on_error = 1; /* default: error */
pe->used = 0;
pe->next = NULL;

while (fgets (buff, sizeof (buff), fp)) {
char *key, *val;

if (parse_preparser_line (buff, &key, &val) != 0) {
continue;
}

if (strcasecmp (key, "tag") == 0) {
size_t i;
pe->tag = cobc_main_strdup (val);
for (i = 0; pe->tag[i]; i++) {
pe->tag[i] = (char)toupper ((unsigned char)pe->tag[i]);
}
} else if (strcasecmp (key, "command") == 0) {
pe->command = cobc_main_strdup (val);
} else if (strcasecmp (key, "cflags") == 0) {
pe->cflags = cobc_main_strdup (val);
} else if (strcasecmp (key, "ldflags") == 0) {
pe->ldflags = cobc_main_strdup (val);
} else if (strcasecmp (key, "on-error") == 0) {
pe->on_error = (strcasecmp (val, "warn") == 0) ? 0 : 1;
} else {
cb_warning (cb_warn_unsupported,
_("unknown preparser configuration key '%s' in '%s'"),
key, resolved);
}
}
fclose (fp);

if (!pe->tag || !pe->command) {
cb_error (_("preparser configuration '%s' is missing 'tag' or 'command'"),
resolved);
return 1;
}

pe->next = cb_preparser_list;
cb_preparser_list = pe;
return 0;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parse_preparser_line() should be replaced by re-using the code for the config lines; to do so move the code of cb_config_entry for the parsing out of the function (the new static function gets a pointer to name+value, just as your version) - in this new variant name and value will be parsed first before cb_config_entry() checks the name (which is now intermixed).

This way we have a single new function called for both compiler and preparser config, ensuring the format is exactly identical, by keeping the way it is looked up / used out of that code.

I agree that cb_load_preparser_conf() itself should be kept separate from the config file loading (though they will look very similar) because of the struct / array approach (which we don't need fo the preparser).

Comment thread cobc/ChangeLog Outdated
Comment thread cobc/ChangeLog

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you want one entry for the baseline (with the old date) and another one for the follow-up work, which then outlines the changes in config.c, tree.h, ...

Comment thread cobc/ChangeLog Outdated

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that whole block should - for the baseline changes - be after the may changes, nomt up-front

Comment thread cobc/cobc.c
Comment thread cobc/config.c Outdated
#include <string.h>
#include <limits.h>

#include <ctype.h>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the existing code has no isspace, so you can get rid of that header when refactoring the existing code to be used for the tag/value split

Comment thread ChangeLog Outdated
Comment on lines +2 to +9
2026-07-31 Uttam Bhadauriya <uttamsinghbhadoriya23@gmail.com>

* cobc/cobc.h, cobc/tree.h, cobc/cobc.c, cobc/config.c, cobc/pplex.l:
Refactor external preparser subsystem, move struct and functions,
rename tag to subsystem, improve error messages and indentation.
* cobc/help.c, doc/gnucobol.texi: Add --preparser documentation.
* tests/testsuite.src/used_binaries.at: Add preparser tests.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cobc and doc go to their own changelog files, the testsuite sources don't get an entry for new elements

Comment thread NEWS Outdated

* New GnuCOBOL features

** cobc provides a --preparser option to register external preparser configurations

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

explain here that EXEC is now supported using external preparsers, registered with --preparser (you can add a hint to check the details in the manual, similar to the existing entries doing so) with a fallback of ignoring any EXEC parts but INCLUDE

Comment thread NEWS Outdated
* New GnuCOBOL features

** cobc provides a --preparser option to register external preparser configurations

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new entries to the NEWS file are added at the end of the relevant chapter

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's still a TODO

Comment thread tests/testsuite.src/used_binaries.at Outdated
Comment on lines +1626 to +1627
AT_CHECK([$COBC --preparser missing.conf prog.cob], [1], [], [stderr])
AT_CHECK([$GREP "cannot load preparser configuration 'missing.conf'" stderr], [0], [ignore], [ignore])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haven't checked the ones below, but instead of placing that to an stderr where you grep from (which either needs a redirection or I learn something new here) you'd just check the output in that [] directly.

Comment thread tests/testsuite.src/used_binaries.at Outdated
AT_CLEANUP

AT_SETUP([external preparser --preparser])
AT_KEYWORDS([preparser])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
AT_KEYWORDS([preparser])
AT_KEYWORDS([cobc configuration EXEC])

you can replace the keyword by cobc + configuration - other words are already included by AT_SETUP content

Comment thread tests/testsuite.src/used_binaries.at
@GitMensch

Copy link
Copy Markdown
Collaborator

@utam-1 please rebase, which should improve the MSVC part and update per review, so we can have a next look at that

GitMensch and others added 7 commits August 6, 2026 09:36
MacOS: pass std=gnu17

MSYS2: disable screenio tests for now

overall actions update (fix npm deprecation)
cobc/:
  * config.def: "tab-width" option is changed to a comma-separated
    list of tab widths, for example "6,1,4", the last one being reused.
    This new meaning is backward compatible.
    Implements FR #498 " Adjust tab-width to optionally be a list of
    tab-stop positions"
  * config.c: initialize cb_tab_width as a string,
    each position indicating the number of spaces to insert for a tab
    at that position
  * pplex.c,cobc.c: use the new type of cb_tab_width
cobc:
* tree.h, parser.y: change type of cobc_cs_check flags to permit distinguishing more grammar contexts
* cobc.h, parser.y, reserved.c (lookup_reserved_word): generalize and cleanup device for handling special contexts
* parser.y: use new macros __CS_ENSURE, __CS_CLEAR, __CS_CLEAR_ALL, __CS_CHECK, __CS_LEAVE, __CS_ENTER, to help debug and explicitly manipulate special contexts
* error.c (cb_error_always, cb_error_internal), tree.h, tree.c (cb_build_program), scanner.l: replace cobc_in_repository flag with a check on a special context
* scanner.l: introduce cobc_in_exit_statement helper macro
@utam-1
utam-1 force-pushed the exec-preprocessor-beyond-baseline branch from 6581475 to 4054ab6 Compare August 6, 2026 04:50
@utam-1

utam-1 commented Aug 6, 2026

Copy link
Copy Markdown
Author

@utam-1 please rebase, which should improve the MSVC part and update per review, so we can have a next look at that

I have pushed my changes, and apologies for the delay I was a bit occupied.

lefessan and others added 3 commits August 6, 2026 19:10
cobc/:
  * config.def: "tab-width" option is changed to a comma-separated
    list of tab widths, for example "6,1,4", the last one being reused.
    This new meaning is backward compatible.
    Implements FR #498 " Adjust tab-width to optionally be a list of
    tab-stop positions"
  * config.c: initialize cb_tab_width as a string,
    each position indicating the number of spaces to insert for a tab
    at that position
  * pplex.c,cobc.c: use the new type of cb_tab_width
cobc:
* tree.h, parser.y: change type of cobc_cs_check flags to permit distinguishing more grammar contexts
* cobc.h, parser.y, reserved.c (lookup_reserved_word): generalize and cleanup device for handling special contexts
* parser.y: use new macros __CS_ENSURE, __CS_CLEAR, __CS_CLEAR_ALL, __CS_CHECK, __CS_LEAVE, __CS_ENTER, to help debug and explicitly manipulate special contexts
* error.c (cb_error_always, cb_error_internal), tree.h, tree.c (cb_build_program), scanner.l: replace cobc_in_repository flag with a check on a special context
* scanner.l: introduce cobc_in_exit_statement helper macro
Credits goes to Saurabh Kumar for the use of vswhere to locate the Visual Studio installation.

Co-authored-by: Saurabh Kumar <developer.saurabh@outlook.com>
@utam-1
utam-1 force-pushed the exec-preprocessor-beyond-baseline branch from c9fe3f3 to 7b34538 Compare August 6, 2026 13:58
@utam-1

utam-1 commented Aug 6, 2026

Copy link
Copy Markdown
Author

@GitMensch I think the issue with msvc persists, maybe I made some mistake while doing the rebase. I'm not sure though.

Comment thread tests/testsuite.src/used_binaries.at Outdated
Comment on lines +1626 to +1639
# 1. nonexistent file -> error
AT_CHECK([$COBC --preparser ./missing.conf prog.cob], [1], [],
[configuration error:
./missing.conf: No such file or directory
])

# 2. invalid file -> error
AT_DATA([invalid.conf], [
# missing subsystem and command
cflags: -O2
])
AT_CHECK([$COBC --preparser ./invalid.conf prog.cob], [1], [],
[error: preparser configuration './invalid.conf' is missing 'subsystem' or 'command'
])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to move these parts to configuration.at and add the missing "unknown configuration tag" code path

Comment thread cobc/config.c Outdated
Comment on lines +980 to +982
/* Simplified: Always append .conf for plain names in config dir */
snprintf (resolved, resolved_size, "%s%c%s.conf",
cob_config_dir, SLASH_CHAR, name);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this simplification is likely the reason for the win32 failures:

  • you want to only do the fopen below if the access worked
  • the snprintf should only be done if the access worked as you want to point to the "normal" name if there is neither one with the original name nor with the config dir

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made this change but it seems to fail again for some reason.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you still set resolved_size if the path did not exist and use it afterwards, I'll do an edit you can inspect and we'll see if that helps

@utam-1
utam-1 force-pushed the exec-preprocessor-beyond-baseline branch 2 times, most recently from caafec2 to c7aa539 Compare August 7, 2026 04:44
@utam-1

utam-1 commented Aug 7, 2026

Copy link
Copy Markdown
Author

@GitMensch The tests are still failing, I'm not sure what's causing this from the error messages it says about some dialect of C89 / C11 not being met ( for declartion of for-loop ) and some other path related issues are present.

@GitMensch

Copy link
Copy Markdown
Collaborator

@GitMensch The tests are still failing, I'm not sure what's causing this from the error messages it says about some dialect of C89 / C11 not being met ( for declartion of for-loop ) and some other path related issues are present.

That's

../../cobc/config.c: In function ‘cb_conf_split_line’:
../../cobc/config.c:938:17: error: ‘for’ loop initial declarations are only allowed in C99 or C11 mode
  938 |                 for (size_t j = strlen (buff);
      |                 ^~~
../../cobc/config.c:938:17: note: use option ‘-std=c99’, ‘-std=gnu99’, ‘-std=c11’ or ‘-std=gnu11’ to compile your code

and it is nice to see that the message got much better over the years :-)

It means "for" is a statement, and "size_t j" is a declaration. In C89 (the compatibility check done here) declarations need to be at the start of a block, so that rule is broken. The fix here is to move the size_t declaration before the for statement - but we don't need that in any case as we can directly adjust the pointer buff.
I'll include that in my commit.

@utam-1

utam-1 commented Aug 7, 2026

Copy link
Copy Markdown
Author

@GitMensch The tests are still failing, I'm not sure what's causing this from the error messages it says about some dialect of C89 / C11 not being met ( for declartion of for-loop ) and some other path related issues are present.

That's

../../cobc/config.c: In function ‘cb_conf_split_line’:
../../cobc/config.c:938:17: error: ‘for’ loop initial declarations are only allowed in C99 or C11 mode
  938 |                 for (size_t j = strlen (buff);
      |                 ^~~
../../cobc/config.c:938:17: note: use option ‘-std=c99’, ‘-std=gnu99’, ‘-std=c11’ or ‘-std=gnu11’ to compile your code

and it is nice to see that the message got much better over the years :-)

It means "for" is a statement, and "size_t j" is a declaration. In C89 (the compatibility check done here) declarations need to be at the start of a block, so that rule is broken. The fix here is to move the size_t declaration before the for statement - but we don't need that in any case as we can directly adjust the pointer buff. I'll include that in my commit.

This is rather interesting; I'll review (and learn) from the commit for both the fix related to MSVC build and this issue once it is applied.

@GitMensch

GitMensch commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

work will still take some time, so I did an untested (not even compiled so far) commit - if it fails it should still show the idea...

edit: seems you only need to update the expected test result to let it pass;
@utam-1 if you have any questions on the changes we can schedule a quick meeting

Comment thread cobc/ChangeLog
functions to config.c; share line-parsing with cb_config_entry;
fix indentation; fix cmd_len calculation
* help.c: add --preparser option documentation
2026-06-08 Nicolas Berthier <nicolas.berthier@ocamlpro.com>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
2026-06-08 Nicolas Berthier <nicolas.berthier@ocamlpro.com>
2026-06-08 Nicolas Berthier <nicolas.berthier@ocamlpro.com>

Comment thread cobc/ChangeLog
* config.c: initialize cb_tab_width as a string,
each position indicating the number of spaces to insert for a tab
at that position
* pplex.c,cobc.c: use the new type of cb_tab_width

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* pplex.c,cobc.c: use the new type of cb_tab_width
* pplex.l, cobc.c: use the new type of cb_tab_width

... but I'm a but confused where that entry comes from...

Comment thread doc/ChangeLog
Comment thread cobc/tree.h Outdated
@utam-1
utam-1 force-pushed the exec-preprocessor-beyond-baseline branch 2 times, most recently from eaa9ed4 to 37fdedc Compare August 8, 2026 14:28
@utam-1

utam-1 commented Aug 8, 2026

Copy link
Copy Markdown
Author

work will still take some time, so I did an untested (not even compiled so far) commit - if it fails it should still show the idea...

edit: seems you only need to update the expected test result to let it pass; @utam-1 if you have any questions on the changes we can schedule a quick meeting

@GitMensch I was able to get an insight on the approach, especially for the fix related to movement of pointer in buff instead of relying at size_t. However, I believe the issue related to MSVC is still there no?
Edit: The tests are passing successfully now, I modified the tests and changed the path look-up. Let me know if any further changes are needed.

@utam-1
utam-1 force-pushed the exec-preprocessor-beyond-baseline branch from 6f664b0 to c3c0a87 Compare August 9, 2026 01:44

@GitMensch GitMensch left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The overall tests and code look good - have you already run it sucessfully with the gixsql examples running gixsql?

If yes, please add the sample configuration for this (and possibly others like esqloc and esql) to the config folder.

If not: is there anything missing but the testing?

There are 3 main issues where I've asked (or suggested) for changes, I hope they are explained well enough - if not, feel free to ask here directly.

Comment thread NEWS Outdated
* New GnuCOBOL features

** cobc provides a --preparser option to register external preparser configurations

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's still a TODO

prog.cob:6: error: EXEC SQL statement ignored
])

AT_CLEANUP

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
AT_CLEANUP
AT_CLEANUP

don't ask me why but some diff tools work better if there's an empty line after the cleanup...

Comment on lines +3078 to +3080
AT_CHECK([$COMPILE_ONLY -Werror=unsupported -ffold-copy=LOWER prog.cob], [1], [],
[prog.cob:8: error: EXEC SQL INCLUDE handled as COPY
])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
AT_CHECK([$COMPILE_ONLY -Werror=unsupported -ffold-copy=LOWER prog.cob], [1], [],
[prog.cob:8: error: EXEC SQL INCLUDE handled as COPY
])
AT_CHECK([$COMPILE_ONLY -Wno-unsupported -ffold-copy=LOWER prog.cob], [0], [], [])

we want to verify that the copy is read, so no reason for an error

Comment on lines +3102 to +3105
AT_CHECK([$COMPILE_ONLY -Werror=unsupported -ffold-copy=LOWER prog.cob], [1], [],
[prog.cob:8: error: EXEC SQL INCLUDE handled as COPY
])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
AT_CHECK([$COMPILE_ONLY -Werror=unsupported -ffold-copy=LOWER prog.cob], [1], [],
[prog.cob:8: error: EXEC SQL INCLUDE handled as COPY
])
AT_CHECK([$COMPILE_ONLY -Wno-unsupported -ffold-copy=LOWER prog.cob], [0], [], [])

same as above

Comment on lines +3122 to +3125
AT_CHECK([$COMPILE_ONLY -Werror=unsupported prog.cob], [1], [],
[prog.cob:9: error: missing END-EXEC at end of file
prog.cob:9: error: EXEC SQL statement ignored
])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
AT_CHECK([$COMPILE_ONLY -Werror=unsupported prog.cob], [1], [],
[prog.cob:9: error: missing END-EXEC at end of file
prog.cob:9: error: EXEC SQL statement ignored
])
AT_CHECK([$COMPILE_ONLY -Wunsupported prog.cob], [1], [],
[prog.cob:9: error: missing END-EXEC at end of file
prog.cob:9: warning: EXEC SQL statement ignored
])

no need to error that explicit - but the missing part should be an error in any case

Comment thread cobc/cobc.c Outdated
Comment on lines +5490 to +5491
cmd_len = strlen (cb_active_preparser->command) + strlen (fn->source) + strlen (fn->preprocess) + 3;
cmd = cobc_malloc (cmd_len);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move those two line directly to the declaration (making cmd_len a const and reorder it)

Comment thread cobc/cobc.c Outdated
char *cmd;
size_t cmd_len;
int ret_sys;
const char *new_preprocess;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drop that var, assigning it directly to fn->preprocess

Comment thread cobc/cobc.c Outdated
cb_source_file = fn->source;
if (!cb_active_preparser->warn_only) {
cobc_err_exit (_("external preparser '%s' failed with exit status %d"), cb_active_preparser->subsystem, ret_sys);
} else {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for the else branch, the err_exit stops the program so everything in an else can be directly placed in the code after the closing }

Comment thread cobc/cobc.c Outdated
Comment on lines +5504 to +5505
fn->source = orig_source; /* Restore the original file names */
fn->preprocess = orig_preprocess;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not checked, just wondering: have we overridden those vars at all? Aren't they still fine?

Comment thread cobc/cobc.c Outdated
Comment on lines +5510 to +5521
/*This prevents collision in file paths based on manual tests*/
new_preprocess = file_replace_extension ((char *)fn->preprocess, ".i2");

fn->source = cobc_strdup (fn->preprocess); /* sqlpp.sh's output */
fn->preprocess = new_preprocess; /* fresh file for pass 2 */

if (cb_active_preparser->cflags) {
COBC_ADD_STR (cobc_cflags, " ", cb_active_preparser->cflags, NULL);
}
if (cb_active_preparser->ldflags) {
COBC_ADD_STR (cobc_ldflags, " ", cb_active_preparser->ldflags, NULL);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can be the else branch for the failure above... actually better check if (ret_sys )= 0) { and have that in the first branch, the hopefully less likely error case in the else - and finally the goto outside of both

Comment thread cobc/config.c Outdated
Comment on lines +938 to +941
e = buff + strlen (buff);
while (e > buff && (e[-1] == '\r' || e[-1] == '\n')) {
*--e = 0;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's slightly confusing - Is there a reason to not subtract 1 when computing e and checking e directly instead of e[-1}?
You can then also just do e--; in the loop and end with a final *(e + 1) = 0; before the return.

... just wondering.

@utam-1 utam-1 Aug 12, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My first thoughts were to use something cleaner and readable as you suggested, but from what I gathered the subtract 1 rule results in an undefined behaviour ( when buff is empty ) as that'll make e point to an address before the start of array. Modern architectures do not take this thing into account but maybe for stricter architectures it may result in issues.
Be as it may, I have updated the code, as per your suggestions.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from what I gathered the subtract 1 rule results in an undefined behaviour ( when buff is empty )

Just interested to follow your thought: How can that be with the previous code (skipping empty lines, and the separator not found)?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right the previous lines do ensure that buff doesn't stay empty, but then again there are issues with aggressive compiler optimization if we're using Undefined Behaviour code, as they're picked up as warnings of some sort in old school systems, apart from that if someone includes a static analysis tools do tend to flag such codes if they're present. I personally would not vouch for encountering issues with compiler optimization when using such kind of code, however I have witnessed issues with static analysis tools complaining if we're moving away from pre-defined standards.
This is nitpicking in my humble opinion, the code as such right now wouldn't create any issues.

@utam-1
utam-1 force-pushed the exec-preprocessor-beyond-baseline branch 2 times, most recently from efacb58 to 8a9ae31 Compare August 12, 2026 04:06
@utam-1

utam-1 commented Aug 12, 2026

Copy link
Copy Markdown
Author

have you already run it sucessfully with the gixsql examples running gixsql?

If yes, please add the sample configuration for this (and possibly others like esqloc and esql) to the config folder.

If not: is there anything missing but the testing?

@GitMensch I have performed a crude test with GixSQL, but I believe since it wasn't rigorous enough I didn't added the config file. I am describing what I have found below:
The config file I am using looks something like this :

subsystem:  SQL
command:    /tmp/gixpp-wrapper.sh
on-error:   error

I have used the following command to invoke cobc --preparser :

cobc --preparser /tmp/sql-gixsql.conf \
     --save-temps -x \
     -I /home/user/gixsql-1.0.20b/examples \
     -I /home/user/gixsql-1.0.20b/copy \
     /home/user/gixsql-1.0.20b/examples/TSQL001A.cbl \
     -o /tmp/TSQL001A

And from this the output I got was :

GIXSQL*    EXEC SQL
GIXSQL*       CONNECT TO :DATASRC USER :DBUSR USING :DBPWD
GIXSQL*    END-EXEC.      
GIXSQL     CALL "GIXSQLConnect" USING
GIXSQL         BY REFERENCE SQLCA
GIXSQL         BY REFERENCE DATASRC
GIXSQL         BY VALUE 64
GIXSQL         BY REFERENCE x"00"
GIXSQL         BY VALUE 0
GIXSQL         BY REFERENCE x"00"
GIXSQL         BY VALUE 0
GIXSQL         BY REFERENCE DBUSR
GIXSQL         BY VALUE 64
GIXSQL         BY REFERENCE DBPWD
GIXSQL         BY VALUE 64
GIXSQL     END-CALL.

I think the translation works, however, if possible, I think it would be better if you would perform more rigorous testing from your end.

Apart from the testing, I don't recall there is anything else, as we have implemented almost everything based on the plan we had outlined.

@GitMensch GitMensch left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please attach your LLM findings about nested preprocessor issues to this PR

Have you tried it locally (for example with a pseudo EXEC CICS which just replaces CICS by SQL and your GixSQL test configuration)?

Comment thread cobc/cobc.c
Comment on lines +3672 to +3673
/* These options were all processed in the first getopt-run */
break;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/* These options were all processed in the first getopt-run */
break;

Comment thread cobc/cobc.h
Comment thread cobc/config.c
Comment on lines +510 to +515
/* strip trailing CR/LF for error message */
e = buff + strlen (buff) - 1;
while (e >= buff && (*e == '\r' || *e == '\n')) {
e--;
}
*(e + 1) = 0;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting side question: what happens if the file has no trailing CR/LF and we get here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That wouldn't be in an issue I think, the loop would exit and the null terminator would be overwritten. The string wouldn't change.

Comment thread cobc/config.c Outdated
line++;
split_ret = cb_conf_split_line (buff, &tag_name, &tag_val);
if (split_ret == 1) {
continue;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
continue;
continue; /* blank or comment line */

Comment thread cobc/config.c
Comment on lines +1034 to +1037
configuration_error (name, line, 1,
_("invalid configuration tag '%s'"), buff);
status = 1;
continue;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

according to the coverage check, this one misses a test case

Comment on lines +1113 to +1129
AT_DATA([unknown.conf], [
subsystem: SQL
command: /bin/true
badkey: something
])

AT_DATA([prog.cob], [
IDENTIFICATION DIVISION.
PROGRAM-ID. prog.
PROCEDURE DIVISION.
STOP RUN.
])

AT_CHECK([$COBC --preparser ./unknown.conf prog.cob], [1], [],
[configuration error:
./unknown.conf:4: unknown configuration tag 'badkey'
])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
AT_DATA([unknown.conf], [
subsystem: SQL
command: /bin/true
badkey: something
])
AT_DATA([prog.cob], [
IDENTIFICATION DIVISION.
PROGRAM-ID. prog.
PROCEDURE DIVISION.
STOP RUN.
])
AT_CHECK([$COBC --preparser ./unknown.conf prog.cob], [1], [],
[configuration error:
./unknown.conf:4: unknown configuration tag 'badkey'
])
AT_DATA([unknown.conf], [
subsystem: SQL
command: /bin/true # value does not matter, we check for the key below
# (and here for inline parsing of comments)
badkey: something
])
AT_DATA([prog.cob], [
IDENTIFICATION DIVISION.
PROGRAM-ID. prog.
PROCEDURE DIVISION.
STOP RUN.
])
AT_CHECK([$COBC --preparser ./unknown.conf prog.cob], [1], [],
[configuration error:
./unknown.conf:6: unknown configuration tag 'badkey'
])

Comment thread cobc/cobc.c Outdated
Comment on lines +5486 to +5491
const size_t cmd_len = strlen (cb_active_preparser->command) + strlen (fn->source) + strlen (fn->preprocess) + 3;
char *cmd = cobc_malloc (cmd_len);
snprintf (cmd, cmd_len, "%s %s %s", cb_active_preparser->command, fn->source, fn->preprocess);

ret_sys = call_system (cmd);
cobc_free (cmd);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do wonder if/how we should pass include paths explicit given to cobc, maybe just as a third long argument that contains -I path1 -I /path/two?
That still allows to do "something else" in the preparser config if -I is not supported by the preparser.

Comment thread cobc/scanner.l Outdated
#define YYSTYPE cb_tree
#define _PARSER_H /* work around bad Windows SDK header */
#include "parser.h"
#include <stdio.h>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include <stdio.h>

We don't need that as there is no new code, do we?

Comment thread cobc/pplex.l Outdated
#include "cobc.h"
#include "tree.h"
#include "ppparse.h"
#include <stdio.h>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do we need this for?

Comment thread doc/gnucobol.texi Outdated
Comment on lines +2016 to +2019
Note that @code{INCLUDE} statements (e.g., @code{EXEC SQL INCLUDE ...}) are
always handled by the internal preprocessor as a standard @code{COPY} statement,
with full support for library paths (@option{-I}) and case folding, before any
external preparser is considered.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that does only apply if no preparser is found:

If a preparser is configured correctly then we would get EXEC (EXEC_STATE) WORD + SUBSYSTEM_FOUND = YYACCEPT + restart --> then the configured EXEC preparser would already see and replace INCLUDE, so COBOL would never see that in the second process, no?

@utam-1
utam-1 force-pushed the exec-preprocessor-beyond-baseline branch from 9780605 to fa073ec Compare August 19, 2026 13:49
@utam-1

utam-1 commented Aug 19, 2026

Copy link
Copy Markdown
Author

please attach your LLM findings about nested preprocessor issues to this PR

Have you tried it locally (for example with a pseudo EXEC CICS which just replaces CICS by SQL and your GixSQL test configuration)?

@GitMensch I have added a folder called test_preparser which details the findings. I have tested this locally. The issue was with name collision in file_replace_extension which seems to have been resolved.

@utam-1
utam-1 force-pushed the exec-preprocessor-beyond-baseline branch from f0b8c93 to 0058c20 Compare August 20, 2026 01:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants