/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "strutil.h"
#include "stringlists.h"
#define ObjExtension ".o"
static StringRecPtr inc_path_list = NULL,
def_list = NULL;
static char *getobj(const char *pSrc)
{
static char buffer[255];
int l
= strlen(pSrc
), bm5
= sizeof(buffer
) - 5;
char *pSearch;
if (l < bm5)
l = bm5;
memcpy(buffer
, pSrc
, l
); buffer
[l
] = '\0';
if (pSearch)
strcpy(pSearch
, ObjExtension
);
return buffer;
}
int MayRecurse(const char *pFileName)
{
/* .rsc files are autogenerated and do not contain any
include statements - no need to scan them */
if ((l
> 4) && (!strcmp(pFileName
+ l
- 4, ".rsc")))
return 0;
return 1;
}
static Boolean regard_sym(const char *p_name)
{
return !strcmp(p_name
, "_USE_MSH");
}
static Boolean def_present(const char *p_name)
{
StringRec *p_run = def_list;
for (p_run = def_list; p_run; p_run = p_run->Next)
{
if (!strcmp(p_name
, p_run
->Content
))
return True;
}
return False;
}
static Boolean curr_if = True;
typedef struct if_stack
{
struct if_stack *p_next;
Boolean condition, save_curr_if;
Boolean evaluated, else_occured;
} if_stack_t;
static if_stack_t *p_if_stack_tos = NULL;
static void push_if(Boolean condition, Boolean evaluated)
{
if_stack_t
*p_new_if
= (if_stack_t
*)calloc(1, sizeof(*p_new_if
));
p_new_if->save_curr_if = curr_if;
p_new_if->condition = condition;
p_new_if->evaluated = evaluated;
p_new_if->else_occured = False;
p_new_if->p_next = p_if_stack_tos;
p_if_stack_tos = p_new_if;
curr_if = evaluated ? curr_if && condition : curr_if;
}
static void toggle_if(const char *p_file, int line)
{
if (!p_if_stack_tos)
{
fprintf(stderr
, "%s:%d: #else without #ifdef\n", p_file
, line
);
}
else if (p_if_stack_tos->else_occured)
{
fprintf(stderr
, "%s:%d: #ifdef with more than one #else\n", p_file
, line
);
}
if (p_if_stack_tos->evaluated)
curr_if = p_if_stack_tos->save_curr_if && !p_if_stack_tos->condition;
p_if_stack_tos->else_occured = True;
}
static void pop_if(const char *p_file, int line)
{
if_stack_t *p_old_if;
if (!p_if_stack_tos)
{
fprintf(stderr
, "%s:%d: #endif without #ifdef\n", p_file
, line
);
}
curr_if = p_if_stack_tos->save_curr_if;
p_old_if = p_if_stack_tos;
p_if_stack_tos = p_old_if->p_next;
}
static void ParseFile(const char *pFileName, const char *pParentFileName, StringRecPtr *pFileList)
{
FILE *pFile;
int l, file_stat, line_num = 0;
char line[512], *pCmd, *pName, *pValue;
String raw_name, eff_name;
struct stat stat_buf;
pFile
= fopen(pFileName
, "r");
if (!pFile)
{
if (pParentFileName)
fprintf(stderr
, "%s: ", pParentFileName
);
return;
}
{
if (!fgets(line
, sizeof(line
), pFile
))
break;
line_num++;
if ((l > 0) && (line[l - 1] == '\n'))
line[l - 1] = '\0';
if (*line != '#')
continue;
pCmd
= strtok(line
+ 1, " \t");
if (!strcmp(pCmd
, "include") && curr_if
)
{
if (!pName)
continue;
if ((*pName != '"') || (pName[l - 1] != '"'))
continue;
if (l - 1 < (int)sizeof(raw_name))
{
memcpy(raw_name
, pName
+ 1, l
- 2);
raw_name[l - 2] = '\0';
strmaxcpy(eff_name, raw_name, sizeof(eff_name));
file_stat = stat(eff_name, &stat_buf);
if (file_stat)
{
StringRecPtr p_run;
const char *p_path;
for (p_path = GetStringListFirst(inc_path_list, &p_run);
p_path; p_path = GetStringListNext(&p_run))
{
Boolean term_path = (l > 0) && ((p_path[l -1] == '/') || (p_path[l -1] == '\\'));
as_snprintf(eff_name, sizeof(eff_name), "%s%s%s", p_path, term_path ? "" : "/", raw_name);
file_stat = stat(eff_name, &stat_buf);
if (!file_stat)
break;
}
}
/* If file is not present, it is created dynamically and expected to appear in the
object subdirectory, which is passed in as -I path. Keep last generated eff_name
for the dependency list: */
if (!StringListPresent(*pFileList, eff_name))
{
AddStringListLast(pFileList, eff_name);
if (MayRecurse(eff_name))
ParseFile(eff_name, pFileName, pFileList);
}
}
}
{
push_if(True, False);
}
else if (!strcmp(pCmd
, "ifdef"))
{
if (!curr_if || !regard_sym(pName))
push_if(True, False);
else
push_if(def_present(pName), True);
}
else if (!strcmp(pCmd
, "ifndef"))
{
if (!curr_if || !regard_sym(pName))
push_if(True, False);
else
push_if(!def_present(pName), True);
}
else if (!strcmp(pCmd
, "else"))
{
if (curr_if)
toggle_if(pFileName, line_num);
}
else if (!strcmp(pCmd
, "endif"))
{
pop_if(pFileName, line_num);
}
else if (!strcmp(pCmd
, "elif"))
{
/* TODO */
}
else if (!strcmp(pCmd
, "define"))
{
if (strchr(pName
, '(') || (pValue
&& *pValue
))
continue;
}
else if (!strcmp(pCmd
, "undef"))
{
}
}
}
int main(int argc, char **argv)
{
int z, ArgsAreObj = 0;
FILE *pDepFile;
const char *pDepFileName = NULL,
*pOutFileName = NULL,
*pObjExtension = NULL;
char used[1024];
StringRecPtr FileList, SrcList;
if (argc < 2)
{
fprintf(stderr
, "usage: %s [args] <file(s)>\n", *argv
);
}
memset(used
, 0, sizeof(used
));
for (z = 1; z < argc; z++)
if ((!used[z]) && (*argv[z] == '-'))
{
used[z] = 1;
if (!strcmp(argv
[z
] + 1, "d"))
{
if (z >= argc - 1)
pDepFileName = NULL;
else
{
pDepFileName = argv[z + 1];
used[z + 1] = 1;
}
}
else if (!strcmp(argv
[z
] + 1, "o"))
{
if (z >= argc - 1)
pOutFileName = NULL;
else
{
pOutFileName = argv[z + 1];
used[z + 1] = 1;
}
}
else if (!strcmp(argv
[z
] + 1, "c"))
{
if (z >= argc - 1)
pObjExtension = NULL;
else
{
pObjExtension = argv[z + 1];
used[z + 1] = 1;
}
}
else if (!strcmp(argv
[z
] + 1, "r"))
{
ArgsAreObj = 1;
}
else if (argv[z][1] == 'I')
{
if (!argv[z][2])
{
if (z < argc - 1)
{
if (argv[z + 1][0] == '-')
fprintf(stderr
, "%s: taking '%s' as include path, is this correct?\n", argv
[0], argv
[z
+ 1]);
AddStringListLast(&inc_path_list, argv[z + 1]);
used[z + 1] = 1;
}
}
else
AddStringListLast(&inc_path_list, argv[z] + 2);
}
else if (argv[z][1] == 'D')
{
if (!argv[z][2])
{
if (z < argc - 1)
{
if (argv[z + 1][0] == '-')
fprintf(stderr
, "%s: taking '%s' as macro, is this correct?\n", argv
[0], argv
[z
+ 1]);
AddStringListLast(&def_list, argv[z + 1]);
used[z + 1] = 1;
}
}
else
AddStringListLast(&def_list, argv[z] + 2);
}
else
{
fprintf(stderr
, "unknown option: %s\n", argv
[z
]);
}
}
if (pDepFileName)
{
pDepFile
= fopen(pDepFileName
, "w");
if (!pDepFile)
{
}
}
else
pDepFile = stdout;
fprintf(pDepFile
, "# auto-generated by %s - do not edit\n\n", *argv
);
InitStringList(&FileList);
InitStringList(&SrcList);
for (z = 1; z < argc; z++)
{
char SrcFileName[512];
const char *pSrcFileName;
if (used[z])
continue;
/* object files may be in paths different than root - strip path to deduce associated src file */
if (ArgsAreObj)
{
char *pRun;
for (pRun
= argv
[z
] + strlen(argv
[z
]); pRun
> argv
[z
]; pRun
--)
if ((*(pRun - 1) == '/') || (*(pRun - 1) == '\\'))
break;
/* leading h_ in object file name is used for host-side files */
pRun += 2;
*SrcFileName
= '\0'; strncat(SrcFileName
, pRun
, sizeof(SrcFileName
) - 1);
if (pObjExtension)
{
if ((l1
> l2
) && !memcmp(SrcFileName
+ l1
- l2
, pObjExtension
, l2
))
strcpy(SrcFileName
+ l1
- l2
, ".c");
}
pSrcFileName = SrcFileName;
}
else
pSrcFileName = argv[z];
ClearStringList(&FileList);
AddStringListLast(&SrcList, pSrcFileName);
ParseFile(pSrcFileName, NULL, &FileList);
if (!StringListEmpty(FileList))
{
pOutFileName ? pOutFileName : (ArgsAreObj ? argv[z] : getobj(argv[z])),
pSrcFileName);
while (True)
{
char *pIncFileName = MoveAndCutStringListFirst(&FileList);
if (pIncFileName)
{
if (*pIncFileName)
fprintf(pDepFile
, " %s", pIncFileName
);
free(pIncFileName
); pIncFileName
= NULL
;
}
else
break;
}
}
}
if (pDepFileName)
{
if (!StringListEmpty(SrcList))
{
fprintf(pDepFile
, "%s:", pDepFileName
);
while (True)
{
char *pIncFileName = MoveAndCutStringListFirst(&SrcList);
if (pIncFileName)
{
if (*pIncFileName)
fprintf(pDepFile
, " %s", pIncFileName
);
free(pIncFileName
); pIncFileName
= NULL
;
}
else
break;
}
}
}
ClearStringList(&inc_path_list);
return 0;
}