dtc/srcpos.c
Colin Finck 1c6c51e51b Consider drive letters when checking for absolute paths on Windows.
This still requires you to specify paths with forward slashes instead of
backslashes on Windows, due to many hardcoded checks for '/'.
Fortunately, the Windows user APIs all support forward slashes too.

Signed-off-by: Colin Finck <mail@colinfinck.de>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2025-08-07 21:42:42 +10:00

441 lines
9.3 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2007 Jon Loeliger, Freescale Semiconductor, Inc.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include "dtc.h"
#include "srcpos.h"
/* A node in our list of directories to search for source/include files */
struct search_path {
struct search_path *next; /* next node in list, NULL for end */
const char *dirname; /* name of directory to search */
};
/* This is the list of directories that we search for source files */
static struct search_path *search_path_head, **search_path_tail;
/* Detect infinite include recursion. */
#define MAX_SRCFILE_DEPTH (200)
static int srcfile_depth; /* = 0 */
static char *get_dirname(const char *path)
{
const char *slash = strrchr(path, '/');
if (slash) {
int len = slash - path;
char *dir = xmalloc(len + 1);
memcpy(dir, path, len);
dir[len] = '\0';
return dir;
}
return NULL;
}
FILE *depfile; /* = NULL */
struct srcfile_state *current_srcfile; /* = NULL */
static char *initial_path; /* = NULL */
static int initial_pathlen; /* = 0 */
static bool initial_cpp = true;
static void set_initial_path(char *fname)
{
int i, len = strlen(fname);
xasprintf(&initial_path, "%s", fname);
initial_pathlen = 0;
for (i = 0; i != len; i++)
if (initial_path[i] == '/')
initial_pathlen++;
}
static char *shorten_to_initial_path(char *fname)
{
char *p1, *p2, *prevslash1 = NULL;
int slashes = 0;
for (p1 = fname, p2 = initial_path; *p1 && *p2; p1++, p2++) {
if (*p1 != *p2)
break;
if (*p1 == '/') {
prevslash1 = p1;
slashes++;
}
}
p1 = prevslash1 + 1;
if (prevslash1) {
int diff = initial_pathlen - slashes, i, j;
int restlen = strlen(fname) - (p1 - fname);
char *res;
res = xmalloc((3 * diff) + restlen + 1);
for (i = 0, j = 0; i != diff; i++) {
res[j++] = '.';
res[j++] = '.';
res[j++] = '/';
}
strcpy(res + j, p1);
return res;
}
return NULL;
}
/**
* Returns true if the given path is an absolute one.
*
* On Windows, it either needs to begin with a forward slash or with a drive
* letter (e.g. "C:").
* On all other operating systems, it must begin with a forward slash to be
* considered an absolute path.
*/
static bool is_absolute_path(const char *path)
{
#ifdef WIN32
return (
path[0] == '/' ||
(((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z')) && path[1] == ':')
);
#else
return (path[0] == '/');
#endif
}
/**
* Try to open a file in a given directory.
*
* If the filename is an absolute path, then dirname is ignored. If it is a
* relative path, then we look in that directory for the file.
*
* @param dirname Directory to look in, or NULL for none
* @param fname Filename to look for
* @param fp Set to NULL if file did not open
* @return allocated filename on success (caller must free), NULL on failure
*/
static char *try_open(const char *dirname, const char *fname, FILE **fp)
{
char *fullname;
if (!dirname || is_absolute_path(fname))
fullname = xstrdup(fname);
else
fullname = join_path(dirname, fname);
*fp = fopen(fullname, "rb");
if (!*fp) {
free(fullname);
fullname = NULL;
}
return fullname;
}
/**
* Open a file for read access
*
* If it is a relative filename, we search the full search path for it.
*
* @param fname Filename to open
* @param fp Returns pointer to opened FILE, or NULL on failure
* @return pointer to allocated filename, which caller must free
*/
static char *fopen_any_on_path(const char *fname, FILE **fp)
{
const char *cur_dir = NULL;
struct search_path *node;
char *fullname;
/* Try current directory first */
assert(fp);
if (current_srcfile)
cur_dir = current_srcfile->dir;
fullname = try_open(cur_dir, fname, fp);
/* Failing that, try each search path in turn */
for (node = search_path_head; !*fp && node; node = node->next)
fullname = try_open(node->dirname, fname, fp);
return fullname;
}
FILE *srcfile_relative_open(const char *fname, char **fullnamep)
{
FILE *f;
char *fullname;
if (streq(fname, "-")) {
f = stdin;
fullname = xstrdup("<stdin>");
} else {
fullname = fopen_any_on_path(fname, &f);
if (!f)
die("Couldn't open \"%s\": %s\n", fname,
strerror(errno));
}
if (depfile) {
fputc(' ', depfile);
fprint_path_escaped(depfile, fullname);
}
if (fullnamep)
*fullnamep = fullname;
else
free(fullname);
return f;
}
void srcfile_push(const char *fname)
{
struct srcfile_state *srcfile;
if (srcfile_depth++ >= MAX_SRCFILE_DEPTH)
die("Includes nested too deeply");
srcfile = xmalloc(sizeof(*srcfile));
srcfile->f = srcfile_relative_open(fname, &srcfile->name);
srcfile->dir = get_dirname(srcfile->name);
srcfile->prev = current_srcfile;
srcfile->lineno = 1;
srcfile->colno = 1;
current_srcfile = srcfile;
if (srcfile_depth == 1)
set_initial_path(srcfile->name);
}
bool srcfile_pop(void)
{
struct srcfile_state *srcfile = current_srcfile;
assert(srcfile);
current_srcfile = srcfile->prev;
if (fclose(srcfile->f))
die("Error closing \"%s\": %s\n", srcfile->name,
strerror(errno));
/* FIXME: We allow the srcfile_state structure to leak,
* because it could still be referenced from a location
* variable being carried through the parser somewhere. To
* fix this we could either allocate all the files from a
* table, or use a pool allocator. */
return current_srcfile ? true : false;
}
void srcfile_add_search_path(const char *dirname)
{
struct search_path *node;
/* Create the node */
node = xmalloc(sizeof(*node));
node->next = NULL;
node->dirname = xstrdup(dirname);
/* Add to the end of our list */
if (search_path_tail)
*search_path_tail = node;
else
search_path_head = node;
search_path_tail = &node->next;
}
void srcpos_update(struct srcpos *pos, const char *text, int len)
{
int i;
pos->file = current_srcfile;
pos->first_line = current_srcfile->lineno;
pos->first_column = current_srcfile->colno;
for (i = 0; i < len; i++)
if (text[i] == '\n') {
current_srcfile->lineno++;
current_srcfile->colno = 1;
} else {
current_srcfile->colno++;
}
pos->last_line = current_srcfile->lineno;
pos->last_column = current_srcfile->colno;
}
struct srcpos *
srcpos_copy(struct srcpos *pos)
{
struct srcpos *pos_new;
struct srcfile_state *srcfile_state;
if (!pos)
return NULL;
pos_new = xmalloc(sizeof(struct srcpos));
assert(pos->next == NULL);
memcpy(pos_new, pos, sizeof(struct srcpos));
/* allocate without free */
srcfile_state = xmalloc(sizeof(struct srcfile_state));
memcpy(srcfile_state, pos->file, sizeof(struct srcfile_state));
pos_new->file = srcfile_state;
return pos_new;
}
struct srcpos *srcpos_extend(struct srcpos *pos, struct srcpos *newtail)
{
struct srcpos *p;
if (!pos)
return newtail;
for (p = pos; p->next != NULL; p = p->next);
p->next = newtail;
return pos;
}
void srcpos_free(struct srcpos *pos)
{
struct srcpos *p_next;
while (pos) {
p_next = pos->next;
free(pos);
pos = p_next;
}
}
char *
srcpos_string(struct srcpos *pos)
{
const char *fname = "<no-file>";
char *pos_str;
if (pos->file && pos->file->name)
fname = pos->file->name;
if (pos->first_line != pos->last_line)
xasprintf(&pos_str, "%s:%d.%d-%d.%d", fname,
pos->first_line, pos->first_column,
pos->last_line, pos->last_column);
else if (pos->first_column != pos->last_column)
xasprintf(&pos_str, "%s:%d.%d-%d", fname,
pos->first_line, pos->first_column,
pos->last_column);
else
xasprintf(&pos_str, "%s:%d.%d", fname,
pos->first_line, pos->first_column);
return pos_str;
}
static char *
srcpos_string_comment(struct srcpos *pos, bool first_line, int level)
{
char *pos_str, *fresh_fname = NULL, *first, *rest;
const char *fname;
if (!pos) {
if (level > 1) {
xasprintf(&pos_str, "<no-file>:<no-line>");
return pos_str;
} else {
return NULL;
}
}
if (!pos->file)
fname = "<no-file>";
else if (!pos->file->name)
fname = "<no-filename>";
else if (level > 1)
fname = pos->file->name;
else {
fresh_fname = shorten_to_initial_path(pos->file->name);
if (fresh_fname)
fname = fresh_fname;
else
fname = pos->file->name;
}
if (level > 1)
xasprintf(&first, "%s:%d:%d-%d:%d", fname,
pos->first_line, pos->first_column,
pos->last_line, pos->last_column);
else
xasprintf(&first, "%s:%d", fname,
first_line ? pos->first_line : pos->last_line);
if (fresh_fname)
free(fresh_fname);
if (pos->next != NULL) {
rest = srcpos_string_comment(pos->next, first_line, level);
xasprintf(&pos_str, "%s, %s", first, rest);
free(first);
free(rest);
} else {
pos_str = first;
}
return pos_str;
}
char *srcpos_string_first(struct srcpos *pos, int level)
{
return srcpos_string_comment(pos, true, level);
}
char *srcpos_string_last(struct srcpos *pos, int level)
{
return srcpos_string_comment(pos, false, level);
}
void srcpos_verror(struct srcpos *pos, const char *prefix,
const char *fmt, va_list va)
{
char *srcstr;
srcstr = srcpos_string(pos);
fprintf(stderr, "%s: %s ", prefix, srcstr);
vfprintf(stderr, fmt, va);
fprintf(stderr, "\n");
free(srcstr);
}
void srcpos_error(struct srcpos *pos, const char *prefix,
const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
srcpos_verror(pos, prefix, fmt, va);
va_end(va);
}
void srcpos_set_line(char *f, int l)
{
current_srcfile->name = f;
current_srcfile->lineno = l;
if (initial_cpp) {
initial_cpp = false;
set_initial_path(f);
}
}