/*------------------------------------------------------------------------- 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 #include #ifdef _WIN32 #include #endif #include #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; } /** 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 given strings to each item of string set. The result is in a new string set. */ set * appendStrSet(set *list, const char *pre, const char *post) { 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 != NULL) dbuf_append_str(&dbuf, pre); dbuf_append_str(&dbuf, item); if (post != NULL) dbuf_append_str(&dbuf, post); /* null terminate the buffer */ dbuf_c_str(&dbuf); addSet(&new_list, dbuf_detach(&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, ' '); } s = dbuf_c_str(&dbuf); dbuf_detach(&dbuf); return s; } /** 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); dbuf_c_str(&path); return dbuf_detach(&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); dbuf_c_str(&path); return dbuf_detach(&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); } void buildCmdLine2 (char *pbuffer, size_t len, const char *pcmd, ...) { va_list ap; char *poutcmd; assert(pbuffer && pcmd); assert(_mainValues); va_start(ap, pcmd); poutcmd = mvsprintf(_mainValues, pcmd, ap); va_end(ap); strncpyz(pbuffer, poutcmd, len); Safe_free(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); } /*-----------------------------------------------------------------*/ /* getBuildDate - return build date */ /*-----------------------------------------------------------------*/ const char *getBuildDate(void) { return (__DATE__); } /*-----------------------------------------------------------------*/ /* getBuildEnvironment - return environment used to build SDCC */ /*-----------------------------------------------------------------*/ const char *getBuildEnvironment(void) { #ifdef __CYGWIN__ return "CYGWIN"; #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"; # else return "Mac OS X ppc"; #endif #else return "UNIX"; #endif } #if defined(HAVE_VSNPRINTF) || defined(HAVE_VSPRINTF) size_t SDCCsnprintf(char *dst, size_t n, const char *fmt, ...) { va_list args; int len; va_start(args, fmt); # if defined(HAVE_VSNPRINTF) len = vsnprintf(dst, n, fmt, args); # else vsprintf(dst, fmt, args); len = strlen(dst) + 1; # endif 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; } #endif /** 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 char hexEscape (const char **src) { char *s ; unsigned long value ; (*src)++ ; /* Skip over the 'x' */ value = strtol (*src, &s, 16); if (s == *src) { // no valid hex found werror(E_INVALID_HEX); } else { if (value > 255) { werror(W_ESC_SEQ_OOR_FOR_CHAR); } } *src = s; return (char) 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 char 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; } } if (digits) { if (value > 255 /* || (**str>='0' && **str<='7') */ ) { werror (W_ESC_SEQ_OOR_FOR_CHAR); } } return value; } /*! /fn int copyStr (char *dest, char *src) Copies a source string to a dest buffer interpreting escape sequences and special characters /param dest Buffer to receive the resultant string /param src Buffer containing the source string with escape sequecnes /return Number of characters in output string */ int copyStr (char *dest, const char *src) { char *OriginalDest = dest ; while (*src) { if (*src == '\"') src++; else if (*src == '\\') { src++; switch (*src) { case 'n': *dest++ = '\n'; break; case 't': *dest++ = '\t'; break; case 'v': *dest++ = '\v'; break; case 'b': *dest++ = '\b'; break; case 'r': *dest++ = '\r'; break; case 'f': *dest++ = '\f'; break; case 'a': *dest++ = '\a'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': *dest++ = octalEscape(&src); src-- ; break; case 'x': *dest++ = hexEscape(&src) ; src-- ; break ; case '\\': *dest++ = '\\'; break; case '\?': *dest++ = '\?'; break; case '\'': *dest++ = '\''; break; case '\"': *dest++ = '\"'; break; default: *dest++ = *src; } src++; } else *dest++ = *src++; } *dest++ = '\0'; return dest - OriginalDest ; }