/* mktool - A Makefile Tool
 * Copyright (c) 2005 Michael B. Allen <mba2000 ioplex.com>
 *
 * The MIT License
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <unistd.h>

#define ERR(e,m) fprintf(stderr, "line %d: %s: %s\n", __LINE__, strerror(e), m);

#define BSIZ 8192

#define ARG_CC     0
#define ARG_DEBUG  1
#define ARG_WARN1  2
#define ARG_WARN2  3
#define ARG_C99    4
#define ARG_C89    5
#define ARG_PIC1   6
#define ARG_PIC2   7
#define ARG_SONAME 8
#define ARG_SOVERS 9
#define ARG_SHARED 10
#define ARG_RPATH  11
#define ARG_SOOUT  12
#define ARG_SOLNK1 13
#define ARG_SOLNK2 14
#define ARG_PLATFO 15

static const char *arg_array[32] = {
#if defined(__GNUC__)
#if defined(linux)
		"gcc",
		" -g",
		" -Wall -W",
		" -Wall -W -ansi -pedantic",
		" -std=c99",
		" -std=c89",
		" -fpic",
		" -fPIC",
		" -Wl,-soname,lib%n.so.%1.%2",
		"",
		" -shared",
		" -Wl,-rpath,%s",
		"lib%n.so.%1.%2.%3",
		"lib%n.so.%1.%2",
		"lib%n.so",
		"linux",
#elif defined(__APPLE__) && defined(__MACH__)
		"gcc",
		" -g",
		" -Wall -W",
		" -Wall -W -ansi -pedantic",
		" -std=c99",
		" -std=c89",
		" -fPIC",
		" -fPIC",
		" -install_name lib%n.%1.%2.dylib",
		" -current_version %1.%2.%3",
		" -dynamiclib",
		"",
		"lib%n.%1.%2.%3.dylib",
		"lib%n.%1.%2.dylib",
		"lib%n.dylib",
		"darwin",
#elif defined(__FreeBSD__) || defined(__NetBSD__)
		"gcc",
		" -g",
		" -Wall -W",
		" -Wall -W -ansi -pedantic",
		" -std=c99",
		" -std=c89",
		" -fpic",
		" -fPIC",
		" -Wl,-soname,lib%n.so.%1.%2",
		"",
		" -shared",
		" -Wl,-rpath,%s",
		"lib%n.so.%1",
		"",
		"lib%n.so",
		"bsd",
#elif defined(hpux)
		"gcc",
		" -g",
		" -Wall -W",
		" -Wall -W -ansi -pedantic",
		" -std=c99",
		" -std=c89",
		" -fpic",
		" -fPIC",
		"",
		"",
		" -shared",
		" -Wl,+b,%s",
		"lib%n.%1.sl",
		"",
		"lib%n.sl",
		"hpux",
#else
#error unknown platform
#endif
#elif defined(__DECC)
		"cc",
		" -g",
		"",
		"",
		" -c99",
		" -std1",
		"",
		"",
		" -soname lib%n.so.%1.%2",
		"",
		" -shared",
		" -rpath %s",
		"lib%n.%1.%2.%3.so",
		"lib%n.%1.%2.so",
		"lib%n.so",
		"osf1",
#elif defined(__HP_aCC) || defined(__HP_cc)
		"cc",
		" -g",
		"",
		" +w",
		" -AC99",
		" -AC89",
		" +z",
		" +Z",
		"",
		"",
		" -b",
		" -Wl,+b,%s",
		"lib%n.%1.sl",
		"",
		"lib%n.sl",
		"hpux",
#else
#error unknown compiler
#endif
		NULL
};

const char *
escape_quotes(const char *str)
{
	static char buf[8192];
	char *bp = buf, *blim = buf + 8192;

	while (*str && bp < blim) {
		if (*str == '"') {
			*bp++ = '\\';
		}
		*bp++ = *str++;
	}
	if (bp == blim) {
		return "error";
	}
	*bp = '\0';

	return buf;
}
int
mkcccmd(char *buf, int argc, char *argv[])
{
	char *bp = buf;
	char **args = argv;

	bp += sprintf(bp, arg_array[ARG_CC]);

	while (argc) {
		if (strcmp(*args, "-g") == 0) {
			bp += sprintf(bp, arg_array[ARG_DEBUG]);
		} else if (strcmp(*args, "-W1") == 0) {
			bp += sprintf(bp, arg_array[ARG_WARN1]);
		} else if (strcmp(*args, "-W2") == 0) {
			bp += sprintf(bp, arg_array[ARG_WARN2]);
		} else if (strncmp(*args, "-std=", 5) == 0) {
			static const char *stds[] = { "c99", "c89", NULL };
			int std = 0;

			while (stds[std] && strcmp(*args + 5, stds[std])) {
				std++;
			}
			if (stds[std] != NULL) {
				bp += sprintf(bp, arg_array[ARG_C99 + std]);
			} else { /* just copy */
				fprintf(stderr, "Argument '%s' not translated - just copying ...\n", *args);
				bp += sprintf(bp, " %s", *args);
			}
		} else if (strcmp(*args, "-fpic") == 0) {
			bp += sprintf(bp, arg_array[ARG_PIC1]);
		} else if (strcmp(*args, "-fPIC") == 0) {
			bp += sprintf(bp, arg_array[ARG_PIC2]);
		} else if (strcmp(*args, "-rpath") == 0) {
			args++; argc--;
			if (!argc) {
				ERR(EINVAL, "-rpath requires an argument");
				return -1;
			}
			bp += sprintf(bp, arg_array[ARG_RPATH], *args);
		} else {
			/* Just copy the argument */
			bp += sprintf(bp, " %s", escape_quotes(*args));
		}
		args++; argc--;
	}

	return bp - buf;
}
/* Examples:
 * lib%n.so.%1.%2 -> libfoo.so.0.8
 * lib%n.%1.%2.%3.dylib -> libfoo.0.8.25.dylib
 */
int
slibprintf(char *str, const char *fmt, const char *name, char *vers[])
{
	char *start = str;

	for ( ; *fmt; fmt++) {
		if (*fmt == '%') {
			fmt++;
			switch (*fmt) {
				case 'n':
					str += sprintf(str, name);
					break;
				case '1':
					str += sprintf(str, vers[0]);
					break;
				case '2':
					str += sprintf(str, vers[1]);
					break;
				case '3':
					str += sprintf(str, vers[2]);
					break;
				case '%':
					*str++ = '%';
					break;
				default:
					fprintf(stderr, "invalid conversion specifier '%%%c' - valid conversion "
							"specifiers are %%n, %%1, %%2, and %%3 for libname and major, "
							"minor, micro version numbers respectively\n", *fmt);
					return 0;
			}
		} else {
			*str++ = *fmt;
		}
	}
	*str = '\0';

	return str - start;
}
int
mklibcmd(char *buf, const char *libname, char *vers[], int argc, char *argv[])
{
	char *bp = buf;
	char **args = argv;

#if defined(__GNUC__)
	bp += sprintf(bp, "gcc");
#else
	bp += sprintf(bp, "cc");
#endif
	while (argc) {
		if (strcmp(*args, "-soname") == 0) {
			bp += slibprintf(bp, arg_array[ARG_SONAME], libname, vers);
		} else if (strcmp(*args, "-sovers") == 0) {
			bp += slibprintf(bp, arg_array[ARG_SOVERS], libname, vers);
		} else if (strcmp(*args, "-shared") == 0) {
			bp += sprintf(bp, arg_array[ARG_SHARED]);
		} else {
			/* Just copy the argument */
			bp += sprintf(bp, " %s", *args);
		}
		args++; argc--;
	}

	bp += sprintf(bp, " -o ");
	bp += slibprintf(bp, arg_array[ARG_SOOUT], libname, vers);

	return bp - buf;
}

int
mkdirs(const char *dir, mode_t mode)
{
	char buf[PATH_MAX + 1], *bp = buf, *blim = buf + PATH_MAX;
	const char *dp = dir;

	while (*dp && bp < blim) {
		*bp++ = *dp++;
		if (*dp == '/' || *dp == '\0') {
			*bp = '\0';
			if (mkdir(buf, mode) == -1 && errno != EEXIST) {
				ERR(errno, buf);
				return -1;
			}
		}
	}

	return 0;
}
int
cpfile(const char *file, const char *dir, mode_t mode)
{
	char buf[BSIZ + 1], *bp = buf, *blim = buf + BSIZ;
	const char *dp = dir, *fname = file;
	int src, dst, n = 1;

	if ((src = open(file, O_RDONLY)) == -1) {
		ERR(errno, file);
		return -1;
	}

	while (bp < blim && *dp) {        /* copy dir */
		*bp++ = *dp++;
		n = *dp == '/';
	}
	if (!n) *bp++ = '/'; /* assure trailing slash */
	dp = fname;      /* find beginning of filename */
	while (*dp) {
		if (*dp++ == '/') {
			fname = dp;
		}
	}
	while (bp < blim && *fname) { /* copy filename */
		*bp++ = *fname++;
	}
	*bp = '\0';
	if ((dst = open(buf, O_RDWR | O_CREAT | O_TRUNC, mode)) == -1) {
		ERR(errno, buf);
		return -1;
	}

	while ((n = read(src, buf, BSIZ)) > 0) {
		while (n) {
			int m;
			if ((m = write(dst, buf, n)) == -1) {
				ERR(errno, file);
				return -1;
			}
			n -= m;
		}
	}
	if (n == -1) {
		ERR(errno, file);
		return -1;
	}

	return 0;
}
int
doinstcmd(const char *dir, int argc, char *argv[], mode_t mode)
{
	int i;

	umask(0);

	if (mkdirs(dir, 0755) == -1) {
		return -1;
	}
	for (i = 0; i < argc; i++) {
		if (cpfile(argv[i], dir, mode) == -1) {
			return -1;
		}
	}

	return 0;
}
int
rmfile(const char *dir, const char *file)
{
	char path[PATH_MAX + 1], *bp = path, *blim = path + PATH_MAX;
	const char *dp = dir;

	if (dir) {
		int n = 1;
		while (bp < blim && *dp) {        /* copy dir */
			n = *dp == '/';
			*bp++ = *dp++;
		}
		if (!n) *bp++ = '/'; /* assure trailing slash */
	}

	dp = file;          /* find beginning of filename */
	while (*dp) {
		if (*dp++ == '/') {
			file = dp;
		}
	}
	while (bp < blim && *file) {     /* copy filename */
		*bp++ = *file++;
	}
	*bp = '\0';

	if (bp == blim) {
		ERR(ENOBUFS, file);
		return -1;
	}

	unlink(path);

	return 0;
}
int
doinst(const char *libname, char *vers[], int argc, char *argv[])
{
	mode_t mode = 0;
	int i;

	for (i = 0; i < argc; i++) {
		if (strcmp(argv[i], "-m") == 0) {
			unsigned long ul;
			if (++i == argc) return -2;
			if ((ul = strtoul(argv[i], NULL, 8)) == ULONG_MAX) {
				ERR(errno, argv[i]);
				return -1;
			}
			mode = (mode_t)ul;
		} else {
			break;
		}
	}
	if (i == argc) return -1;

	if (libname) {
		char fname[255], *arg[] = { fname }, lname[255];

		slibprintf(fname, arg_array[ARG_SOOUT], libname, vers);
		if (doinstcmd(argv[argc - 1], 1, arg, mode ? mode : 0755) == -1) {
			return -1;
		}
		if (chdir(argv[argc - 1]) == -1) {
			ERR(errno, argv[argc - 1]);
			return -1;
		}
		if (arg_array[ARG_SOLNK1][0]) {
			slibprintf(lname, arg_array[ARG_SOLNK1], libname, vers);
			symlink(fname, lname);
		}
		if (arg_array[ARG_SOLNK2][0]) {
			slibprintf(lname, arg_array[ARG_SOLNK2], libname, vers);
			symlink(fname, lname);
		}
	} else {
		if (doinstcmd(argv[argc - 1], argc - i - 1, argv + i, mode ? mode : 0644) == -1) {
			return -1;
		}
	}

	return 0;
}

int
main(int argc, char *argv[])
{
	char buf[BSIZ];
	int cmd, i = 2, verbose = 0;

	if (argc < 2) {
usage:
		fprintf(stderr, "%s -c [-v] [-g] [-W1|-W2] [-std=xxx] [-fpic|-fPIC] [-rpath <path>]"
				"<other arguments>\n", argv[0]);
		fprintf(stderr, "%s -l [-v] -libname <name> -libvers <vers> [-shared] [-soname] [-sovers] "
				"<other arguments>\n", argv[0]);
		fprintf(stderr, "%s -i [-m <mode>] <file1> <file2> <fileN> <dir>\n", argv[0]);
		fprintf(stderr, "%s -i -libname <name> -libvers <vers> [-m mode] <dir>\n", argv[0]);
		fprintf(stderr, "%s -u <dir> <file1> <file2> <fileN>\n", argv[0]);
		fprintf(stderr, "%s -u -libname <name> -libvers <vers> <srcdir>\n", argv[0]);
		fprintf(stderr, "%s -m <target>\n", argv[0]);
		return EXIT_FAILURE;
	}

	cmd = argv[1][1];
	if (argc > 2 && strcmp(argv[2], "-v") == 0) {
		verbose = 1;
		i++;
	}

	switch (cmd) {
		case 'c':
			if (argc < 3) {
				fprintf(stderr, "no compiler arguments provided\n");
				goto usage;
			}
			if (mkcccmd(buf, argc - i, argv + i) == -1) {
				fprintf(stderr, "failed to build command\n");
				return EXIT_FAILURE;
			}
			break;
		case 'l':
		case 'i':
		case 'u':
		case 'C':
			{
				const char *libname = NULL, *dir = NULL;
				char vbuf[255], *vers[3] = { NULL, NULL, NULL };

				for ( ; i < argc; i++) {
					if (strcmp(argv[i], "-libname") == 0) {
						if (++i == argc) goto usage;
						libname = argv[i];
					} else if (strcmp(argv[i], "-libvers") == 0) {
						char *vp = vbuf;
						int vi = 0;

						if (++i == argc) goto usage;
						strcpy(vbuf, argv[i]);
						vers[vi++] = vp;
						while (vi < 3 && *vp++) {
							if (*vp == '.') {
								*vp++ = '\0';
								vers[vi++] = vp;
							}
						}
					} else {
						break;
					}
				}
				if (i == argc && cmd != 'C') {
					ERR(EINVAL, "more arguments expected");
					goto usage;
				}

				switch (cmd) {
					case 'l':
						if (mklibcmd(buf, libname, vers, argc - i, argv + i) == -1) {
							return EXIT_FAILURE;
						}
						break;
					case 'i':
						if (doinst(libname, vers, argc - i, argv + i) == -1) {
							return EXIT_FAILURE;
						}
						return EXIT_SUCCESS;
					case 'u':
						dir = argv[argc - 1];
						argc--;
					case 'C':
						if (libname) {
							if (cmd != 'C') {
								if (arg_array[ARG_SOLNK2][0]) {
									slibprintf(buf, arg_array[ARG_SOLNK2], libname, vers);
									if (rmfile(dir, buf) == -1 && errno != ENOENT) return EXIT_FAILURE;
								}
								if (arg_array[ARG_SOLNK1][0]) {
									slibprintf(buf, arg_array[ARG_SOLNK1], libname, vers);
									if (rmfile(dir, buf) == -1 && errno != ENOENT) return EXIT_FAILURE;
								}
							}
							slibprintf(buf, arg_array[ARG_SOOUT], libname, vers);
							if (rmfile(dir, buf) == -1 && errno != ENOENT) return EXIT_FAILURE;
						} else {
							for ( ; i < argc; i++) {
								if (rmfile(dir, argv[i]) == -1 && errno != ENOENT) {
									return EXIT_FAILURE;
								}
							}
						}

						return EXIT_SUCCESS;
				}
			}
			break;
		case 'm':
			{
				char *bp = buf;
				bp += sprintf(bp, "make -f Makefile.%s", arg_array[ARG_PLATFO]);
				if (i < argc) {
					bp += sprintf(bp, " %s", argv[i]);
				}
			}
			break;
		default:
			fprintf(stderr, "first argument must be a command flag\n");
			goto usage;
	}

	/* run command */
	
	if (verbose) {
		fprintf(stderr, "%s\n", buf);
	}

	if (system(buf) == -1) {
		perror("system");
		return -1;
	}

	return EXIT_SUCCESS;
}



syntax highlighted by Code2HTML, v. 0.9.1