blob: cb5a678db7df05d30d7bf9effed67efbd45991a9 [file] [log] [blame]
/* --COPYRIGHT--,EPL
* Copyright (c) 2008-2018 Texas Instruments Incorporated
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Texas Instruments - initial implementation
*
* --/COPYRIGHT--*/
/*
* ======== xutl.c ========
*/
#include <xdc/std.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <xutl.h>
#define MAXNAME 512
#define MAXLINE (2 * MAXNAME)
#define MAXERROR (2 * MAXNAME)
#define MAXTMP 16
#define MAXARGS 512
#define MAXATDEPTH 64
#if defined(xdc_target__os_Windows)
#include <Windows.h> /* GetModuleFileName */
#include <io.h>
#include <process.h>
#include <direct.h>
#include <sys/stat.h>
#include <sys/utime.h>
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define DIRSTR "\\"
#define PATHSEP ";"
#define USERWRITE (S_IWRITE | S_IREAD)
#else /* else assume posix */
/* define realpath here because it is not ansi-standard but very common */
extern char *realpath(const char *, char *);
#include <errno.h>
#include <dirent.h>
#include <unistd.h>
#include <utime.h>
#include <sys/types.h>
#include <sys/stat.h>
#define DIRSTR "/"
#define PATHSEP ":"
#define USERWRITE (S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)
#endif
/* XUTL_setDate options */
#define DATEMAX (1)
#define DATEMIN (-1)
#define DATESAME (0)
#define MAXPATHLEN (FILENAME_MAX > 1024 ? FILENAME_MAX : 1024)
#define RTOKEN '^'
#define TABQUANTUM 16
typedef struct ArgvDesc {
String *argv;
Int argc;
} ArgvDesc;
static XUTL_PkgDesc EMPTYDESC = {
"", /* the package name */
"", /* release date */
"", /* release label */
"", /* package compatibility key */
"", /* build count of package */
"" /* release name */
};
static char globalPathBuf[MAXPATHLEN + 1] = {'\0'};
static String tmpNames[MAXTMP];
static int tmpNamesLen = 0;
static Char lastErrorString[MAXERROR + 1] = {'\0'};
static Bool appendName(String name, String **tab, Int *tabLen, Int *tabMax);
static Void copyAttrs(String src, String dst);
static Void cleanPath(String old);
static Bool expandArgv(ArgvDesc *in, ArgvDesc *out, String *err);
static Int find(String root, Bool b, String **tab, Int *tabLen, Int *tabMax);
static String getPackageName(String dname);
static String getRepository(String name);
static Bool getXMLine(Char *buffer, Int size, FILE *in);
static String getXMLValue(String pair);
static Bool mkArgv(String fileName, ArgvDesc *desc, String *err);
static String newString(String old);
static Bool isAbsolute(String name);
static Bool isPkg(String name);
static Bool mkArgv(String fileName, ArgvDesc *desc, String *err);
static Bool mkDir(String dir);
static Void saveError(Int len, String format, ...);
static String stripPkgName(String root, String pname);
static Void tmpCleanup(Void);
static String tryPath(String pname, String cdir);
/*
* ======== XUTL_cp ========
*/
Bool XUTL_cp(String src, String dst)
{
FILE *in, *out;
static Char buf[1024];
SizeT n;
Bool result = FALSE;
/* printf("XUTL_cp('%s', '%s')\n", src, dst); */
if ((in = fopen(src, "rb")) != NULL
&& (out = fopen(dst, "wb")) != NULL) {
while ((n = fread(buf, 1, sizeof(buf), in)) > 0) {
if (fwrite(buf, 1, n, out) != n) {
break; /* write failed, so stop copying now */
}
}
result = ((ferror(in) == 0) && (ferror(out) == 0)) ? TRUE : FALSE;
}
if (in != NULL) {
fclose(in);
}
if (out != NULL) {
fclose(out);
if (result == FALSE) {
remove(dst); /* remove dst if the copy failed */
}
}
/* if the copy was successful, clone timestamp and permissions from src */
if (result == TRUE) {
copyAttrs(src, dst);
}
#if 0
else {
perror("XUTL_cp failed");
}
#endif
return (result);
}
/*
* ======== XUTL_expandArgv ========
* Expand an argv array that may contain @file options
*/
String *XUTL_expandArgv(Int argc, String *argv, Int *outArgc, String *err)
{
ArgvDesc oldArgs, newArgs;
Int i;
oldArgs.argv = argv;
oldArgs.argc = argc;
for (i = 0; i < MAXATDEPTH; i++) {
if (expandArgv(&oldArgs, &newArgs, err)) {
if (i > 0) {
free(oldArgs.argv);
}
oldArgs = newArgs;
}
else {
break;
}
}
*outArgc = newArgs.argc;
return (newArgs.argv);
}
/*
* ======== XUTL_insertArgv ========
* Insert arguments into an argv array
*/
String *XUTL_insertArgv(Int insertArgc, String *insertArgv, Int insertBefore,
Int argc, String *argv, Int *outArgc, String *err)
{
Int newArgc;
String *newArgv;
/* by default signal no error */
*err = NULL;
/* allocate memory for new argument array */
newArgc = argc + insertArgc;
newArgv = malloc(newArgc * sizeof (String));
if (!newArgv) {
*err = "can't allocate argv array";
return NULL;
}
/* copy args from before the insert point */
if (insertBefore > 0) {
memcpy(newArgv, argv, insertBefore * sizeof (String));
}
/* copy the new args */
if (insertArgc > 0) {
memcpy(&newArgv[insertBefore], insertArgv, insertArgc * sizeof (String));
}
/* copy args from after the insert point */
if (insertBefore < argc) {
memcpy(&newArgv[insertBefore + insertArgc], &argv[insertBefore],
(argc - insertBefore) * sizeof (String));
}
/* clean up the old argv */
free(argv);
/* and return the new one */
*outArgc = newArgc;
return (newArgv);
}
/*
* ======== XUTL_expandPath ========
*/
String XUTL_expandPath(String str, String pdir, Bool absolute)
{
Char *src, *dst;
String result = NULL;
Int i;
/* count the number of items to expand */
for (i = 0, src = str; *src != '\0'; src++) {
if (*src == RTOKEN) {
i++;
}
}
/* if there is nothing to expand, simply return str */
if (i == 0) {
return (str);
}
else {
/* otherwise, get the repository name (relative to pkg) and cwd */
Char cwd[MAXPATHLEN + 1];
String rname = XUTL_getPackageName(pdir, TRUE);
Int plen = 0;
cwd[0] = '\0';
if (rname != NULL) {
if (absolute && !isAbsolute(pdir)) {
if (getcwd(cwd, sizeof(cwd)) == NULL) {
saveError(80, "XUTL_expandPath: can't get cwd");
free(rname);
return (NULL);
}
plen = strlen(pdir) + strlen(rname) + strlen(cwd) + 2;
}
else {
plen = strlen(pdir) + strlen(rname) + 1;
}
}
/* allocate space for the expanded path string */
if ((result = malloc((i * plen) + strlen(str) + 1)) == NULL) {
if (rname != NULL) {
free(rname);
}
saveError(80, "XUTL_expandPath: Out of memory");
return (NULL);
}
/* replace RTOKEN with the repository path */
for (src = str, dst = result; *src != '\0';) {
if (*src == RTOKEN) {
src++;
/* if we are not in a package, remove expr from path */
if (rname == NULL) {
while (*src != ';' && *src != '\0') {
src++;
}
if (*src == ';') {
src++;
}
}
else {
if (cwd[0] != '\0') {
sprintf(dst, "%s/%s/%s", cwd, pdir, rname);
}
else {
sprintf(dst, "%s/%s", pdir, rname);
}
dst += plen;
}
}
else {
*dst++ = *src++;
}
}
*dst = '\0';
if (rname != NULL) {
free(rname);
}
}
return (result);
}
/*
* ======== XUTL_findFile ========
* Find file along specified path. Returned string must be freed (via
* free()) by caller.
*/
String XUTL_findFile(String file, String path, String pathSep)
{
Char nameBuf[MAXPATHLEN + 1];
String pathBuf = NULL;
String tmp;
Int len;
if (pathSep == NULL) {
pathSep = PATHSEP;
}
/* if file is a full path name, test it unadorned */
if (file[0] == DIRSTR[0] || file[1] == ':') {
if (XUTL_isFile(file)) {
return (newString(file));
}
}
/* if the nameBuf is too small we fail */
if (file == NULL || (len = strlen(file)) > MAXPATHLEN) {
saveError(80, "Invalid file name argument");
return (NULL);
}
if (path == NULL) {
saveError(80, "Invalid path name argument");
return (NULL);
}
/* otherwise, try every prefix in path */
if ((pathBuf = (Char *)malloc(strlen(path) + 1)) != NULL) {
strcpy(pathBuf, path);
tmp = strtok(pathBuf, pathSep);
for (; tmp != NULL; tmp = strtok(NULL, pathSep)) {
if ((len + 1 + strlen(tmp)) <= MAXPATHLEN) {
sprintf(nameBuf, "%s%s%s", tmp, DIRSTR, file);
if (XUTL_isFile(nameBuf)) {
free(pathBuf);
return (newString(nameBuf));
}
}
}
free(pathBuf);
}
return (NULL);
}
/*
* ======== XUTL_findPackage ========
*/
String XUTL_findPackage(String pname, String ppath)
{
Char pdir[MAXNAME + 1];
String pathBuf = NULL;
String tmp;
Char *cp;
Int len;
/* if the nameBuf is too small we fail */
if (pname == NULL || ppath == NULL || (len = strlen(pname)) > MAXNAME) {
saveError(80, "Invalid pname or ppath argument");
return (NULL);
}
/* otherwise, try every prefix in path */
if ((pathBuf = (Char *)malloc(strlen(ppath) + 1)) != NULL) {
strcpy(pathBuf, ppath);
strcpy(pdir, pname);
for (cp = pdir; *cp != '\0'; cp++) {
if (*cp == '.') {
*cp = '/';
}
}
tmp = strtok(pathBuf, ";");
for (; tmp != NULL; tmp = strtok(NULL, ";")) {
if ((len + 1 + strlen(tmp)) <= MAXPATHLEN) {
Char nameBuf[MAXPATHLEN + 1];
sprintf(nameBuf, "%s/%s", tmp, pdir);
if (isPkg(nameBuf)) {
free(pathBuf);
return (newString(nameBuf));
}
}
}
free(pathBuf);
}
return (NULL);
}
/*
* ======== XUTL_findPackages ========
* Return array of all packages starting from (and including) startDir.
*
* Every string in the returned array begins with startDir followed by
* '/'. Thus, relative startDir's result in relative paths to packages
* and an absolute startDir results in absolute paths to packages.
*
* Returns negative length on error.
*/
String *XUTL_findPackages(String startDir, Bool buildable, Int *len)
{
char cwd[MAXPATHLEN + 1];
String *result = NULL;
Int resultLen = 0;
Int resultMax = 0;
Int status;
Int i;
/* capture the current working directory */
if (getcwd(cwd, MAXPATHLEN) == NULL) {
saveError(80, "XUTL_findPackages: can't get cwd");
*len = -1;
return (NULL);
}
status = find(startDir, buildable, &result, &resultLen, &resultMax);
for (i = 0; i < resultLen; i++) {
cleanPath(result[i]);
}
/* restore the current working directory */
if (chdir(cwd) < 0) {
saveError(80 + strlen(cwd),
"XUTL_findPackages: chdir(%s) failed", cwd);
return (NULL);
}
*len = status == 0 ? resultLen : (resultLen == 0 ? -1 : (-1 * resultLen));
return (result);
}
/*
* ======== XUTL_freePackageDesc ========
*/
Void XUTL_freePackageDesc(XUTL_PkgDesc *desc)
{
if (desc != NULL) {
if (desc->name != EMPTYDESC.name) {
free(desc->name);
}
if (desc->date != EMPTYDESC.date) {
free(desc->date);
}
if (desc->label != EMPTYDESC.label) {
free(desc->label);
}
if (desc->compatKey != EMPTYDESC.compatKey) {
free(desc->compatKey);
}
if (desc->buildCount != EMPTYDESC.buildCount) {
free(desc->buildCount);
}
if (desc->releaseName != EMPTYDESC.releaseName) {
free(desc->releaseName);
}
*desc = EMPTYDESC;
}
}
/*
* ======== XUTL_getCanonicalPath ========
*/
String XUTL_getCanonicalPath(String path)
{
Char buffer[MAXPATHLEN + 1];
String cpath = NULL;
#if defined(xdc_target__os_Windows)
Char *fp;
Int len = GetFullPathName(path, sizeof(buffer)/sizeof(TCHAR), buffer, &fp);
if (len > 0) {
if ((cpath = malloc((len + 1) * sizeof(TCHAR))) != NULL) {
strcpy(cpath, buffer);
/* trim trailing directory separation character. We do this to
* ensure that "." and "./" result in the same path;
* getFullPathName() adds a trailing '\' character if the input
* ends in '\'
*/
if (cpath[len - 1] == '\\') {
cpath[len - 1] = '\0';
}
}
}
#else
String tmp;
if ((tmp = realpath(path, buffer)) != NULL) {
Int len = strlen(tmp);
if ((cpath = malloc((len + 1) * sizeof(Char))) != NULL) {
strcpy(cpath, tmp);
}
}
#endif
if (cpath == NULL) {
saveError(80 + strlen(path), "XUTL_getCanonicalPath(%s) failed", path);
}
return (cpath);
}
/*
* ======== XUTL_getLastErrorString ========
* Return meaningful error string for the last error that occured.
*/
String XUTL_getLastErrorString(Void)
{
return (lastErrorString);
}
/*
* ======== XUTL_getPackageDesc ========
*/
Bool XUTL_getPackageDesc(String dname, XUTL_PkgDesc *desc)
{
FILE *pfile;
static char lineBuf[MAXLINE + 32]; /* +32 to allow + "/package/..." */
if (strlen(dname) >= MAXLINE) {
saveError(80, "XUTL_getPackageDesc: package path too long");
return (FALSE);
}
/* initialize all fields to non-NULL strings */
*desc = EMPTYDESC;
/* open the release XML file */
sprintf(lineBuf, "%s/package/package.rel.xml", dname);
if ((pfile = fopen(lineBuf, "r")) == NULL) {
String name;
if (XUTL_isFile(lineBuf)) {
saveError(80 + strlen(dname),
"Can't open %s/package/package.rel.xdc", dname);
return (FALSE);
}
if ((name = XUTL_getPackageName(dname, FALSE)) == NULL) {
return (FALSE);
}
desc->name = name;
return (TRUE);
}
/* scan line by line for release info */
while (getXMLine(lineBuf, sizeof(lineBuf), pfile)) {
if (strncmp(lineBuf, "release ", 8) == 0) {
/* parse release attrs */
String name;
if ((name = strstr(lineBuf, "name=")) != NULL) {
desc->releaseName = getXMLValue(name);
}
if ((name = strstr(lineBuf, "label=")) != NULL) {
desc->label = getXMLValue(name);
}
if ((name = strstr(lineBuf, "date=")) != NULL) {
desc->date = getXMLValue(name);
}
if ((name = strstr(lineBuf, "buildCount=")) != NULL) {
desc->buildCount = getXMLValue(name);
}
}
else if (strncmp(lineBuf, "package ", 8) == 0) {
/* parse package attrs */
String name;
if ((name = strstr(lineBuf, "name=")) != NULL) {
desc->name = getXMLValue(name);
}
if ((name = strstr(lineBuf, "version=")) != NULL) {
desc->compatKey = getXMLValue(name);
if (desc->compatKey == NULL) {
desc->compatKey = strdup("0, 0, 0, 0");
}
else if (desc->compatKey[0] == '\0') {
free(desc->compatKey);
desc->compatKey = strdup("0, 0, 0, 0");
}
}
break;
}
}
fclose(pfile);
/* ensure we never return a NULL string */
if (desc->name == NULL) {
desc->name = EMPTYDESC.name;
}
if (desc->date == NULL) {
desc->date = EMPTYDESC.date;
}
if (desc->label == NULL) {
desc->label = EMPTYDESC.label;
}
if (desc->compatKey == NULL) {
desc->compatKey = EMPTYDESC.compatKey;
}
if (desc->buildCount == NULL) {
desc->buildCount = EMPTYDESC.buildCount;
}
if (desc->releaseName == NULL) {
desc->releaseName = EMPTYDESC.releaseName;
}
return (TRUE);
}
/*
* ======== XUTL_getPackageName ========
* Return the name of a package (or it's repository) specified by the
* directory containing the package's specification file (package.xdc).
*
* Params:
* dname directory containing package.xdc
* rflag if TRUE, return the package's repository path (relative to
* dname); otherwise return the "dot separated" package name.
*
* Returned string must be freed (via free()) by caller.
*/
String XUTL_getPackageName(String dname, Bool rflag)
{
String name, result;
if ((name = getPackageName(dname)) == NULL) {
return (NULL);
}
result = rflag ? getRepository(name) : newString(name);
if (result == NULL) {
saveError(80, "XUTL_getPackageName: Out of memory");
}
return (result);
}
/*
* ======== XUTL_getPackageRep ========
* Return canonical name of a package's repository
*
* The package is specified by the directory containing the package's
* specification file (package.xdc).
*
* Params:
* dname directory containing package.xdc; must be a non-empty
* string
*
* Returns:
* absolute canonical path to package's repository. In the event of
* an error it returns NULL
*
* Returned string must be freed (via free()) by caller.
*/
String XUTL_getPackageRep(String dname)
{
String result;
String pname;
if (dname[0] == '\0' || (pname = getPackageName(dname)) == NULL) {
return (NULL);
}
/* start with canonical path to the package's base */
if ((result = XUTL_getCanonicalPath(dname)) != NULL) {
Int len, rlen;
/* compute length of suffix to trim from result, len */
if (pname[0] == '\0') { /* support for unnamed packages (remove?) */
Char *cp = result + strlen(result);
for (len = 0; cp != result && *cp != '/' && *cp != '\\'; len++) {
cp--;
}
}
else {
len = strlen(pname) + 1;
}
/* trim package name from canonical path to package's base dir */
rlen = strlen(result);
if (len >= rlen) {
/* package name doesn't match its directory (try symbolic links) */
return (tryPath(pname, result));
}
else {
Char *cp;
String suffix = result + (rlen - len) + 1;
for (cp = pname; *cp != '\0'; cp++, suffix++) {
if (*cp == *suffix
|| (*cp == '.' && (*suffix == '\\' || *suffix == '/'))) {
continue;
}
/* package name doesn't match its directory (try symbolic links) */
return (tryPath(pname, result));
}
}
/* we have a direct match! */
result[rlen - len] = '\0';
}
return (result);
}
/*
* ======== XUTL_getPackageReqs ========
* Return an array strings (package names) that the specified package
* requires. The package is specified using the name of the directory
* containing the package's specification file (package.xdc).
*
* Params:
* dname directory containing package.xdc
* len output number of strings in the returned array
*
* Returns NULL in the event of an error; otherwise it returns an array
* of strings naming required packages (possibly of length 0).
*
* Note that the caller is responsible for freeing the storage associated
* with the returned array. If the array length is zero, no storage needs
* to be freed.
*/
String *XUTL_getPackageReqs(String dname, Int *len)
{
FILE *pfile;
char lineBuf[MAXLINE + 32]; /* +32 to cover appending "/package.xdc" */
String name = NULL;
static String mt[] = {NULL};
String *result = NULL;
Int resultLen = 0;
Int resultMax = 0;
*len = 0;
if (strlen(dname) >= MAXLINE) {
sprintf(lastErrorString,"XUTL_getPackageReqs: Invalid dname argument");
return (NULL);
}
sprintf(lineBuf, "%s/package.xdc", dname);
if ((pfile = fopen(lineBuf, "r")) == NULL) {
saveError(80 + strlen(dname), "Can't open %s/package.xdc", dname);
return (NULL);
}
while (fgets(lineBuf, sizeof(lineBuf), pfile) != NULL) {
String token = strtok(lineBuf, " \t");
if (token != NULL) {
if (strcmp(token, "requires") == 0) {
if ((name = strtok(NULL, " \t{[;/\n\r")) != NULL) {
if (strcmp(name, "internal") == 0) {
if ((name = strtok(NULL, " \t{[;/\n\r")) == NULL) {
continue;
}
}
if (!appendName(name, &result, &resultLen, &resultMax)) {
fclose(pfile);
return (NULL);
}
}
}
/* support possible future "internal requires" syntax */
else if (strcmp(token, "internal") == 0
&& (name = strtok(NULL, " \t{[;/\n\r")) != NULL
&& strcmp(name, "requires") == 0
&& (name = strtok(NULL, " \t{[;/\n\r")) != NULL) {
if (!appendName(name, &result, &resultLen, &resultMax)) {
fclose(pfile);
return (NULL);
}
}
else if (strcmp(token, "package") == 0) {
break; /* requires statements must preceed package statement */
}
}
}
fclose(pfile);
*len = resultLen;
return (resultLen == 0 ? mt : result);
}
/*
* ======== XUTL_getProgPath ========
*/
String XUTL_getProgPath(String pname)
{
String tmp;
Char *pathBuf;
#if defined(xdc_target__os_Windows)
Int len;
if ((pathBuf = malloc((sizeof (Char)) * (MAXNAME + 1))) == NULL) {
strcpy(lastErrorString, "XUTL_getProgPath ran out of memory");
return (NULL);
}
if ((len = GetModuleFileName(0, pathBuf, MAXNAME)) != 0) {
#if 0
/* Call GetLongPathName() to get a canonical name. Without this,
* calling xdc differently may result in different (but equivalent)
* names. We can (should) fix this in xdcenv.c where we check
* (this name and others) but doing it here means that everyone
* benefits.
*/
if ((len = GetLongPathName(pathBuf, pathBuf, MAXNAME)) == 0) {
;
}
#endif
if ((tmp = strrchr(pathBuf, DIRSTR[0])) != NULL) {
tmp[0] = '\0';
cleanPath(pathBuf);
return (pathBuf);
}
}
strcpy(lastErrorString, "GetModuleFileName() failed");
return (NULL);
#else
/* if progName has no directory info look for it along the path */
tmp = NULL;
if (strrchr(pname, DIRSTR[0]) == NULL) {
if ((tmp = XUTL_findFile(pname, getenv("PATH"), NULL)) != NULL) {
pname = tmp;
}
}
/* convert to canonical name and free any allocated tmp string */
pathBuf = XUTL_getCanonicalPath(pname);
if (tmp != NULL) {
free(tmp);
}
/* extract just the directory name into pathBuf */
if (pathBuf != NULL && (tmp = strrchr(pathBuf, DIRSTR[0])) != NULL) {
tmp[0] = '\0';
}
return (pathBuf);
#endif
}
/*
* ======== XUTL_init ========
*/
Void XUTL_init(Void)
{
static Bool called = FALSE;
if (called == FALSE) {
called = TRUE;
atexit(tmpCleanup);
}
}
/*
* ======== XUTL_isDir ========
* Return TRUE iff name exists and is a directory
*/
Bool XUTL_isDir(String name)
{
struct stat buf;
if (stat(name, &buf) == 0 && S_ISDIR(buf.st_mode)) {
/* printf("XUTL_isDir('%s'): true\n", name); */
return (TRUE);
}
/* printf("XUTL_isDir('%s'): false\n", name); */
return (FALSE);
}
/*
* ======== XUTL_isPackageBase ========
* Is the specified directory a package.
*
* If buildFlag is FALSE, return TRUE iff pbase is a package base directory.
*
* If buildFlag is TRUE, return TRUE iff pbase is a package base directory
* and it is a buildable package.
*/
Bool XUTL_isPackageBase(String pbase, Bool buildFlag)
{
Char nameBuf[MAXPATHLEN + 1];
Int len;
/* if the nameBuf is too small we fail; 12 = length of "/package.???" */
if ((len = strlen(pbase)) > (MAXPATHLEN - 12)) {
saveError(80, "XUTL_isPackageBase: directory too long");
return (FALSE);
}
strcpy(nameBuf, pbase);
strcat(nameBuf, "/package.xdc");
if (access(nameBuf, 00) == 0) {
if (buildFlag) {
strcpy(nameBuf + len + 9, "bld"); /* 9 = length of "/package." */
if (access(nameBuf, 00) == 0) {
return (TRUE);
}
}
else {
return (TRUE);
}
}
return (FALSE);
}
/*
* ======== XUTL_reducePath ========
* remove redundent '/' and '\' characters and embedded "/../" sequences.
*
*/
String XUTL_reducePath(String path, Bool trim)
{
String src, dst;
Bool eraseDotDot = TRUE;
Bool trimFlag = trim;
src = path;
dst = path;
/* copy src to dst removing embedded /../ sequences */
while (*src != '\0') {
if (trimFlag) {
/* remove any leading "./" */
if (src[0] == '.' && (src[1] == '/' || src[1] == '\\')) {
for (src += 2; src[0] == '/' || src[1] == '\\'; src++) {
;
}
}
trimFlag = FALSE;
}
top:
if (src[0] == '/' || src[0] == '\\') {
/* remove redundent '//' or '/./' */
while (src[1] == '/' || src[1] == '\\'
|| (src[1] == '.' && (src[2] == '/' || src[2] == '\\'))) {
src++;
}
/* if eraseDotDot, process '../' sequences */
if (eraseDotDot && src[1] == '.' && src[2] == '.'
&& strchr("/\\", src[3]) != NULL) {
/* erase until next '/' but not beyond ';' */
Char *cp;
for (cp = dst - 1; cp >= path; cp--) {
if (cp[0] == '/' || cp[0] == '\\') {
dst = cp;
src += 3;
if (src[0] == '\0') {
*dst = '\0';
return (path);
}
goto top;
}
else if (cp[0] == ';') {
break; /* don't erase beyond a ';' */
}
}
/* if we get here we can't erase anymore; "/a/../.." */
eraseDotDot = FALSE;
}
}
if (src[0] == ';') {
eraseDotDot = TRUE;
trimFlag = trim;
}
*dst++ = *src++;
}
*dst = *src; /* '\0' terminate path */
return (path);
}
/*
* ======== XUTL_run ========
* Even though Win32 provides execve, it does not have the same effect
* when it comes to the exit status of the calling program. Therefore,
* we are forced to re-implement this function using Win32 primitives
* and explicitly exit the calling process.
*/
Int XUTL_run(const char *cmdname, String argv[], String envp[])
{
#if defined(xdc_target__os_Windows)
const char *const *tmp;
int argc, i;
int status;
char **args;
/* Workaround for a bug in ucrtbase.dll:
* https://wpdev.uservoice.com/forums/110705-universal-windows-platform/suggestions/18355615-ucrtbase-bug-wspawnve-is-broken
*/
SetEnvironmentVariable(TEXT("=C"), TEXT("C:"));
/* count number of arguments in argv and create local array of arguments */
for (tmp = argv, argc = 0; *tmp != NULL; tmp++) {
argc++;
}
if ((args = malloc(sizeof(char *) * (argc + 1))) == NULL) {
fprintf(stderr, "can't execute '%s'.\n", cmdname);
return (1);
}
/* initialize local argument array from argv */
/* quote all arguments to command (to preserve white space in args) */
for (i = 0; i < argc; i++) {
const char *src;
char *dst;
if ((args[i] = malloc(2 * strlen(argv[i]) + 3)) == NULL) {
fprintf(stderr, "can't execute '%s'.\n", cmdname);
return (1);
}
dst = args[i];
*dst++ = '"';
for (src = argv[i]; *src != '\0'; ) {
if (*src == '"') {
*dst++ = '\\'; /* backquote embedded "'s */
}
*dst++ = *src++;
}
*dst++ = '"';
*dst = '\0'; /* args[i] = "argv[i]" */
}
args[argc] = NULL;
/*
* added "'s around all arguments to counter-act argument processing
* done by #^#%^! Win32 spawn command.
*/
status = _spawnvpe(_P_WAIT, cmdname, args, envp);
if (status < 0) {
return (status);
}
exit(status);
#else
int status = 1;
if (access(cmdname, 00) == 0) {
status = execve(cmdname, argv, envp);
}
else {
/* cmdname doesn't exist, so search for it along the PATH */
String cmd = XUTL_findFile((String)cmdname, getenv("PATH"), ":");
if (cmd != NULL) {
status = execve(cmd, argv, envp);
}
free(cmd);
}
return (status);
#endif
}
/*
* ======== XUTL_rm ========
*/
Bool XUTL_rm(String fileName)
{
struct stat buf;
if (stat(fileName, &buf) >= 0) {
if (remove(fileName) != 0) {
#if defined(xdc_target__os_Windows)
/* Windows does not allow one to remove a read-only file !!! */
chmod(fileName, USERWRITE);
if (remove(fileName) != 0) {
/* if we fail again, do no harm: restore file mode bits */
chmod(fileName, buf.st_mode);
return (FALSE);
}
#else
/* UNIX only requires write permission on the directory */
return (FALSE);
#endif
}
}
return (TRUE);
}
#if defined(xdc_target__os_Windows)
/*
* ======== XUTL_scanFS ========
*/
Int XUTL_scanFS(String root, XUTL_FileScanFxn fileApply,
XUTL_FileFilterFxn fileSkip, IArg arg, XUTL_FSFlags flags)
{
Int status = 0;
Char *cp, *cp2;
IArg cookie = (IArg)0;
Bool empty = TRUE;
Bool visited = FALSE;
WIN32_FIND_DATA findFileData;
HANDLE hFind;
if ((strlen(globalPathBuf) + strlen(root) + 3) >= MAXPATHLEN) {
return (1);
}
/* append root to globalPathBuf and remember where root starts and ends */
cp = globalPathBuf + strlen(globalPathBuf);
strcat(globalPathBuf, root);
cp2 = cp + strlen(root);
strcat(globalPathBuf, "/*"); /* add '/*' to get the list started */
hFind = FindFirstFile(globalPathBuf, &findFileData);
cp2[0] = '\0'; /* remove trailing "/*" */
if (hFind == INVALID_HANDLE_VALUE) {
saveError(80 + strlen(globalPathBuf),
"XUTL scan: opendir(%s) failed", globalPathBuf);
status = 1;
goto exit;
}
/* scan all files in the directory globalPathBuf */
do {
String fileName = findFileData.cFileName;
/* skip "." ".." */
if (fileName[0] == '.'
&& (fileName[1] == '\0'
|| (fileName[1] == '.' && fileName[2] == '\0'))) {
continue;
}
empty = FALSE;
/* skip hidden files, and anything fileSkip says to */
if (((flags & XUTL_FSALL) == 0 && (fileName[0] == '.'
|| (findFileData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)))
|| (fileSkip != NULL
&& fileSkip(arg, fileName, globalPathBuf, cp2) == TRUE)) {
continue;
}
visited = TRUE;
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
/* append cFileName to globalPathBuf */
if ((strlen(fileName) + (cp2 - globalPathBuf) + 1) >= MAXPATHLEN) {
status = 1; /* total name is too long, skip it */
continue;
}
strcat(globalPathBuf, "/");
strcat(globalPathBuf, fileName);
status |= XUTL_scanFS("", fileApply, fileSkip, arg, flags);
*cp2 = '\0'; /* remove cFileName suffix from globalPathBuf */
}
else {
/* fileName is an ordinary file */
status = fileApply(arg, &cookie, fileName, globalPathBuf);
if (status != 0) {
goto exit;
}
}
} while (FindNextFile(hFind, &findFileData));
if (empty == TRUE || (!visited && (flags & XUTL_FSNEDIR))) {
status = fileApply(arg, &cookie, "", globalPathBuf);
}
exit:
if (hFind != INVALID_HANDLE_VALUE) {
FindClose(hFind);
}
*cp = '\0'; /* remove root suffix from globalPathBuf */
return (status);
}
#else
#if defined(xdc_target__os_Linux) || defined(xdc_target__os_MacOS)
/*
* ======== XUTL_scanFS ========
*/
Int XUTL_scanFS(String root, XUTL_FileScanFxn fileApply,
XUTL_FileFilterFxn fileSkip, IArg arg, XUTL_FSFlags flags)
{
struct stat buf;
register DIR *fd;
register struct dirent *dp;
Int status = 0;
Char *cp, *cp2;
IArg cookie = (IArg)0;
Bool empty = TRUE;
Bool visited = FALSE;
if ((strlen(globalPathBuf) + strlen(root) + 3) >= MAXPATHLEN) {
return (1);
}
/* append root to globalPathBuf and remember where root starts and ends */
cp = globalPathBuf + strlen(globalPathBuf);
strcat(globalPathBuf, root);
cp2 = cp + strlen(root);
/* open globalPathBuf directory to scan its contents */
if ((fd = opendir(globalPathBuf)) == NULL) {
status = 1;
saveError(80 + strlen(globalPathBuf),
"XUTL scan: opendir(%s) failed", globalPathBuf);
*cp = '\0'; /* restore original globalPathBuf */
return (status);
}
/* scan all files in the directory globalPathBuf */
while ((dp = readdir(fd)) != NULL) {
if (dp->d_ino != 0) {
String fileName = dp->d_name;
/* skip "." ".." */
if (fileName[0] == '.'
&& (fileName[1] == '\0'
|| (fileName[1] == '.' && fileName[2] == '\0'))) {
continue;
}
empty = FALSE;
/* skip hidden files, and anything fileSkip says to */
if (((flags & XUTL_FSALL) == 0 && fileName[0] == '.')
|| (fileSkip != NULL
&& fileSkip(arg, fileName, globalPathBuf, cp2) == TRUE)) {
continue;
}
visited = TRUE;
/* append d_name to globalPathBuf so we can stat it */
if ((strlen(dp->d_name) + (cp2-globalPathBuf) + 1) >= MAXPATHLEN) {
status = 1; /* total name is too long, skip it */
continue;
}
strcat(globalPathBuf, "/");
strcat(globalPathBuf, dp->d_name);
if (lstat(globalPathBuf, &buf) < 0) {
/* EOVERFLOW can occur because of files that exceed 2GB.
* Since we don't expect package.* files to ever get this
* large, its ok to ignore this error.
*/
if (errno != EOVERFLOW) {
status = 1; /* something unexpected happened */
}
}
else if (((buf.st_mode & S_IFMT) == S_IFREG)
|| ((buf.st_mode & S_IFMT) == S_IFLNK)) {
*cp2 = '\0'; /* remove d_name suffix from globalPathBuf */
status = fileApply(arg, &cookie, dp->d_name, globalPathBuf);
if (status != 0) {
goto exit;
}
}
else if ((buf.st_mode & S_IFMT) == S_IFDIR) {
status |= XUTL_scanFS("", fileApply, fileSkip, arg, flags);
}
*cp2 = '\0'; /* remove d_name suffix from globalPathBuf */
}
}
if (empty == TRUE || (!visited && (flags & XUTL_FSNEDIR))) {
status = fileApply(arg, &cookie, "", globalPathBuf);
}
exit:
closedir(fd);
*cp = '\0'; /* remove root suffix from globalPathBuf */
return (status);
}
#endif
#endif
/*
* ======== XUTL_tmpFile ========
* This function is necessary because Windows creates temp files at the
* root of a drive; if this drive is a network mounted filesystem, the
* caller may not have access rights to create a file here!!!!
*/
FILE *XUTL_tmpFile(Void)
{
#if defined(xdc_target__os_Windows)
String tmp, tmpName;
FILE *file;
if ((tmp = tmpnam(NULL)) == NULL) {
return (NULL);
}
if (tmp[0] == '\\') { /* Windows adds '\\' on file names in the cwd!! */
tmp++;
}
if ((tmpName = (String)malloc(strlen(tmp) + 1)) == NULL) {
return (NULL);
}
strcpy(tmpName, tmp);
if ((file = fopen(tmpName, "w+b")) == NULL) {
free(tmpName);
return (NULL);
}
tmpNames[tmpNamesLen++] = tmpName;
return (file);
#else
return (tmpfile());
#endif
}
/*
* ======== XUTL_setDate ========
* if cmp == 0 dst date is set equal to src date
* if cmp == 1 dst date is to the max date of src and dst
* if cmp == -1 dst date is to the min date of src and dst
*/
Bool XUTL_setDate(String dst, String ref, Int cmp)
{
struct utimbuf rtbuf; /* time buffer */
struct utimbuf *rtp = NULL; /* time buffer pointer */
struct stat sbuf; /* file status buffer */
if (dst == NULL) {
saveError(80, "Invalid file argument: NULL");
return (FALSE);
}
/* if ref is specified, we use it to initialize rtbuf and rtp */
if (ref != NULL) {
/* use ref file's times to set dst's timestamp */
if (stat(ref, &sbuf) >= 0) {
rtbuf.modtime = sbuf.st_mtime;
rtbuf.actime = sbuf.st_atime;
rtp = &rtbuf; /* use rtbuf values to set time */
}
else {
saveError(80, "Can't stat reference file");
return (FALSE);
}
}
/* if dst file does not exist, we must create it */
if (access(dst, 00) != 0) {
FILE *file = fopen(dst, "wb");
if (file != NULL) {
fclose(file);
}
else {
saveError(80, "File create failed");
return (FALSE);
}
}
/* handle min/max date */
if (rtp != NULL && cmp != DATESAME) {
if (stat(dst, &sbuf) >= 0) {
if (cmp >= DATEMAX) {
if (rtp->modtime <= sbuf.st_mtime) {
return (TRUE);
}
}
else if (rtp->modtime >= sbuf.st_mtime) {
return (TRUE);
}
}
else {
saveError(80, "Can't stat file");
return (FALSE);
}
}
/* set time value according to the times referenced by rtp */
if (utime(dst, rtp) != 0) {
saveError(80, "Can't change file time");
return (FALSE);
}
return (TRUE);
}
/*
* ======== XUTL_getVersion ========
*/
String XUTL_getVersion(Void)
{
/* defined in this package's generated package/<pkg>.c file */
extern String xdc_services_host_lib__whatString;
/* skip over the "what string" prefix */
return (&xdc_services_host_lib__whatString[5]);
}
/*
* ======== XUTL_getXDCRoot ========
* Return the installation directory of the XDCtools
*
* This executable can be run from one of two places:
* 1. XDCROOT
* 2. XDCROOT/<some_repository>/<some_package>
*
* To determine the XDCROOT we first locate the directory containing
* progName. If this directory is the package providing this
* progName (e.g., xdc.services.host.bin), XDCROOT is the directory
* containing this package's repository; otherwise, XDCROOT is the
* directory containing this executable.
*/
String XUTL_getXDCRoot(String progName)
{
String pname;
String root;
Char rootDef[MAXNAME + 16]; /* +16 for "/packages" */
/* find where this executable lives */
if ((root = XUTL_getProgPath(progName)) == NULL) {
return (NULL);
}
/* as an optimization, first check for default root/packages */
sprintf(rootDef, "%s%cpackages", root, DIRSTR[0]);
if (XUTL_isDir(rootDef)) {
return (root);
}
/* check to see if root is the base of a package */
if ((pname = XUTL_getPackageName(root, FALSE)) != NULL) {
Char *cp;
/* make a copy of root; so stripPkgName can modify it */
rootDef[strlen(root)] = '\0';
/* strip <some_repository>/<some_package> from the copy of root */
if ((cp = stripPkgName(rootDef, pname)) != NULL) {
*cp = '\0'; /* strip the repository name from rootDef */
/* if stripPkgName succeeds, apply the changes to root */
root[strlen(rootDef)] = '\0';
}
}
if (pname != NULL) {
free(pname);
}
return (root);
}
/*
* ======== appendName ========
*/
static Bool appendName(String name, String **tab, Int *tabLen, Int *tabMax)
{
Char *ns;
String *nt = NULL;
if ((ns = (Char *)malloc((1 + strlen(name)) * sizeof (Char))) != NULL) {
strcpy(ns, name);
if (*tabLen < *tabMax) {
nt = *tab; /* there is room in the table for another string */
}
else { /* othrewise, we need to re-alloc the table */
size_t size = (*tabMax + TABQUANTUM) * sizeof (String);
if ((nt = (String *)realloc(*tab, size)) == NULL) {
free(ns);
goto exit;
}
*tabMax += TABQUANTUM;
*tab = nt;
}
nt[*tabLen] = ns;
*tabLen += 1;
return (TRUE);
}
exit:
saveError(80, "XUTL: Out of memory");
return (FALSE);
}
/*
* ======== cleanPath ========
* Cleanup path (in place) to be more canonical
*/
static Void cleanPath(String old)
{
Char *src, *dst;
/* replace '\\' with '/' */
for (src = old; *src != '\0'; src++) {
if (*src == '\\') {
*src = '/';
}
}
/* eliminate multiple '/'s */
if (*old != '\0') {
/* printf("cleaning '%s'\n", old); */
old++; /* don't eliminate '//' UNC prefix */
}
for (src = old, dst = old; *src != '\0'; src++) {
if (src[0] == '/') {
while (src[1] == '/') {
src++;
}
}
*dst++ = *src;
}
*dst = '\0';
/* TBD: remove embedded "/../" */
/* TBD: replace drive letters with lower case */
}
/*
* ======== copyAttrs ========
*/
static Void copyAttrs(String src, String dst)
{
struct stat sbuf; /* status buffer */
struct utimbuf tbuf; /* time buffer */
/* use stat() and chmod() to set permissions */
if (stat(src, &sbuf) >= 0) {
chmod(dst, sbuf.st_mode);
/* use utime() to set timestamps */
tbuf.modtime = sbuf.st_mtime;
tbuf.actime = sbuf.st_atime;
utime(dst, &tbuf);
}
}
/*
* ======== isAtOption ========
*/
#define isAtOption(s) ( (s)[0] == '@' || ((s)[0] == '-' && (s)[1] == '@') )
/*
* ======== expandArgv ========
* Expand @file options specified by in and create new option arrays
*/
static Bool expandArgv(ArgvDesc *in, ArgvDesc *out, String *err)
{
Int i, tmp;
Int atCount = 0;
ArgvDesc atArgs[MAXARGS];
Bool recurse = FALSE;
/* initialize output parameters */
out->argv = NULL;
out->argc = 0;
*err = NULL;
/* scan in->argv for @file options and expand them */
for (i = 1; i < in->argc; i++) {
if (isAtOption(in->argv[i])) {
String fname = in->argv[i] + (in->argv[i][0] == '-' ? 2 : 1);
if (atCount >= MAXARGS) {
*err = "too many arguments";
return (FALSE);
}
/* expand @file into an ArgvDesc */
if (mkArgv(fname, &atArgs[atCount++], err) == FALSE) {
return (FALSE);
}
}
}
/* compute the size of the final in->argv array */
out->argc = in->argc - atCount;
for (i = 0; i < atCount; i++) {
out->argc += atArgs[i].argc;
}
/* allocate new argv array */
out->argv = (String *)malloc(out->argc * sizeof(String));
if (out->argv == NULL) {
out->argc = 0;
*err = "out of memory";
return (FALSE);
}
/* copy args into new array */
for (atCount = tmp = i = 0; i < in->argc; i++) {
if (isAtOption(in->argv[i])) {
ArgvDesc *ap = &atArgs[atCount++];
Int j;
for (j = 0; j < ap->argc; j++) {
String opt = ap->argv[j];
out->argv[tmp++] = opt;
if (isAtOption(opt)) {
recurse = TRUE;
}
}
free(ap->argv);
}
else {
out->argv[tmp++] = in->argv[i];
}
}
return (recurse);
}
typedef struct FindObj {
Bool buildable;
String **tab;
Int *tabLen;
Int *tabMax;
} FindObj;
/*
* ======== fileFxn ========
*/
static Int fileFxn(IArg arg, IArg *cookie, String base, String fullPath)
{
FindObj *obj = (FindObj *)arg;
Int status = 0;
if (*cookie < 0) { /* we've already added fullPath */
return (0);
}
if (strcmp(base, "package.xdc") == 0) {
if (!obj->buildable || *cookie > 0) {
// printf("adding %s\n", fullPath);
if (!appendName(fullPath, obj->tab, obj->tabLen, obj->tabMax)) {
status = 1;
}
*cookie = -2; /* ensure we add fullPath just once */
}
else {
*cookie = *cookie + 1;
}
}
else if (strcmp(base, "package.bld") == 0) {
if (*cookie > 0) {
// printf("adding %s\n", fullPath);
if (!appendName(fullPath, obj->tab, obj->tabLen, obj->tabMax)) {
status = 1;
}
*cookie = -2; /* ensure we add fullPath just once */
}
else {
*cookie = *cookie + 1;
}
}
return (status);
}
/*
* ======== skipFxn ========
*/
static Bool skipFxn(IArg obj, String base, String fullPath, Char *end)
{
Int len = end - fullPath;
Bool result = FALSE;
/* skip "package" if we are in a package */
if (strcmp(base, "package") == 0) {
/* append "/package.xdc" so we can check for it's existance */
if ((sizeof("/package.xdc") + len + 1) < MAXPATHLEN) {
strcat(fullPath, "/package.xdc");
result = (access(fullPath, 00) == 0) ? TRUE : FALSE;
*end = '\0'; /* remove package.xdc */
// printf("skip %s/%s: %s\n", fullPath, base, result ? "TRUE" : "FALSE");
}
}
return (result);
}
/*
* ======== find ========
*/
static Int find(String root, Bool buildable,
String **tab, Int *tabLen, Int *tabMax)
{
FindObj findObj;
findObj.buildable = buildable;
findObj.tab = tab;
findObj.tabLen = tabLen;
findObj.tabMax = tabMax;
return (XUTL_scanFS(root, fileFxn, skipFxn, (IArg)&findObj, XUTL_FSNORM));
}
/*
* ======== tryPath ========
* Last resort to find a package's repository.
*
* pname is the name of a package and cdir is the canonical path to
* the base of this package. But the name specified in package.xdc
* does *not* match cdir. As a last resort we try looking along
* XDCPATH for the pname and *if* we find cdir, we return the package's
* repository (the component of XDCPATH that allowed us to find pname).
*
* The method frees cdir and modifies pname.
*
* This method returns NULL if pname can not be found along XDCPATH;
* otherwise it returns a string that must be freed via free().
*/
static String tryPath(String pname, String cdir)
{
Char nameBuf[MAXPATHLEN + 1];
String pathBuf;
Int len;
String path = getenv("XDCPATH");
String result = NULL;
Char *cp;
/* nothing to look along, so we have to give up */
if (path == NULL) {
goto exit;
}
/* if the nameBuf is too small we fail */
if ((len = strlen(pname)) > MAXPATHLEN) {
saveError(80 + len, "Package name '%s' too long", pname);
goto exit;
}
/* convert pname into directory name (in place!!!) */
for (cp = pname; *cp != '\0'; cp++) {
if (cp[0] == '.') {
cp[0] = DIRSTR[0];
}
}
/* otherwise, try every prefix in path */
if ((pathBuf = (Char *)malloc(strlen(path) + 1)) != NULL) {
String tmp;
Bool done;
strcpy(pathBuf, path);
tmp = strtok(pathBuf, PATHSEP);
for (done = FALSE; !done && tmp != NULL; tmp = strtok(NULL, PATHSEP)) {
if ((len + 1 + strlen(tmp)) <= MAXPATHLEN) {
sprintf(nameBuf, "%s%c%s", tmp, DIRSTR[0], pname);
if (isPkg(nameBuf)) {
/* found pname along path, now is it the right one? */
String cname = XUTL_getCanonicalPath(nameBuf);
if (cname != NULL) {
if (strcmp(cname, cdir) == 0) {
/* found result along package path! */
result = XUTL_getCanonicalPath(tmp);
done = TRUE;
}
free(cname);
}
}
}
}
free(pathBuf);
}
exit:
if (result == NULL) {
saveError(80 + strlen(cdir),
"Package name in %s/package.xdc doesn't match its directory", cdir);
}
free(cdir);
return (result);
}
/*
* ======== getPackageName ========
* Read the package name from the package.xdc file in the directory
* specified by dname.
*/
static String getPackageName(String dname)
{
FILE *pfile;
static char lineBuf[MAXLINE + 32]; /* +32 to allow + "/package.xdc" */
String name = NULL;
if (strlen(dname) >= MAXLINE) {
saveError(80, "directory path too long");
return (NULL);
}
sprintf(lineBuf, "%s/package.xdc", dname);
if ((pfile = fopen(lineBuf, "r")) == NULL) {
saveError(80 + strlen(dname), "Can't open %s/package.xdc", dname);
return (NULL);
}
while (fgets(lineBuf, sizeof(lineBuf), pfile) != NULL) {
Char *cp;
String token = strtok(lineBuf, " \t\r\n");
if (token != NULL && strcmp(token, "package") == 0) {
if ((name = strtok(NULL, " \t\n\r")) == NULL) {
lineBuf[0] = '\0';
name = lineBuf;
}
if (!(isalnum(name[0]) || name[0] == '_')) {
name = ""; /* unnamed package */
}
else if ((cp = strpbrk(name, "[{")) != NULL) {
*cp = '\0';
}
break;
}
}
fclose(pfile);
if (name == NULL) {
saveError(80 + strlen(dname), "Can't parse %s/package.xdc", dname);
}
return (name);
}
/*
* ======== getRepository ========
* Return (../)^n where n is the number of components in the package
* named name.
*/
static String getRepository(String name)
{
Char topBuf[MAXLINE + 1] = {'\0'};
Int len = 3;
if (name[0] == '\0') {
strcpy(topBuf, "..");
}
else {
String token = strtok(name, ".");
String prefix = "";
topBuf[0] = '\0';
for (len = 3; token != NULL; token = strtok(NULL, "."), len += 3) {
if (len >= MAXLINE) {
saveError(80, "repository path too long");
return (NULL);
}
strcat(topBuf, prefix);
strcat(topBuf, "..");
prefix = "/";
}
}
return (newString(topBuf));
}
/*
* ======== getXMLine ========
* Read one XML line at a time from in stream.
*
* Return FALSE at end of FILE (or error)
*/
static Bool getXMLine(Char *buffer, Int size, FILE *in)
{
Char c;
/* skip to first '<' character */
while ((c = fgetc(in)) != EOF && c != '<') {
;
}
/* copy into buffer until first '>' character */
if (c != EOF) {
Int len = 0;
while ((len < size - 1) && (c = fgetc(in)) != EOF && c != '>') {
if (c != '\r' && c != '\n') {
*buffer++ = c;
len++;
}
}
*buffer = '\0'; /* ensure buffer is a C string */
return (TRUE);
}
return (FALSE); /* indicate EOF */
}
/*
* ======== getXMLValue ========
*/
static String getXMLValue(String pair)
{
String value = NULL;
Char *start, *end;
if ((start = strchr(pair, '"')) != NULL) {
start++;
end = strchr(start, '"');
if (end != NULL) {
Int len = end - start;
if ((value = malloc(len + 1)) != NULL) {
strncpy(value, start, len);
value[len] = '\0';
}
}
}
return (value);
}
/*
* ======== isAbsolute ========
* Return TRUE iff name is an absolute path
*/
static Bool isAbsolute(String name)
{
if (name[0] == '/' || name[0] == '\\') {
return (TRUE);
}
if (name[0] != '\0' && name[1] == ':') {
if (name[2] == '/' || name[2] == '\\') {
return (TRUE);
}
}
return (FALSE);
}
/*
* ======== XUTL_isFile ========
* Return TRUE iff name exists and is not a directory
*/
Bool XUTL_isFile(String name)
{
struct stat buf;
if (stat(name, &buf) == 0 && !S_ISDIR(buf.st_mode)) {
return (TRUE);
}
return (FALSE);
}
/*
* ======== XUTL_mkdir ========
* Create the specified directory if it doesn't already exist
*/
Bool XUTL_mkdir(String name)
{
Char tmp;
Char *cp;
String dirs;
Int offset;
/* printf("XUTL_mkdir('%s')\n", name); */
if (XUTL_isDir(name)) {
return (TRUE); /* nothing to do */
}
/* create a modifiable copy of name */
if ((dirs = strdup(name)) == NULL) {
return (FALSE);
}
/* compute starting offset for absolute paths */
offset = 0;
if (dirs[0] == '\\' || dirs[0] == '/') {
offset = 1;
}
else if (dirs[0] != '\0' && dirs[1] == ':') {
/* handle Windows drive letters */
offset = 2;
if (dirs[2] == '\\' || dirs[2] == '/') {
offset = 3;
}
}
/* try each dir prefix and create any that don't exist */
for (cp = dirs + offset; *cp != '\0'; cp++) {
if (*cp == '\\' || *cp == '/') {
tmp = *cp;
*cp = '\0';
if (XUTL_isDir(dirs) != TRUE) {
if (mkDir(dirs) == FALSE) {
free(dirs);
return (FALSE);
}
}
for (*cp++ = tmp; *cp == '/' || *cp == '\\'; cp++);
}
}
free(dirs);
/* create the directory */
return (mkDir(name));
}
/*
* ======== isPkg ========
* Return TRUE iff name exists, is a directory, and contains a file
* named "package.xdc".
*/
static Bool isPkg(String name)
{
Bool status = FALSE;
if (XUTL_isDir(name)) {
String file = (String)malloc(strlen(name) + 32);
if (file != NULL) {
strcpy(file, name);
strcat(file, "/package.xdc");
if (XUTL_isFile(file)) {
status = TRUE;
}
free(file);
}
}
return (status);
}
/*
* ======== getLine ========
*/
#define LINEQUANTUM 512
static String getLine(FILE *file)
{
String lineBuf;
Int lineBufLen;
Char *cp;
Int max;
Bool empty = TRUE;
/* allocate initial line buffer */
lineBufLen = LINEQUANTUM;
if ((lineBuf = (String)malloc(lineBufLen * sizeof (Char))) == NULL) {
return (NULL);
}
lineBuf[0] = '\0';
/* read from file into lineBuf until we get end-of-line */
cp = lineBuf;
max = lineBufLen;
while (fgets(cp, max, file) != NULL) {
Int len = strlen(lineBuf);
empty = FALSE;
if (lineBuf[len - 1] == '\n') { /* reached EOL */
lineBuf[len - 1] = '\0'; /* trim newline char and stop */
break;
}
else if (len < (lineBufLen - 1)) { /* reached EOF */
break; /* last line is missing new line */
}
else {
/* don't have a full line yet, need to grow line buffer */
String tmp;
lineBufLen += LINEQUANTUM;
tmp = (Char *)realloc(lineBuf, lineBufLen * sizeof (Char));
if (tmp == NULL) {
free(lineBuf);
return (NULL);
}
lineBuf = tmp;
}
cp = lineBuf + len;
max = lineBufLen - (cp - lineBuf);
}
if (empty) {
free(lineBuf);
return (NULL);
}
return (lineBuf);
}
/*
* ======== mkArgv ========
*/
static Bool mkArgv(String fileName, ArgvDesc *desc, String *err)
{
FILE *file;
String line;
static String argv[MAXARGS];
Int argc;
*err = NULL;
if ((file = fopen(fileName, "r")) == NULL) {
*err = "can't open argument file";
return (FALSE);
}
for (argc = 0; argc < MAXARGS;) {
if ((line = getLine(file)) == NULL) {
break;
}
argv[argc++] = line;
}
if (feof(file) == 0) {
*err = "too many arguments";
}
fclose(file);
if (*err != NULL) {
goto fail;
}
else if ((desc->argv = malloc(argc * sizeof (String))) == NULL) {
*err = "out of memory";
goto fail;
}
desc->argc = argc;
memcpy(desc->argv, argv, argc * sizeof (String));
return (TRUE);
fail:
for (--argc; argc >= 0; argc--) {
if (argv[argc] != NULL) {
free(argv[argc]);
}
}
return (FALSE);
}
/*
* ======== mkDir ========
*/
static Bool mkDir(String dir)
{
/* printf("mkDir('%s')\n", dir); */
#if defined(xdc_target__os_Windows)
if (_mkdir(dir) < 0) {
/* perror(" _mkdir failed"); */
return (FALSE);
}
#else
if (mkdir(dir, (mode_t)0755) < 0) {
return (FALSE);
}
#endif
return (TRUE);
}
/*
* ======== newString ========
* Create new string converting all '\\' path characters to '/' and
* removing all duplicate '/'s
*/
static String newString(String old)
{
Char *ns;
if ((ns = (Char *)malloc((1 + strlen(old)) * sizeof (Char))) != NULL) {
strcpy(ns, old);
cleanPath(ns);
}
return (ns);
}
/*
* ======== saveError ========
*/
static Void saveError(Int len, String format, ...)
{
va_list va;
va_start(va, format);
if (len < sizeof(lastErrorString)) {
vsprintf(lastErrorString, format, va);
}
else if (strlen(format) < sizeof(lastErrorString)) {
sprintf(lastErrorString, "%s", format);
}
else {
sprintf(lastErrorString, "XUTL: error string too long");
}
va_end(va);
}
/*
* ======== stripPkgName ========
* Update root by stripping the package directories from the end.
*
* Returns a pointer to the first character of the last directory
* containing the package. For example, if
* root = /db/ztree/iliad/packages/xdc/runtime/, and
* pname = xdc.runtime
* then stripPkgName() sets root to "/db/ztree/iliad/packages"
* and returns a pointer to "packages" inside of root.
*/
static String stripPkgName(String root, String pname)
{
Char *cp;
String token;
cp = root + strlen(root) - 1;
do {
/* get next package token to match */
token = strrchr(pname, '.');
if (token == NULL) {
token = pname;
}
else {
token++;
}
/* strip trailing directory characters */
while (cp > root && (*cp == '/' || *cp == '\\')) {
*cp-- = '\0';
}
/* find the start of the last directory in root */
while (cp > root && (*cp != '/' && *cp != '\\')) {
cp--;
}
if (strcmp(cp + 1, token) != 0) {
return (NULL); /* package name and directory names don't match */
}
/* remove token from root and pname */
*cp = '\0';
if (token != pname) {
token[-1] = '\0'; /* -1 needed to remove leading '.' */
}
} while (token != pname);
/* strip trailing directory characters */
while (cp > root && (*cp == '/' || *cp == '\\')) {
*cp-- = '\0';
}
/* find the start of the repository in root */
while (cp > root && (*cp != '/' && *cp != '\\')) {
cp--;
}
return (cp);
}
/*
* ======== tmpCleanup ========
*/
static Void tmpCleanup(Void)
{
Int i;
for (i = 0; i < tmpNamesLen; i++) {
remove(tmpNames[i]);
free(tmpNames[i]);
}
}