@@ -180,11 +180,53 @@ static void mask_c_comments(char *text)
180180 */
181181static 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 */
240283static 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
541583static 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 */
13611400static 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)
15131552int 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 , 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 , 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