sdcc-gas/src/SDCCutil.c

1174 lines
26 KiB
C

/*-------------------------------------------------------------------------
SDCCutil.c - Small utility functions.
Written By - Sandeep Dutta . sandeep.dutta@usa.net (1999)
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them. Help stamp out software-hoarding!
-------------------------------------------------------------------------*/
#include <math.h>
#include <string.h>
#include <ctype.h>
#ifdef _WIN32
#include <windows.h>
#undef TRUE
#undef FALSE
#endif
#include <sys/stat.h>
#include "dbuf.h"
#include "SDCCglobl.h"
#include "SDCCmacro.h"
#include "SDCCutil.h"
#include "newalloc.h"
#include "dbuf_string.h"
#ifndef _WIN32
#include "findme.h"
#endif
#include "version.h"
/** Given an array of name, value string pairs creates a new hash
containing all of the pairs.
*/
hTab *
populateStringHash (const char **pin)
{
hTab *pret = NULL;
while (*pin)
{
shash_add (&pret, pin[0], pin[1]);
pin += 2;
}
return pret;
}
/** shell escape string
returns dynamically allocated string, which should be free-ed
*/
char *
shell_escape (const char *str)
{
#ifdef _WIN32
/* see http://www.autohotkey.net/~deleyd/parameters/parameters.htm */
struct dbuf_s dbuf;
int backshl = 0;
const char *begin = str;
char backslbuf[16];
char *backslp;
dbuf_init (&dbuf, 128);
/* opening double quotes */
dbuf_append_char(&dbuf, '"');
while (*str)
{
if (NULL == begin)
begin = str;
if ('\\' == *str)
++backshl;
else
{
if ('"' == *str)
{
/* append the remaining characters */
if (str > begin)
dbuf_append (&dbuf, begin, str - begin);
/* append additional beckslash */
++backshl;
/* special handling if last chars before double quote are backslashes */
backslp = backslbuf;
while (backshl--)
{
*backslp++ = '\\';
if (sizeof (backslbuf) == backslp - backslbuf)
{
dbuf_append (&dbuf, backslbuf, sizeof (backslbuf));
backslp = backslbuf;
}
}
if (backslp > backslbuf)
dbuf_append (&dbuf, backslbuf, backslp - backslbuf);
begin = str;
}
else if ('%' == *str)
{
/* diseble env. variable expansion */
/* append the remaining characters */
if (begin && str > begin)
dbuf_append (&dbuf, begin, str - begin);
/* special handling if last chars before double quote are backslashes */
backslp = backslbuf;
while (backshl--)
{
*backslp++ = '\\';
if (sizeof (backslbuf) == backslp - backslbuf)
{
dbuf_append (&dbuf, backslbuf, sizeof (backslbuf));
backslp = backslbuf;
}
}
if (backslp > backslbuf)
dbuf_append (&dbuf, backslbuf, backslp - backslbuf);
/* closing doube quotes */
backslbuf[0] = '"';
backslbuf[1] = *str;
/* re opening double quotes */
backslbuf[2] = '"';
dbuf_append (&dbuf, backslbuf, 3);
begin = NULL;
}
backshl = 0;
}
++str;
}
/* append the remaining characters */
if (begin && str > begin)
dbuf_append (&dbuf, begin, str - begin);
/* special handling if last chars before double quote are backslashes */
backslp = backslbuf;
while (backshl--)
{
*backslp++ = '\\';
if (sizeof (backslbuf) == backslp - backslbuf)
{
dbuf_append (&dbuf, backslbuf, sizeof (backslbuf));
backslp = backslbuf;
}
}
if (backslp > backslbuf)
dbuf_append (&dbuf, backslbuf, backslp - backslbuf);
/* closing doube quotes */
dbuf_append_char (&dbuf, '"');
return dbuf_detach_c_str (&dbuf);
#else
struct dbuf_s dbuf;
const char *s = str;
const char *begin = str;
dbuf_init (&dbuf, 128);
while (*s)
{
switch (*s)
{
case ' ': case '\t': case '\n': /* IFS white space */
case '\'': case '"': case '\\': /* quoting chars */
case '|': case '&': case ';': /* shell metacharacters */
case '(': case ')': case '<': case '>':
case '!': case '{': case '}': /* reserved words */
case '*': case '[': case '?': case ']': /* globbing chars */
case '^':
case '$': case '`': /* expansion chars */
case ',': /* brace expansion */
/* flush */
if (s > begin)
dbuf_append (&dbuf, begin, s - begin);
dbuf_append_char (&dbuf, '\\');
begin = s;
break;
case '~': /* tilde expansion */
if (s == str || s[-1] == '=' || s[-1] == ':')
{
/* flush */
if (s > begin)
dbuf_append (&dbuf, begin, s - begin);
dbuf_append_char (&dbuf, '\\');
begin = s;
}
break;
case '#': /* comment char */
{
/* flush */
if (s > begin)
dbuf_append (&dbuf, begin, s - begin);
dbuf_append_char (&dbuf, '\\');
begin = s;
}
/* FALLTHROUGH */
default:
break;
}
++s;
}
/* flush */
if (s > begin)
dbuf_append (&dbuf, begin, s - begin);
return dbuf_detach_c_str (&dbuf);
#endif
}
/** Prints elements of the set to the file, each element on new line
*/
void
fputStrSet (FILE * fp, set * list)
{
const char *s;
for (s = setFirstItem (list); s != NULL; s = setNextItem (list))
{
fputs (s, fp);
fputc ('\n', fp);
}
}
/** Prepend / append / process given strings to each item of string set.
The result is in a new string set.
*/
set *
processStrSet (set * list, const char *pre, const char *post, char *(*func)(const char *))
{
set *new_list = NULL;
const char *item;
struct dbuf_s dbuf;
for (item = setFirstItem (list); item != NULL; item = setNextItem (list))
{
dbuf_init (&dbuf, PATH_MAX);
if (pre)
dbuf_append_str (&dbuf, pre);
if (func)
{
char *s = func (item);
dbuf_append_str (&dbuf, s);
Safe_free (s);
}
else
dbuf_append_str (&dbuf, item);
if (post)
dbuf_append_str (&dbuf, post);
addSet (&new_list, dbuf_detach_c_str (&dbuf));
}
return new_list;
}
/** Given a set returns a string containing all of the strings seperated
by spaces. The returned string is on the heap.
*/
const char *
joinStrSet (set * list)
{
const char *s;
struct dbuf_s dbuf;
dbuf_init (&dbuf, PATH_MAX);
for (s = setFirstItem (list); s != NULL; s = setNextItem (list))
{
dbuf_append_str (&dbuf, s);
dbuf_append_char (&dbuf, ' ');
}
return dbuf_detach_c_str (&dbuf);
}
/** Split the path string to the directory and file name (including extension) components.
The directory component doesn't contain trailing directory separator.
Returns true if the path contains the directory separator. */
int
dbuf_splitPath (const char *path, struct dbuf_s *dir, struct dbuf_s *file)
{
const char *p;
int ret;
const char *end = &path[strlen (path)];
for (p = end - 1; p >= path && !IS_DIR_SEPARATOR (*p); --p)
;
ret = p >= path;
if (NULL != dir)
{
int len = p - path;
if (0 < len)
dbuf_append (dir, path, len);
}
if (NULL != file)
{
int len;
++p;
len = end - p;
if (0 < len)
dbuf_append (file, p, len);
}
return ret;
}
/** Split the path string to the file name (including directory) and file extension components.
File extension component contains the extension separator.
Returns true if the path contains the extension separator. */
int
dbuf_splitFile (const char *path, struct dbuf_s *file, struct dbuf_s *ext)
{
const char *p;
const char *end = &path[strlen (path)];
for (p = end - 1; p >= path && !IS_DIR_SEPARATOR (*p) && '.' != *p; --p)
;
if (p < path || '.' != *p)
{
dbuf_append_str (file, path);
return 0;
}
else
{
if (NULL != file)
{
int len = p - path;
if (0 < len)
dbuf_append (file, path, len);
}
if (NULL != ext)
{
int len = end - p;
if (0 < len)
dbuf_append (ext, p, len);
}
return 1;
}
}
/** Combine directory and the file name to a path string using the DIR_SEPARATOR_CHAR.
*/
void
dbuf_makePath (struct dbuf_s *path, const char *dir, const char *file)
{
if (dir != NULL)
dbuf_append_str (path, dir);
dbuf_append_char (path, DIR_SEPARATOR_CHAR);
if (file != NULL)
dbuf_append_str (path, file);
}
/** Given a file with path information in the binary files directory,
returns the directory component. Used for discovery of bin
directory of SDCC installation. Returns NULL if the path is
impossible.
*/
#ifdef _WIN32
const char *
getBinPath (const char *prel)
{
struct dbuf_s path;
const char *p;
dbuf_init (&path, 128);
dbuf_splitPath (prel, &path, NULL);
p = dbuf_c_str (&path);
if ('\0' != *p)
return p;
else
{
char module[PATH_MAX];
dbuf_destroy (&path);
/* not enough info in prel; do it with module name */
if (0 != GetModuleFileName (NULL, module, sizeof (module)))
{
dbuf_init (&path, 128);
dbuf_splitPath (module, &path, NULL);
return dbuf_detach_c_str (&path);
}
else
return NULL;
}
}
#else
const char *
getBinPath (const char *prel)
{
const char *ret_path;
if (NULL != (ret_path = findProgramPath (prel)))
{
struct dbuf_s path;
dbuf_init (&path, 128);
dbuf_splitPath (ret_path, &path, NULL);
free ((void *) ret_path);
return dbuf_detach_c_str (&path);
}
else
return NULL;
}
#endif
/** Returns true if the given path exists.
*/
bool
pathExists (const char *ppath)
{
struct stat s;
return stat (ppath, &s) == 0;
}
static hTab *_mainValues;
void
setMainValue (const char *pname, const char *pvalue)
{
assert (pname);
shash_add (&_mainValues, pname, pvalue);
}
char *
buildMacros (const char *cmd)
{
return eval_macros (_mainValues, cmd);
}
char *
buildCmdLine (const char **cmds, const char *p1, const char *p2, const char *p3, set * list)
{
int first = 1;
struct dbuf_s dbuf;
assert (cmds != NULL);
dbuf_init (&dbuf, 256);
while (*cmds)
{
const char *p, *from, *par;
int sep = 1;
from = *cmds;
cmds++;
/* See if it has a '$' anywhere - if not, just copy */
if ((p = strchr (from, '$')))
{
/* include first part of cmd */
if (p != from)
{
if (!first && sep)
dbuf_append_char (&dbuf, ' ');
dbuf_append (&dbuf, from, p - from);
sep = 0;
}
from = p + 2;
/* include parameter */
p++;
switch (*p)
{
case '1':
par = p1;
break;
case '2':
par = p2;
break;
case '3':
par = p3;
break;
case 'l':
{
const char *tmp;
par = NULL;
if (list != NULL && (tmp = (const char *) setFirstItem (list)) != NULL)
{
do
{
if (*tmp != '\0')
{
if (sep)
dbuf_append_char (&dbuf, ' '); /* seperate it */
dbuf_append_str (&dbuf, tmp);
tmp++;
sep = 1;
}
}
while ((tmp = (const char *) setNextItem (list)) != NULL);
}
}
break;
default:
par = NULL;
assert (0);
}
if (par && *par != '\0')
{
if (!first && sep)
dbuf_append_char (&dbuf, ' '); /* seperate it */
dbuf_append_str (&dbuf, par);
sep = 0;
}
}
/* include the rest of cmd, e.g. ".asm" from "$1.asm" */
if (*from != '\0')
{
if (!first && sep)
dbuf_append_char (&dbuf, ' '); /* seperate it */
dbuf_append_str (&dbuf, from);
sep = 0;
}
first = 0;
}
return dbuf_detach_c_str (&dbuf);
}
char *
buildCmdLine2 (const char *pcmd, ...)
{
va_list ap;
char *poutcmd;
assert (pcmd);
assert (_mainValues);
va_start (ap, pcmd);
poutcmd = mvsprintf (_mainValues, pcmd, ap);
va_end (ap);
return poutcmd;
}
void
populateMainValues (const char **ppin)
{
_mainValues = populateStringHash (ppin);
}
/** Returns true if sz starts with the string given in key.
*/
bool
startsWith (const char *sz, const char *key)
{
return !strncmp (sz, key, strlen (key));
}
/** Removes any newline characters from the string. Not strictly the
same as perl's chomp.
*/
void
chomp (char *sz)
{
char *nl;
while ((nl = strrchr (sz, '\n')))
*nl = '\0';
}
hTab *
getRuntimeVariables (void)
{
return _mainValues;
}
/* strncpy() with guaranteed NULL termination. */
char *
strncpyz (char *dest, const char *src, size_t n)
{
assert (n > 0);
--n;
/* paranoia... */
if (strlen (src) > n)
{
fprintf (stderr, "strncpyz prevented buffer overrun!\n");
}
strncpy (dest, src, n);
dest[n] = 0;
return dest;
}
/* like strncat() with guaranteed NULL termination
* The passed size should be the size of the dest buffer, not the number of
* bytes to copy.
*/
char *
strncatz (char *dest, const char *src, size_t n)
{
size_t maxToCopy;
size_t destLen = strlen (dest);
assert (n > 0);
assert (n > destLen);
maxToCopy = n - destLen - 1;
/* paranoia... */
if (strlen (src) + destLen >= n)
{
fprintf (stderr, "strncatz prevented buffer overrun!\n");
}
strncat (dest, src, maxToCopy);
dest[n - 1] = 0;
return dest;
}
/*-----------------------------------------------------------------*/
/* getBuildNumber - return build number */
/*-----------------------------------------------------------------*/
const char *
getBuildNumber (void)
{
return (SDCC_BUILD_NUMBER);
}
/*-----------------------------------------------------------------*/
/* getBuildEnvironment - return environment used to build SDCC */
/*-----------------------------------------------------------------*/
const char *
getBuildEnvironment (void)
{
#ifdef __CYGWIN__
return "CYGWIN";
#elif defined __MINGW64__
return "MINGW64";
#elif defined __MINGW32__
return "MINGW32";
#elif defined __DJGPP__
return "DJGPP";
#elif defined(_MSC_VER)
return "MSVC";
#elif defined(__BORLANDC__)
return "BORLANDC";
#elif defined(__APPLE__)
#if defined(__i386__)
return "Mac OS X i386";
#elif defined(__x86_64__)
return "Mac OS X x86_64";
#else
return "Mac OS X ppc";
#endif
#elif defined(__linux__)
return "Linux";
#elif defined(__NetBSD__)
return "NetBSD";
#elif defined(__FreeBSD__)
return "FreeBSD";
#elif defined(__OpenBSD__)
return "OpenBSD";
#elif defined(__sun)
#if defined(__i386)
return "Solaris i386";
#elif defined(__amd64)
return "Solaris amd64";
#else
return "Solaris SPARC";
#endif
#else
return "UNIX";
#endif
}
size_t
SDCCsnprintf (char *dst, size_t n, const char *fmt, ...)
{
va_list args;
int len;
va_start (args, fmt);
len = vsnprintf (dst, n, fmt, args);
va_end (args);
/* on some gnu systems, vsnprintf returns -1 if output is truncated.
* In the C99 spec, vsnprintf returns the number of characters that
* would have been written, were space available.
*/
if ((len < 0) || (size_t) len >= n)
{
fprintf (stderr, "internal error: sprintf truncated.\n");
}
return len;
}
/** Pragma tokenizer
*/
void
init_pragma_token (struct pragma_token_s *token)
{
dbuf_init (&token->dbuf, 16);
token->type = TOKEN_UNKNOWN;
}
char *
get_pragma_token (const char *s, struct pragma_token_s *token)
{
dbuf_set_length (&token->dbuf, 0);
/* skip leading spaces */
while ('\n' != *s && isspace (*s))
++s;
if ('\0' == *s || '\n' == *s)
{
token->type = TOKEN_EOL;
}
else
{
char *end;
long val = strtol (s, &end, 0);
if (end != s && ('\0' == *end || isspace (*end)))
{
token->val.int_val = val;
token->type = TOKEN_INT;
dbuf_append (&token->dbuf, s, end - s);
s = end;
}
else
{
while ('\0' != *s && !isspace (*s))
{
dbuf_append_char (&token->dbuf, *s);
++s;
}
token->type = TOKEN_STR;
}
}
return (char *) s;
}
const char *
get_pragma_string (struct pragma_token_s *token)
{
return dbuf_c_str (&token->dbuf);
}
void
free_pragma_token (struct pragma_token_s *token)
{
dbuf_destroy (&token->dbuf);
}
/*! /fn char hexEscape(char **src)
/param src Pointer to 'x' from start of hex character value
*/
unsigned long int
hexEscape (const char **src)
{
char *s;
unsigned long value;
++*src; /* Skip over the 'x' */
value = strtoul (*src, &s, 16);
if (s == *src)
{
/* no valid hex found */
werror (E_INVALID_HEX);
}
*src = s;
return value;
}
/*------------------------------------------------------------------*/
/* universalEscape - process an hex constant of exactly four digits */
/* return the hex value, throw a warning for illegal octal */
/* adjust src to point at the last proccesed char */
/*------------------------------------------------------------------*/
unsigned long int
universalEscape (const char **str, unsigned int n)
{
unsigned int digits;
unsigned long value = 0;
const char *s = *str;
if (!options.std_c95)
{
werror (W_UNIVERSAL_C95);
}
++*str; /* Skip over the 'u' or 'U' */
for (digits = 0; digits < n; ++digits)
{
if (**str >= '0' && **str <= '7')
{
value = (value << 4) + (**str - '0');
++*str;
}
else if ((**str | 0x20) >= 'a' && (**str | 0x20) <= 'f')
{
value = (value << 4) + (**str - 'a' + 10);
++*str;
}
else
break;
}
if (digits != n || value < 0x00a0 && value != 0x0024 && value != 0x0040 && value != 0x0060 || value >= 0xd800 && 0xdfff >= value)
{
werror (E_INVALID_UNIVERSAL, s);
}
return value;
}
/*------------------------------------------------------------------*/
/* octalEscape - process an octal constant of max three digits */
/* return the octal value, throw a warning for illegal octal */
/* adjust src to point at the last proccesed char */
/*------------------------------------------------------------------*/
unsigned long int
octalEscape (const char **str)
{
int digits;
unsigned value = 0;
for (digits = 0; digits < 3; ++digits)
{
if (**str >= '0' && **str <= '7')
{
value = value * 8 + (**str - '0');
++*str;
}
else
{
break;
}
}
return value;
}
/*!
/fn const char *copyStr (const char *src, size_t *size)
Copies source string to a dynamically allocated buffer interpreting
escape sequences and special characters
/param src Buffer containing the source string with escape sequecnes
/param size Pointer to loction where the resulting buffer length is written
/return Dynamically allocated resulting buffer
*/
const char *
copyStr (const char *src, size_t *size)
{
const char *begin = NULL;
struct dbuf_s dbuf;
dbuf_init(&dbuf, 128);
while (*src)
{
if (*src == '\"')
{
if (begin)
{
/* copy what we have until now */
dbuf_append (&dbuf, begin, src - begin);
begin = NULL;
}
++src;
}
else if (*src == '\\')
{
unsigned long int c;
bool universal = FALSE;
if (begin)
{
/* copy what we have until now */
dbuf_append (&dbuf, begin, src - begin);
begin = NULL;
}
++src;
switch (*src)
{
case 'n':
c = '\n';
break;
case 't':
c = '\t';
break;
case 'v':
c = '\v';
break;
case 'b':
c = '\b';
break;
case 'r':
c = '\r';
break;
case 'f':
c = '\f';
break;
case 'a':
c = '\a';
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
c = octalEscape (&src);
--src;
break;
case 'x':
c = hexEscape (&src);
--src;
break;
case 'u':
c = universalEscape (&src, 4);
universal = TRUE;
--src;
break;
case 'U':
c = universalEscape (&src, 8);
universal = TRUE;
--src;
break;
case '\\':
case '\?':
case '\'':
case '\"':
default:
c = *src;
break;
}
if (universal) // Encode one utf-32 character to utf-8
{
char s[5] = "\0\0\0\0";
if (c < 0x80)
s[0] = (char)c;
else if (c < 0x800)
{
s[0] = (c >> 6) & 0x1f | 0xc0;
s[1] = (c >> 0) & 0x3f | 0x80;
}
else if (c < 0x10000)
{
s[0] = (c >> 12) & 0x0f | 0xe0;
s[1] = (c >> 6) & 0x3f | 0x80;
s[2] = (c >> 0) & 0x3f | 0x80;
}
else if (c < 0x110000)
{
s[0] = (c >> 18) & 0x07 | 0xf0;
s[1] = (c >> 12) & 0x3f | 0x80;
s[2] = (c >> 6) & 0x3f | 0x80;
s[3] = (c >> 0) & 0x3f | 0x80;
}
else
wassert (0);
dbuf_append_str (&dbuf, s);
}
else
dbuf_append_char (&dbuf, (char)c);
++src;
}
else
{
if (!begin)
begin = src;
++src;
}
}
if (begin)
{
/* copy what we have until now */
dbuf_append (&dbuf, begin, src - begin);
begin = NULL;
}
if (size)
{
/* include null terminator character
appended by dbuf_detach_c_str() */
*size = dbuf_get_length (&dbuf) + 1;
}
return dbuf_detach_c_str (&dbuf);
}
static char prefix[256] = "";
static char suffix[256] = "";
static char cmd[4096] = "";
void getPrefixSuffix(const char *arg)
{
const char *p;
const char sdcc[] = "sdcc";
if (!arg)
return;
p = arg + strlen (arg);
while (p != arg && *(p - 1) != '\\' && *(p - 1) != '/')
p--;
arg = p;
/* found no "sdcc" in command line argv[0] */
if ((p = strstr (arg, sdcc)) == NULL)
return;
/* found more than one "sdcc" in command line argv[0] */
if (strstr (p + strlen (sdcc), sdcc) != NULL)
return;
/* copy prefix and suffix */
strncpy (prefix, arg, p - arg);
strcpy (suffix, p + strlen (sdcc));
}
char *setPrefixSuffix(const char *arg)
{
const char *p;
if (!arg)
return NULL;
else
memset (cmd, 0x00, sizeof (cmd));
/* find the core name of command line */
for (p = arg; (*p) && isblank (*p); p++);
arg = p;
assert (strstr (arg, ".exe") == NULL);
for (p = arg; (*p) && !isblank (*p); p++);
/* compose new command line with prefix and suffix */
strcpy (cmd, prefix);
strncat (cmd, arg, p - arg);
strcat (cmd, suffix);
strcat (cmd, p);
return cmd;
}
char *formatInlineAsm (char *asmStr)
{
char *p, *q;
if (!asmStr)
return NULL;
else
q = asmStr;
for (;;)
{
// omit leading space or tab
while (*q == '\t' || *q == ' ')
q++;
// then record the head of current line
p = q;
// search for CL or reach the end
while (*q != '\n' && *q != '\r' && *q != 0)
q++;
// omit more CL characters
while (*q == '\n' || *q == '\r')
q++;
// replace the first with tab
while (p != q)
if (*p == '\t') // '\t' appears first then no need to do
{
break;
}
else if (*p == ' ') // find the first space then replace it with tab
{
*p = '\t';
break;
}
else // go on to search
{
p++;
}
// check if end
if (*q == 0)
return asmStr;
}
}