/* filename.c - follow symbolic links in a filename
 */

#include <string.h>
#include <unistd.h>
#include "mstring.h"
#include "ldb.h"


/* Turn "file" into an absolute pathname, by prepending the CWD if it
 * doesn't begin with "/", and then stripping out all occurrences of "/./"
 * and "/../". "//" is stripped as well.
 *
 * All symbolic links are followed and resolved with readlink(2).
 *
 * Returns a pointer to the filename buffer, which contains the altered
 * version of the original filename.
 *
 * Note that ldb_lbuf, the LDB internal line buffer, is used. So is
 * ldb_fbuf.
 */
char *ldb_filename(char *file)
{
	char *a;
	char b;
	int n, l, i, j;
	char foundlink;

	if (!file)
		return ("");

	ldb_fbuf[0] = 0;

	if (file[0] != '/') {
		getcwd(ldb_fbuf, 1024);
		strcat(ldb_fbuf, "/");
	}

	strcat(ldb_fbuf, file);

	do {
		foundlink = 0;
		i = 0;
		do {
			i++;
			j = i;
			for (n = 0;
			     (ldb_fbuf[i] != '/') && (ldb_fbuf[i] != 0);
			     i++)
				n++;
			b = ldb_fbuf[i];
			ldb_fbuf[i] = 0;

			l = readlink(ldb_fbuf, ldb_lbuf, 1024);
			ldb_fbuf[i] = b;
			if (l <= 0)
				continue;

			foundlink = 1;

			ldb_lbuf[l] = 0;

			if (ldb_lbuf[0] == '/') {	/* fully specified symlink */
				mstrdelete(ldb_fbuf, 0, j + n);
				i = 0;
				mstrinsert(ldb_fbuf, ldb_lbuf, &i, 1024);
				i = -1;
				continue;
			}

			i = j;
			mstrdelete(ldb_fbuf, i, n);	/* remove symlink from path */
			mstrinsert(ldb_fbuf, ldb_lbuf, &i, 1024);	/* replace with link content */

		} while ((i >= 0) && (ldb_fbuf[i] != 0));

	} while (foundlink);

	while ((a = strstr(ldb_fbuf, "/./")))
		mstrdelete(a, 0, 2);
	while ((a = strstr(ldb_fbuf, "//")))
		mstrdelete(a, 0, 1);

	while ((a = strstr(ldb_fbuf, "/../"))) {
		mstrdelete(a, 0, 3);
		a--;
		n = 1;
		while ((a >= ldb_fbuf) && (a[0] != '/')) {
			a--;
			n++;
		}
		if (a >= ldb_fbuf)
			mstrdelete(a, 0, n);
	}

	return (ldb_fbuf);
}

/* EOF */
