/*
    A rather quick hack to open a file via a path -- could be made
    more general.  Would prefer to use a standard one out of a library
    but there doesn't seem to be one...
      Seems to cover all the bases -- elements of path may end in '/' if
    that's your style; absolute (rooted) filenames do not go via path,
    and '.' works OK.  I would prefer not to do the open() in this code
    but I suppose I have to worry about efficiency. [I would prefer not
    to do the open because I might want to find a file for reading via
    FILE * instead, or for some other reason than opening it.]

    pathopen(
       "PATHNAME",                    Open a dictionary file via PATHNAME
       default path,                  what to use if PATHNAME not found
       include file name,             eg "linux.inc", or "math" etc.
       extension,                     "inc" for an include file at the moment.
       &expanded name)                Fills in full name with path and extension.
                                   -> int file descriptor or -1 for not found.

     If you want to do a FILE * open, do the pathopen first, and if it succeeds,
     either close the fd and reopen a FILE * using the updated filename, *or*
     use fdopen on the returned fd. ( FILE *fdopen(int fd, const char *mode); )
     *HOWEVER* fdopen of an fd opened for write does *not* reset the file
     the way fopen with "w" does, so is unwise to use if writing to an
     existing file.  Fortunately this is not an issue for reading include files.
 */

#define _POSIX_C_SOURCE 200809L
#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h> // for close()
#include <string.h>

int     pathopen_debug = (0 == 0);

/* Generic Unix thingummybobs - could put inline but it's easier
   for me to test at home on my Archimedes if I make it portable... */

#define DIRSEP "/"
#define EXTSEPCH '.'
#define PATHSEPCH ':'
#define ROOT "/"
#define HOME "."		/* To catch non-path names ./fred & ../fred */
#define HERE "./"

int     getpath_debug = (0 != 0);	/* May be tweaked by debugger */

static int absolute(char *fname) {				/* Don't look in path if absolute filename
				 * given */
	/* This could easily be a macro, but I've made it a function so that
	 * it can more easily be expanded later.  (For instance, on the
	 * Archimedes it is quite a hassle to work out whether a filename is
	 * absolute...) */
	if (strncmp(fname, ROOT, strlen(ROOT)) == 0)
		return (0 == 0);
	if (strncmp(fname, HOME, strlen(HOME)) == 0)
		return (0 == 0);
	return (0 != 0);
}

int pathopen(char *pathname, char *defpath, char *basename, char *ext, char **fullnamep) {
#define NULLEXIT(p) if (p == NULL) exit(EXIT_FAILURE); else	/* OK */
#define fullname (*fullnamep)

	char   *dictp = NULL, *dictpath = NULL;
	char   *sep = NULL, *place = NULL;
	char   *filename = NULL;
	char   *savepath = NULL, *savefile = NULL;
	int     f = -1;

	dictp = "";
	if (absolute(basename) || (dictp = getenv(pathname)) != NULL) {
		savepath = dictpath = malloc(strlen(dictp) + 1);
		NULLEXIT(dictpath);
		strcpy(dictpath, dictp);
	} else {
		savepath = dictpath = malloc(strlen(defpath) + 1);
		NULLEXIT(dictpath);
		strcpy(dictpath, defpath);
	}

	for (;;) {
		sep = strchr(dictpath, PATHSEPCH);
		if (sep == NULL) {	/* last element of path */
			place = dictpath;
			dictpath = dictpath + strlen(dictpath);
		} else {	/* More to come */
			place = dictpath;
			*sep = '\0';
			dictpath = sep + 1;
		}
		if (*place == '\0') {	/* "/usr/share/dict:/usr/dict:" --
					 * last ":" implies current dir */
			savefile = filename = malloc(strlen(basename) + 1);
			NULLEXIT(filename);
			strcpy(filename, basename);
		} else {	/* Use given path.  Allow it to be of the form
				 * "/usr/dict/" */
                  if (*place == '~') {
                    char newplace[1024];
                    sprintf(newplace, "%s%s", getenv("HOME")?:"", place+1);
                    place = strdup(newplace);
                  }
                  //fprintf(stderr, "Looking in %s\n", place);
			if (strncmp(place + strlen(place) - strlen(DIRSEP), DIRSEP, strlen(DIRSEP)) == 0) {
				savefile = filename = malloc(strlen(place) + strlen(basename) + 1);
				NULLEXIT(filename);
				sprintf(filename, "%s%s", place, basename);
			} else {
				savefile = filename = malloc(strlen(place) + strlen(DIRSEP) + strlen(basename) + 1);
				NULLEXIT(filename);
				sprintf(filename, "%s%s%s", place, DIRSEP, basename);
			}
		}
		if (strncmp(filename, HERE, strlen(HERE)) == 0) {	/* For tidyness, strip
									 * leading "./" */
			filename += strlen(HERE);
		}
		if (strrchr(filename, EXTSEPCH) == NULL && ext != NULL) {
			/* If no extension given, add default */
			char   *fileext = malloc(strlen(filename) + 1 + strlen(ext) + 1);
			NULLEXIT(fileext);
			sprintf(fileext, "%s%c%s", filename, EXTSEPCH, ext);
			free(savefile);
			savefile = filename = fileext;
		}
		if (getpath_debug)
			if (pathopen_debug)
				fprintf(stderr, "Looking for %s\n", filename);
		if ((f = open(filename, O_RDONLY)) != -1) {
			fullname = filename;	/* Returns a mallocked string */
			free(savepath);
			return (f);	/* return the fd of the file */
		}
		if (sep == NULL && *dictpath == '\0')
			break;
	}

	free(savefile);
	free(savepath);
	fullname = NULL;

	return (-1);		/* could not open the file */
#undef NULLEXIT
#undef fullname
}

// Only used if environment variable doesn't exist.
#define IMP_INC_PATH "/usr/local/include/i2c:."

char *fullpath(char *filename) {
  char   *fullname;
  int     fd;
  fd = pathopen("IMP_INC_PATH", IMP_INC_PATH, filename, "inc", &fullname);
  //fprintf(stderr, "pathopen(\"IMP_INC_PATH\", \"%s\", \"%s\", \"inc\", -> ", getenv("IMP_INC_PATH") ?: IMP_INC_PATH, filename);
  if (fd != -1) {
    //fprintf(stderr, "\"%s\") -> %d\n", fullname, fd);
    close(fd);
    return strdup(fullname);
  } else {
    //fprintf(stderr, "NULL) -> -1\n");
    return strdup(filename); // already an imp string
  }
}

#ifdef DBMAIN

int main(int argc, char **argv) {
	char   *fullname;
	int     fd;
	if (argc != 2) {
		fprintf(stderr, "syntax: pathopen includefile\n");
		exit(EXIT_FAILURE);
	}
	getpath_debug = (0 == 0);
	fd = pathopen("IMP_INC_PATH", IMP_INC_PATH, argv[1], "inc", &fullname);
	fprintf(stderr, "pathopen(\"IMP_INC_PATH\", \"%s\", \"%s\", \"inc\", -> ", IMP_INC_PATH, argv[1]);
	if (fd != -1) {
		fprintf(stderr, "\"%s\") -> %d\n", fullname, fd);
		free(fullname);
	} else {
		fprintf(stderr, "NULL) -> -1\n");
	}
}
#endif				/* DBMAIN */
