Skip to content

Commit 4a73807

Browse files
Martin Belangerigaw
authored andcommitted
code-generation: Put #include and forward declaration at the top
Before this fix, #includes and forward declarations were inserted in the generated outputs on a first-come-first-serve basis. In other words, #include statements and forward struct declarations would get scattered thoughout the output instead of appearing at the top of the files as they should. This patch ensures that all #includes and forward declarations appear at the top of the file where it is customary to find them. Signed-off-by: Martin Belanger <martin.belanger@dell.com>
1 parent bb51f39 commit 4a73807

File tree

1 file changed

+146
-60
lines changed

1 file changed

+146
-60
lines changed

tools/generate-accessors.c

Lines changed: 146 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,53 @@ static void mask_c_comments(char *text)
180180
*/
181181
static char *to_uppercase(char *s)
182182
{
183+
if (!s)
184+
return s;
183185
for (int i = 0; s[i] != '\0'; i++)
184186
s[i] = toupper((unsigned char)s[i]);
185187
return s;
186188
}
187189

190+
/**
191+
* @brief Sanitize a string to form a valid C identifier.
192+
*
193+
* This function modifies the given string in place so that all characters
194+
* conform to the rules of a valid C variable name (identifier):
195+
* - The first character must be a letter (A–Z, a–z) or underscore ('_').
196+
* - Subsequent characters may be letters, digits, or underscores.
197+
*
198+
* Any character that violates these rules is replaced with an underscore ('_').
199+
* The string is always modified in place; no new memory is allocated.
200+
*
201+
* @param s Pointer to the NUL-terminated string to sanitize.
202+
* If @p s is NULL or points to an empty string, the function does nothing.
203+
*
204+
* @note This function does not check for C keywords or identifier length limits.
205+
*
206+
* @code
207+
* char name[] = "123bad-name!";
208+
* sanitize_identifier(name);
209+
* // Result: "_23bad_name_"
210+
* @endcode
211+
*/
212+
static const char *sanitize_identifier(char *s)
213+
{
214+
if (s == NULL || *s == '\0')
215+
return s;
216+
217+
// The first character must be a letter or underscore
218+
if (!isalpha((unsigned char)s[0]) && s[0] != '_')
219+
s[0] = '_';
220+
221+
// Remaining characters: letters, digits, underscores allowed
222+
for (char *p = s + 1; *p; ++p) {
223+
if (!isalnum((unsigned char)*p) && *p != '_')
224+
*p = '_';
225+
}
226+
227+
return s;
228+
}
229+
188230
/**
189231
* @brief Duplicate a C string safely.
190232
*
@@ -207,13 +249,13 @@ static char *safe_strdup(const char *s)
207249
if (!s)
208250
return NULL;
209251

210-
size_t len = strlen(s) + 1; /* length including null-terminator */
252+
size_t len = strlen(s) + 1; /* length including NUL-terminator */
211253
char *new_string = (char *)malloc(len);
212254

213255
if (!new_string)
214256
return NULL; /* Return NULL on allocation failure */
215257

216-
memcpy(new_string, s, len); /* Copy the string including null-terminator */
258+
memcpy(new_string, s, len); /* Copy the string including NUL-terminator */
217259

218260
return new_string;
219261
}
@@ -234,8 +276,9 @@ static char *safe_strdup(const char *s)
234276
* @param s: Source string to duplicate. If NULL, returns NULL.
235277
* @param size: Maximum number of characters to consider from @s.
236278
*
237-
* @return Newly allocated null-terminated copy, or NULL on allocation
238-
* failure or if @s is NULL. Caller must free().
279+
* @return Newly allocated NUL-terminated copy, or NULL on
280+
* allocation failure or if @s is NULL. Caller must
281+
* free().
239282
*/
240283
static char *safe_strndup(const char *s, size_t size)
241284
{
@@ -283,7 +326,7 @@ static bool str_is_all_numbers(const char *s)
283326
* Finds the last '/' in @path and returns pointer to the next
284327
* character; if no '/' is found returns the original @path.
285328
*
286-
* @param path: Input path string (must be null-terminated).
329+
* @param path: Input path string (must be NUL-terminated).
287330
*
288331
* @return Pointer to filename portion (not newly allocated).
289332
*/
@@ -533,9 +576,8 @@ typedef struct StringList {
533576
* empty after initialization.
534577
*
535578
* @param list: Pointer to the list to initialize.
536-
* @param initial_capacity: Desired initial capacity (will be
537-
* rounded up to at least
538-
* STRINGLIST_INITIAL_CAPACITY).
579+
* @param initial_capacity: Desired initial capacity (will be rounded
580+
* up to at least STRINGLIST_INITIAL_CAPACITY).
539581
*/
540582
#define STRINGLIST_INITIAL_CAPACITY 8
541583
static void strlst_init(StringList_t *list, size_t initial_capacity)
@@ -1275,9 +1317,6 @@ static void generate_src(FILE *generated_src, StructInfo_t *si, Conf_t *conf)
12751317
member->name, member->name, member->name);
12761318
} else if (member->is_char_array) {
12771319
/* fixed-size array */
1278-
int dont_care;
1279-
(void)dont_care;
1280-
12811320
if (str_is_all_numbers(member->array_size)) {
12821321
unsigned long sz = strtoul(member->array_size, NULL, 10);
12831322

@@ -1360,10 +1399,8 @@ static void print_usage(const char *prog)
13601399
*/
13611400
static void args_init(Args_t *args, int argc, char *argv[])
13621401
{
1363-
int opt;
1364-
int option_index = 0;
1365-
int dont_care;
1366-
(void)dont_care;
1402+
int opt;
1403+
int option_index = 0;
13671404

13681405
args->verbose = false;
13691406
args->c_fname = OUTPUT_FNAME_DEFAULT_C;
@@ -1406,9 +1443,11 @@ static void args_init(Args_t *args, int argc, char *argv[])
14061443
args->verbose = true;
14071444
break;
14081445
case 'H':
1409-
print_usage(argv[0]); exit(EXIT_SUCCESS);
1446+
print_usage(argv[0]);
1447+
exit(EXIT_SUCCESS);
14101448
default:
1411-
print_usage(argv[0]); exit(EXIT_FAILURE);
1449+
print_usage(argv[0]);
1450+
exit(EXIT_FAILURE);
14121451
}
14131452
}
14141453

@@ -1513,44 +1552,31 @@ static void conf_free(Conf_t *conf)
15131552
int main(int argc, char *argv[])
15141553
{
15151554
StructList_t sl;
1555+
StringList_t files_to_include;
1556+
StringList_t forward_declares;
1557+
const char *struct_to_declare;
1558+
const char *include_fname;
15161559
const char *in_hdr;
1517-
char *h_fname_upper;
1560+
char *guard;
15181561
FILE *generated_hdr = NULL;
15191562
FILE *generated_src = NULL;
1563+
FILE *tmp_hdr_code = NULL;
1564+
FILE *tmp_src_code = NULL;
15201565
Conf_t conf;
1566+
int dont_care;
1567+
int c;
1568+
1569+
(void)dont_care;
15211570

15221571
conf_init(&conf, argc, argv);
15231572

15241573
struct_list_init(&sl);
1574+
strlst_init(&files_to_include, 0);
1575+
strlst_init(&forward_declares, 0);
15251576

1526-
/* Create directories (if needed) */
1527-
mkdir_fullpath(conf.args.h_fname, 0755);
1528-
mkdir_fullpath(conf.args.c_fname, 0755);
1529-
1530-
generated_hdr = fopen(conf.args.h_fname, "w");
1531-
generated_src = fopen(conf.args.c_fname, "w");
1532-
1533-
h_fname_upper = to_uppercase(safe_strdup(get_filename(conf.args.h_fname)));
1534-
strchrnul(h_fname_upper, '.')[0] = '\0';
1535-
1536-
fprintf(generated_hdr,
1537-
"%s\n"
1538-
"#ifndef %s_H\n"
1539-
"#define %s_H\n"
1540-
"\n"
1541-
"#include <stdlib.h>\n"
1542-
"#include <string.h>\n"
1543-
"#include <stdbool.h>\n"
1544-
"#include <stdint.h>\n"
1545-
"#include <linux/types.h> /* __u32, __u64, etc. */\n"
1546-
"\n", banner, h_fname_upper, h_fname_upper);
1547-
fprintf(generated_src,
1548-
"%s\n"
1549-
"#include <stdlib.h>\n"
1550-
"#include <string.h>\n"
1551-
"#include \"%s\"\n"
1552-
"\n", banner, get_filename(conf.args.h_fname));
1553-
1577+
/* Creates temporary files to hold the generated code. */
1578+
tmp_hdr_code = tmpfile();
1579+
tmp_src_code = tmpfile();
15541580

15551581
STRLST_FOREACH(&conf.args.hdr_files, in_hdr) {
15561582
StructInfo_t *si;
@@ -1576,41 +1602,101 @@ int main(int argc, char *argv[])
15761602
continue;
15771603
}
15781604

1579-
fprintf(generated_src, "#include \"%s\"\n", in_hdr_fname);
1605+
strlst_add(&files_to_include, in_hdr_fname, false);
15801606

1581-
fprintf(generated_hdr, "/* Forward declarations. These are internal (opaque) structs. */\n");
15821607
STRUCT_LIST_FOREACH(&sl, si) {
1583-
/* Add forward declaration of struct. We assume all
1584-
* structs are opaque, therefore only forward declaration allowed.
1585-
*/
1586-
fprintf(generated_hdr, "struct %s;\n", si->name);
1587-
}
1608+
if (STRUCT_INFO_EMPTY(si))
1609+
continue;
1610+
1611+
strlst_add(&forward_declares, si->name, false);
15881612

1589-
STRUCT_LIST_FOREACH(&sl, si) {
15901613
/* Generate code for the header file (*.h) */
1591-
fprintf(generated_hdr,
1614+
fprintf(tmp_hdr_code,
15921615
"\n"
15931616
"/****************************************************************************\n"
15941617
" * Accessors for: struct %s\n"
15951618
" */\n", si->name);
1596-
generate_hdr(generated_hdr, si, &conf);
1619+
generate_hdr(tmp_hdr_code, si, &conf);
15971620

15981621
/* Generate code for the source file (*.c) */
1599-
fprintf(generated_src,
1622+
fprintf(tmp_src_code,
16001623
"\n"
16011624
"/****************************************************************************\n"
16021625
" * Accessors for: struct %s\n"
16031626
" */\n", si->name);
1604-
generate_src(generated_src, si, &conf);
1627+
generate_src(tmp_src_code, si, &conf);
16051628
}
16061629
}
16071630

16081631
struct_list_free(&sl);
16091632

1610-
fprintf(generated_hdr, "#endif /* %s_H */\n", h_fname_upper);
1611-
free(h_fname_upper);
1633+
/* We've collected all the data we needed. Now let's generate some files. */
1634+
1635+
/***********************************************************************
1636+
* First, output the generated header file.
1637+
*/
1638+
1639+
/* Add a guard in the generated header file that is made of
1640+
* the UPPERCASE file name's stem. In other words, if the file
1641+
* name is "accessors.h" then the guard should be "_ACCESSORS_H_"
1642+
*/
1643+
dont_care = asprintf(&guard, "_%s_", get_filename(conf.args.h_fname));
1644+
sanitize_identifier(to_uppercase(guard));
1645+
1646+
mkdir_fullpath(conf.args.h_fname, 0755); /* create output file's directory if needed */
1647+
1648+
generated_hdr = fopen(conf.args.h_fname, "w");
1649+
fprintf(generated_hdr,
1650+
"%s\n"
1651+
"#ifndef %s\n"
1652+
"#define %s\n"
1653+
"\n"
1654+
"#include <stdlib.h>\n"
1655+
"#include <string.h>\n"
1656+
"#include <stdbool.h>\n"
1657+
"#include <stdint.h>\n"
1658+
"#include <linux/types.h> /* __u32, __u64, etc. */\n"
1659+
"\n", banner, guard, guard);
1660+
1661+
fprintf(generated_hdr, "/* Forward declarations. These are internal (opaque) structs. */\n");
1662+
STRLST_FOREACH(&forward_declares, struct_to_declare)
1663+
fprintf(generated_hdr, "struct %s;\n", struct_to_declare);
1664+
strlst_free(&forward_declares);
1665+
1666+
/* Copy temporary file to output */
1667+
rewind(tmp_hdr_code);
1668+
while ((c = fgetc(tmp_hdr_code)) != EOF)
1669+
fputc(c, generated_hdr);
1670+
fclose(tmp_hdr_code);
16121671

1672+
fprintf(generated_hdr, "#endif /* %s */\n", guard);
16131673
fclose(generated_hdr);
1674+
free(guard);
1675+
1676+
1677+
/***********************************************************************
1678+
* Second, output the generated source file.
1679+
*/
1680+
1681+
mkdir_fullpath(conf.args.c_fname, 0755); /* create output file's directory if needed */
1682+
generated_src = fopen(conf.args.c_fname, "w");
1683+
fprintf(generated_src,
1684+
"%s\n"
1685+
"#include <stdlib.h>\n"
1686+
"#include <string.h>\n"
1687+
"#include \"%s\"\n"
1688+
"\n", banner, get_filename(conf.args.h_fname));
1689+
1690+
STRLST_FOREACH(&files_to_include, include_fname)
1691+
fprintf(generated_src, "#include \"%s\"\n", include_fname);
1692+
strlst_free(&files_to_include);
1693+
1694+
/* Copy temporary file to output */
1695+
rewind(tmp_src_code);
1696+
while ((c = fgetc(tmp_src_code)) != EOF)
1697+
fputc(c, generated_src);
1698+
fclose(tmp_src_code);
1699+
16141700
fclose(generated_src);
16151701

16161702
if (conf.args.verbose)

0 commit comments

Comments
 (0)