/*
    scconfig - detection of standard library features: file system specific calls
    Copyright (C) 2010  Tibor Palinkas

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

		Project page: http://repo.hu/projects/scconfig
		Contact via email: scconfig [at] igor2.repo.hu
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "libs.h"
#include "log.h"
#include "db.h"
#include "dep.h"

int find_fs_realpath(int logdepth, int fatal)
{
	char *test_c =
		NL "#include <limits.h>"
		NL "#include <stdlib.h>"
		NL "#include <stdio.h>"
		NL "int main() {"
		NL "#ifdef PATH_MAX"
		NL "	char out[PATH_MAX];"
		NL "#else"
		NL "	char out[32768];"
		NL "#endif"
		NL "	if (realpath(\".\", out) == out)"
		NL "		puts(\"OK\");"
		NL "	return 0;"
		NL "}"
		NL;

	require("cc/cc", logdepth, fatal);

	report("Checking for realpath()... ");
	logprintf(logdepth, "find_fs_realpath: trying to find realpath...\n");
	logdepth++;

	if (try_icl(logdepth, "libs/fs/realpath", test_c, NULL, NULL, NULL)) return 0;
	if (try_icl(logdepth, "libs/fs/realpath", test_c, "#define _BSD_SOURCE", NULL, NULL)) return 0;
	return try_fail(logdepth, "libs/fs/realpath");
}


int find_fs_readdir(int logdepth, int fatal)
{
	char *test_c =
		NL "#include <stdlib.h>"
		NL "#include <stdio.h>"
		NL "int main() {"
		NL "	DIR *dirp;"
		NL "	struct dirent *dp;"
		NL "	int found = 0;"
		NL "	if ((dirp = opendir(\".\")) == 0)"
		NL "		return -1;"
		NL "	while ((dp = readdir(dirp)) != 0)"
		NL "		if (strcmp(dp->d_name, \"configure\") == 0)"
		NL "			found++;"
		NL "	closedir(dirp);"
		NL "	if (found == 1)"
		NL "		puts(\"OK\");"
		NL "	return 0;"
		NL "}";
	char *includes[] = {
		"#include <dirent.h>",
		"#include <sys/dir.h>",    /* 4.2BSD */
		NULL
	};
	char **i;

	require("cc/cc", logdepth, fatal);

	report("Checking for readdir()... ");
	logprintf(logdepth, "find_fs_readdir: trying to find readdir...\n");
	logdepth++;

	for (i = includes; *i != NULL; i++)
		if (try_icl(logdepth, "libs/fs/readdir", test_c, *i, NULL, NULL))
			return 0;
	return try_fail(logdepth, "libs/fs/readdir");
}


int find_fs_findnextfile(int logdepth, int fatal)
{
	char *test_c =
		NL "#include <stdlib.h>"
		NL "#include <stdio.h>"
		NL "#include <windows.h>"
		NL "int main(int argc, char *argv[]) {"
		NL "	WIN32_FIND_DATA fd;"
		NL "	HANDLE h;"
		NL "	int found=0;"
		NL "	h = FindFirstFile(argv[0], &fd);"
		NL "	if (h == INVALID_HANDLE_VALUE)"
		NL "		return -1;"
		NL "	while (FindNextFile(h, &fd) != 0);"
		NL "		found++;"
		NL "	FindClose(h);"
		NL "	if (found > 0)"
		NL "		puts(\"OK\");"
		NL "	return 0;"
		NL "}";

	require("cc/cc", logdepth, fatal);

	report("Checking for FindNextFile()... ");
	logprintf(logdepth, "find_fs_findnextfile: trying to find FindNextFile...\n");
	logdepth++;

	if (try_icl(logdepth, "libs/fs/findnextfile", test_c, NULL, NULL, NULL)) return 0;
	return try_fail(logdepth, "libs/fs/findnextfile");
}


int find_fs_stat_macros(int logdepth, int fatal)
{
	char *test_c_templ =
		NL "#include <sys/stat.h>"
		NL "#include <sys/types.h>"
		NL "int main() {"
		NL "	int a = %s(0);"
		NL "		puts(\"OK\");"
		NL "	return 0;"
		NL "}"
		NL;
	char test_c[256];

	char *names[][3] = {
		{"S_ISREG",  "S_IFREG",  NULL},
		{"S_ISDIR",  "S_IFDIR",  NULL},
		{"S_ISCHR",  "S_IFCHR",  NULL},
		{"S_ISBLK",  "S_IFBLK",  NULL},
		{"S_ISFIFO", "S_IFFIFO", NULL},
		{"S_ISLNK",  "S_IFLNK",  NULL},
		{"S_ISCHR",  "S_IFCHR",  NULL},
		{"S_ISSOCK", "S_IFSOCK", NULL},
		{NULL,       NULL,       NULL}
		};
	char **n;
	int name, pr;
	char nodename[128];

	require("cc/cc", logdepth, fatal);

	report("Checking for stat macros:\n");
	logprintf(logdepth, "find_fs_stat_macros: trying to find stat macros...\n");
	logdepth++;

	pr = 0;
	for(name = 0; *names[name] != NULL; name++) {
		report(" %s...\t", names[name][0]);
		for(n = &names[name][0]; *n != NULL; n++) {
			sprintf(test_c, test_c_templ, *n);
			if (try_icl(logdepth, NULL, test_c, NULL, NULL, NULL)) {
				sprintf(nodename, "libs/fs/stat/macros/%s", names[name][0]);
				put(nodename, *n);
				report("found as %s\n", *n);
				pr++;
				goto found;
			}
		}
		report("not found\n");
		found:;
	}

	put("libs/fs/stat/macros/presents", ((pr > 0) ? (strue) : (sfalse)));
	return (pr == 0);
}

int find_fs_getcwd(int logdepth, int fatal)
{
	char *test_c =
		NL "#include <unistd.h>"
		NL "int main() {"
		NL "	char b[1024];"
		NL "	if (getcwd(b, sizeof(b)) != NULL)"
		NL "		puts(\"OK\");"
		NL "	return 0;"
		NL "}"
		NL;

	require("cc/cc", logdepth, fatal);

	report("Checking for getcwd... ");
	logprintf(logdepth, "find_fs_getcwd: trying to find getcwd()...\n");
	logdepth++;

	if (try_icl(logdepth, "libs/fs/getcwd", test_c, NULL, NULL, NULL)) return 0;
	return try_fail(logdepth, "libs/fs/getcwd");
}

int find_fs__getcwd(int logdepth, int fatal)
{
	char *test_c =
		NL "#include <stdlib.h>"
		NL "int main() {"
		NL "	char b[1024];"
		NL "	if (_getcwd(b, sizeof(b)) != NULL)"
		NL "		puts(\"OK\");"
		NL "	return 0;"
		NL "}"
		NL;

	require("cc/cc", logdepth, fatal);

	report("Checking for _getcwd... ");
	logprintf(logdepth, "find_fs__getcwd: trying to find _getcwd()...\n");
	logdepth++;

	if (try_icl(logdepth, "libs/fs/_getcwd", test_c, "#include <direct.h>", NULL, NULL)) return 0;
	return try_fail(logdepth, "libs/fs/_getcwd");
}


int find_fs_getwd(int logdepth, int fatal)
{
	char *test_c =
		NL "#include <unistd.h>"
		NL "int main() {"
		NL "	char b[8192];"
		NL "	if (getwd(b) != NULL)"
		NL "		puts(\"OK\");"
		NL "	return 0;"
		NL "}"
		NL;

	require("cc/cc", logdepth, fatal);

	report("Checking for getwd... ");
	logprintf(logdepth, "find_fs_getwd: trying to find getwd()...\n");
	logdepth++;

	if (try_icl(logdepth, "libs/fs/getwd", test_c, NULL, NULL, NULL)) return 0;
	return try_fail(logdepth, "libs/fs/getwd");
}

int find_fs_mkdir(int logdepth, int fatal)
{
	char *dir;
	char test_c[1024];
	char *test_c_in =
		NL "#include <stdio.h>"
		NL "int main() {"
		NL "	if (mkdir(\"%s\"%s) == 0)"
		NL "		puts(\"OK\");"
		NL "	return 0;"
		NL "}"
		NL;

	require("cc/cc", logdepth, fatal);

	dir = tempfile_new("");
	unlink(dir);

	report("Checking for mkdir... ");
	logprintf(logdepth, "find_fs_mkdir: trying to find mkdir()...\n");
	logdepth++;

	/* POSIX, 2 arguments, standard includes */
	sprintf(test_c, test_c_in, dir, ", 0755");
	if (try_icl(logdepth, "libs/fs/mkdir", test_c, "#include <sys/types.h>\n#include <sys/stat.h>\n", NULL, NULL)) {
		if (!is_dir(dir))
			goto oops1;
		put("libs/fs/mkdir/num_args", "2");
		rmdir(dir);
		return 0;
	}

	/* POSIX, 2 arguments, no includes */
	oops1:;
	sprintf(test_c, test_c_in, dir, ", 0755");
	if (try_icl(logdepth, "libs/fs/mkdir", test_c, NULL, NULL, NULL)) {
		if (!is_dir(dir))
			goto oops2;
		put("libs/fs/mkdir/num_args", "2");
		rmdir(dir);
		return 0;
	}

	oops2:;
	put("libs/fs/mkdir/includes", "");
	put("libs/fs/mkdir/ldflags", "");
	put("libs/fs/mkdir/cdflags", "");

	rmdir(dir);
	return try_fail(logdepth, "libs/fs/mkdir");
}

int find_fs__mkdir(int logdepth, int fatal)
{
	char *dir;
	char test_c[1024];
	char *test_c_in =
		NL "#include <stdio.h>"
		NL "int main() {"
		NL "	if (_mkdir(\"%s\"%s) == 0)"
		NL "		puts(\"OK\");"
		NL "	return 0;"
		NL "}"
		NL;

	require("cc/cc", logdepth, fatal);

	dir = tempfile_new("");
	unlink(dir);

	report("Checking for _mkdir... ");
	logprintf(logdepth, "find_fs__mkdir: trying to find _mkdir()...\n");
	logdepth++;

	/* win32, 2 arguments, standard includes */
	sprintf(test_c, test_c_in, dir, ", 0755");
	if (try_icl(logdepth, "libs/fs/_mkdir", test_c, "#include <direct.h>\n", NULL, NULL)) {
		if (!is_dir(dir))
			goto oops1;
		put("libs/fs/_mkdir/num_args", "2");
		rmdir(dir);
		return 0;
	}

	oops1:;
	/* win32, 1 argument, standard includes */
	sprintf(test_c, test_c_in, dir, "");
	if (try_icl(logdepth, "libs/fs/_mkdir", test_c, "#include <direct.h>\n", NULL, NULL)) {
		if (!is_dir(dir))
			goto oops2;
		put("libs/fs/_mkdir/num_args", "1");
		rmdir(dir);
		return 0;
	}

	oops2:;
	put("libs/fs/_mkdir/includes", "");
	put("libs/fs/_mkdir/ldflags", "");
	put("libs/fs/_mkdir/cdflags", "");

	rmdir(dir);
	return try_fail(logdepth, "libs/fs/_mkdir");
}

int find_fs_mkdtemp(int logdepth, int fatal)
{
	char *test_c =
		NL "#include <stdio.h>"
		NL "#include <unistd.h>"
		NL "#include <string.h>"
		NL "int main() {"
		NL "	char fn[32], *o;"
		NL "	strcpy(fn, \"scc.XXXXXX\");"
		NL "	o = mkdtemp(fn);"
		NL "	if (o != NULL) {"
		NL "		unlink(fn);"
		NL "		puts(\"OK\");"
		NL "	}"
		NL "	return 0;"
		NL "}"
		NL;

	require("cc/cc", logdepth, fatal);

	report("Checking for mkdtemp... ");
	logprintf(logdepth, "find_fs_mkdtemp: trying to find mkdtemp()...\n");
	logdepth++;

	if (try_icl(logdepth, "libs/fs/mkdtemp", test_c, "#include <stdlib.h>\n", NULL, NULL)) return 0;
	return try_fail(logdepth, "libs/fs/mkdtemp");
}
