mirror of
				https://gitlab.com/sortix/sortix.git
				synced 2023-02-13 20:55:38 -05:00 
			
		
		
		
	Add the Tix package management system.
This commit is contained in:
		
							parent
							
								
									dce618af93
								
							
						
					
					
						commit
						b0d07b9142
					
				
					 16 changed files with 4532 additions and 4 deletions
				
			
		
							
								
								
									
										4
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										4
									
								
								Makefile
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -34,11 +34,13 @@ all: sysroot
 | 
			
		|||
build-tools:
 | 
			
		||||
	$(MAKE) -C mkinitrd
 | 
			
		||||
	$(MAKE) -C mxmpp
 | 
			
		||||
	$(MAKE) -C tix
 | 
			
		||||
 | 
			
		||||
.PHONY: install-build-tools
 | 
			
		||||
install-build-tools:
 | 
			
		||||
	$(MAKE) -C mkinitrd install
 | 
			
		||||
	$(MAKE) -C mxmpp install
 | 
			
		||||
	$(MAKE) -C tix install
 | 
			
		||||
 | 
			
		||||
.PHONY: sysroot-fsh
 | 
			
		||||
sysroot-fsh:
 | 
			
		||||
| 
						 | 
				
			
			@ -99,7 +101,7 @@ sysroot: sysroot-system sysroot-source sysroot-overlay sysroot-home-directory
 | 
			
		|||
 | 
			
		||||
.PHONY: clean-core
 | 
			
		||||
clean-core:
 | 
			
		||||
	(for D in $(MODULES); do $(MAKE) clean $(SUBMAKE_OPTIONS) --directory $$D || exit $$?; done)
 | 
			
		||||
	(for D in $(MODULES) tix; do $(MAKE) clean $(SUBMAKE_OPTIONS) --directory $$D || exit $$?; done)
 | 
			
		||||
 | 
			
		||||
.PHONY: clean-builds
 | 
			
		||||
clean-builds:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										7
									
								
								README
									
										
									
									
									
								
							
							
						
						
									
										7
									
								
								README
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -91,9 +91,10 @@ The build scripts might not contain a copyright license in which case they are
 | 
			
		|||
covered by the standard license for the software component they relate to.
 | 
			
		||||
 | 
			
		||||
Unless the license header in the source code states otherwise, the Sortix
 | 
			
		||||
kernel, the filesystem servers, the initrd tools, the utilities, the games, and
 | 
			
		||||
the benchmark programs are licensed under the GNU General Public License, either
 | 
			
		||||
version 3 or (at your option) any later version.
 | 
			
		||||
kernel, the filesystem servers, the initrd tools, the utilities, the games, the
 | 
			
		||||
benchmark programs, and the tix package management programs are licensed under
 | 
			
		||||
the GNU General Public License, either version 3 or (at your option) any later
 | 
			
		||||
version.
 | 
			
		||||
 | 
			
		||||
Unless the license header in the source code states otherwise, the libc library
 | 
			
		||||
and the libdispd library are licensed under the GNU Lesser General Public
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										11
									
								
								tix/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								tix/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
*.tix*
 | 
			
		||||
porttix-create
 | 
			
		||||
srctix-create
 | 
			
		||||
tix
 | 
			
		||||
tix-build
 | 
			
		||||
tix-collection
 | 
			
		||||
tix-execdiff
 | 
			
		||||
tix-execpatch
 | 
			
		||||
tix-install
 | 
			
		||||
tix-object-insert
 | 
			
		||||
tix-rmpatch
 | 
			
		||||
							
								
								
									
										44
									
								
								tix/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								tix/Makefile
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
include ../compiler.mak
 | 
			
		||||
include ../version.mak
 | 
			
		||||
include ../dirs.mak
 | 
			
		||||
 | 
			
		||||
OPTLEVEL?=-g -O2
 | 
			
		||||
CPPFLAGS?=
 | 
			
		||||
CXXFLAGS?=$(OPTLEVEL)
 | 
			
		||||
 | 
			
		||||
CPPFLAGS:=$(CPPFLAGS)
 | 
			
		||||
CXXFLAGS:=$(CXXFLAGS) -Wall -Wextra -fno-exceptions -fno-rtti
 | 
			
		||||
 | 
			
		||||
BINARIES:=\
 | 
			
		||||
porttix-create \
 | 
			
		||||
srctix-create \
 | 
			
		||||
tix \
 | 
			
		||||
tix-build \
 | 
			
		||||
tix-collection \
 | 
			
		||||
tix-execdiff \
 | 
			
		||||
tix-execpatch \
 | 
			
		||||
tix-install \
 | 
			
		||||
tix-object-insert \
 | 
			
		||||
tix-rmpatch \
 | 
			
		||||
 | 
			
		||||
PROGRAMS:=\
 | 
			
		||||
$(BINARIES) \
 | 
			
		||||
tix-eradicate-libtool-la \
 | 
			
		||||
 | 
			
		||||
LIBS:=-lnettle
 | 
			
		||||
 | 
			
		||||
all: $(PROGRAMS)
 | 
			
		||||
 | 
			
		||||
.PHONY: all install clean
 | 
			
		||||
 | 
			
		||||
%: %.cpp util.h
 | 
			
		||||
	$(CXX) -std=gnu++11 $(CPPFLAGS) $(CXXFLAGS) $< -o $@ $(LIBS)
 | 
			
		||||
 | 
			
		||||
$(DESTDIR)$(BINDIR):
 | 
			
		||||
	mkdir -p $@
 | 
			
		||||
 | 
			
		||||
install: all $(DESTDIR)$(BINDIR)
 | 
			
		||||
	install $(PROGRAMS) $(DESTDIR)$(BINDIR)
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -f $(BINARIES)
 | 
			
		||||
							
								
								
									
										436
									
								
								tix/porttix-create.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										436
									
								
								tix/porttix-create.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,436 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2013.
 | 
			
		||||
 | 
			
		||||
    This file is part of Tix.
 | 
			
		||||
 | 
			
		||||
    Tix is free software: you can redistribute it and/or modify it under the
 | 
			
		||||
    terms of the GNU General Public License as published by the Free Software
 | 
			
		||||
    Foundation, either version 3 of the License, or (at your option) any later
 | 
			
		||||
    version.
 | 
			
		||||
 | 
			
		||||
    Tix 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 General Public License for more
 | 
			
		||||
    details.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    Tix. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    porttix-create.cpp
 | 
			
		||||
    Creates a port tix by generating patches using source code and tarballs.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#define __STDC_CONSTANT_MACROS
 | 
			
		||||
#define __STDC_LIMIT_MACROS
 | 
			
		||||
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <error.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <libgen.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
int redirect(const char* path, int flags, mode_t mode = 0)
 | 
			
		||||
{
 | 
			
		||||
	int fd = open(path, flags, mode);
 | 
			
		||||
	if ( fd < 0 )
 | 
			
		||||
		return -1;
 | 
			
		||||
	dup2(fd, 1);
 | 
			
		||||
	close(fd);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void help(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(fp, "Usage: %s [OPTION]... --tarball=TARBALL --normalized=NORMALIZED SOURCE-TIX\n", argv0);
 | 
			
		||||
	fprintf(fp, "Creates a port tix by generating patches using source code and tarballs.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void version(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	help(fp, argv0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
	char* cp = strdup(getenv_def("CP", "cp"));
 | 
			
		||||
	char* diff = strdup(getenv_def("DIFF", "diff"));
 | 
			
		||||
	char* input_normalized_path = NULL;
 | 
			
		||||
	char* input_tarball_path = NULL;
 | 
			
		||||
	char* output_directory = strdup(".");
 | 
			
		||||
	char* output = NULL;
 | 
			
		||||
	char* tar = strdup(getenv_def("TAR", "tar"));
 | 
			
		||||
	char* tix_execdiff = strdup(getenv_def("TIX_EXECDIFF", "tix-execdiff"));
 | 
			
		||||
	char* tmp = strdup(getenv_def("TMP", "/tmp"));
 | 
			
		||||
 | 
			
		||||
	const char* argv0 = argv[0];
 | 
			
		||||
	for ( int i = 0; i < argc; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		const char* arg = argv[i];
 | 
			
		||||
		if ( arg[0] != '-' )
 | 
			
		||||
			continue;
 | 
			
		||||
		argv[i] = NULL;
 | 
			
		||||
		if ( !strcmp(arg, "--") )
 | 
			
		||||
			break;
 | 
			
		||||
		if ( arg[1] != '-' )
 | 
			
		||||
		{
 | 
			
		||||
			while ( char c = *++arg ) switch ( c )
 | 
			
		||||
			{
 | 
			
		||||
			default:
 | 
			
		||||
				fprintf(stderr, "%s: unknown option -- `%c'\n", argv0, c);
 | 
			
		||||
				help(stderr, argv0);
 | 
			
		||||
				exit(1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !strcmp(arg, "--help") ) { help(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( !strcmp(arg, "--version") ) { version(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--cp", &cp) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--diff", &diff) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--normalized", &input_normalized_path) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--output-directory", &output_directory) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--output", &output) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--tarball", &input_tarball_path) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--tar", &tar) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--tix-execdiff", &tix_execdiff) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--tmp", &tmp) ) { }
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
 | 
			
		||||
			help(stderr, argv0);
 | 
			
		||||
			exit(1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( argc == 1 )
 | 
			
		||||
	{
 | 
			
		||||
		help(stdout, argv0);
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CompactArguments(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	if ( argc <= 1 )
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "%s: no source tix specified\n", argv0);
 | 
			
		||||
		help(stderr, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( 3 <= argc )
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "%s: unexpected extra operand `%s'\n", argv0, argv[2]);
 | 
			
		||||
		help(stderr, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const char* input_srctix_path = argv[1];
 | 
			
		||||
 | 
			
		||||
	if ( !IsDirectory(input_srctix_path) )
 | 
			
		||||
		error(1, errno, "`%s'", input_srctix_path);
 | 
			
		||||
 | 
			
		||||
	char* tixbuildinfo_path = print_string("%s/tixbuildinfo", input_srctix_path);
 | 
			
		||||
 | 
			
		||||
	string_array_t package_info = string_array_make();
 | 
			
		||||
	if ( !dictionary_append_file_path(&package_info, tixbuildinfo_path) )
 | 
			
		||||
	{
 | 
			
		||||
		if ( errno == ENOENT )
 | 
			
		||||
			fprintf(stderr, "%s: `%s' doesn't appear to be a source tix:\n",
 | 
			
		||||
			                argv0, input_srctix_path);
 | 
			
		||||
		error(1, errno, "`%s'", tixbuildinfo_path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const char* package_name = strdup(dictionary_get(&package_info, "pkg.name"));
 | 
			
		||||
 | 
			
		||||
	if ( !output )
 | 
			
		||||
		output = print_string("%s/%s.porttix.tar.xz", output_directory, package_name);
 | 
			
		||||
 | 
			
		||||
	char* tmp_root = print_string("%s/tmppid.%ju", tmp, (uintmax_t) getpid());
 | 
			
		||||
	if ( mkdir_p(tmp_root, 0777) != 0 )
 | 
			
		||||
		error(1, errno, "mkdir: `%s'", tmp_root);
 | 
			
		||||
 | 
			
		||||
	on_exit(cleanup_file_or_directory, tmp_root);
 | 
			
		||||
 | 
			
		||||
	const char* tarball_basename = non_modify_basename(input_tarball_path);
 | 
			
		||||
 | 
			
		||||
	char* rel_srctix_path = print_string("%s.srctix", package_name);
 | 
			
		||||
	char* rel_normalized_path = print_string("%s.normalized", package_name);
 | 
			
		||||
 | 
			
		||||
	char* porttix_path = print_string("%s/%s", tmp_root, package_name);
 | 
			
		||||
	if ( mkdir_p(porttix_path, 0777) != 0 )
 | 
			
		||||
		error(1, errno, "mkdir: `%s'", porttix_path);
 | 
			
		||||
 | 
			
		||||
	char* srctix_path = print_string("%s/%s", tmp_root, rel_srctix_path);
 | 
			
		||||
	if ( mkdir_p(srctix_path, 0777) != 0 )
 | 
			
		||||
		error(1, errno, "mkdir: `%s'", srctix_path);
 | 
			
		||||
 | 
			
		||||
	char* normalized_path = print_string("%s/%s", tmp_root, rel_normalized_path);
 | 
			
		||||
	if ( mkdir_p(normalized_path, 0777) != 0 )
 | 
			
		||||
		error(1, errno, "mkdir: `%s'", normalized_path);
 | 
			
		||||
 | 
			
		||||
	// Create the porttixinfo file.
 | 
			
		||||
	char* porttixinfo_path = join_paths(porttix_path, "porttixinfo");
 | 
			
		||||
	FILE* porttixinfo_fp = fopen(porttixinfo_path, "w");
 | 
			
		||||
	if ( !porttixinfo_fp )
 | 
			
		||||
		error(1, errno, "`%s'", porttixinfo_path);
 | 
			
		||||
	fprintf(porttixinfo_fp, "package_name %s\n", package_name);
 | 
			
		||||
 | 
			
		||||
	// Copy the input source tix to the temporary root.
 | 
			
		||||
	if ( fork_and_wait_or_death() )
 | 
			
		||||
	{
 | 
			
		||||
		const char* cmd_argv[] =
 | 
			
		||||
		{
 | 
			
		||||
			cp,
 | 
			
		||||
			"-HRT",
 | 
			
		||||
			"--preserve=timestamps,links",
 | 
			
		||||
			"--",
 | 
			
		||||
			input_srctix_path,
 | 
			
		||||
			srctix_path,
 | 
			
		||||
			NULL,
 | 
			
		||||
		};
 | 
			
		||||
		execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
		error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If no tarball exists, then package up the source directory!
 | 
			
		||||
	if ( !input_tarball_path )
 | 
			
		||||
	{
 | 
			
		||||
		input_tarball_path = print_string("%s/%s.tar.xz", tmp_root, package_name);
 | 
			
		||||
		if ( fork_and_wait_or_death() )
 | 
			
		||||
		{
 | 
			
		||||
			char* work_dir = dirname(strdup(srctix_path));
 | 
			
		||||
			char* subdir_name = dirname(strdup(srctix_path));
 | 
			
		||||
			if ( chdir(work_dir) != 0 )
 | 
			
		||||
				error(1, errno, "chdir: `%s'", work_dir);
 | 
			
		||||
			const char* cmd_argv[] =
 | 
			
		||||
			{
 | 
			
		||||
				tar,
 | 
			
		||||
				"--create",
 | 
			
		||||
				"--xz",
 | 
			
		||||
				"--directory", input_normalized_path,
 | 
			
		||||
				"--file", input_tarball_path,
 | 
			
		||||
				"--",
 | 
			
		||||
				subdir_name,
 | 
			
		||||
				NULL,
 | 
			
		||||
			};
 | 
			
		||||
			execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
			error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Copy the normalized directory (if one exists) to the temporary root.
 | 
			
		||||
	if ( input_normalized_path )
 | 
			
		||||
	{
 | 
			
		||||
		if ( fork_and_wait_or_death() )
 | 
			
		||||
		{
 | 
			
		||||
			const char* cmd_argv[] =
 | 
			
		||||
			{
 | 
			
		||||
				cp,
 | 
			
		||||
				"-HRT",
 | 
			
		||||
				"--preserve=timestamps,links",
 | 
			
		||||
				"--",
 | 
			
		||||
				input_normalized_path,
 | 
			
		||||
				normalized_path,
 | 
			
		||||
				NULL,
 | 
			
		||||
			};
 | 
			
		||||
			execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
			error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// There is no input normalized directory, so just extract the tarball here.
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		if ( fork_and_wait_or_death() )
 | 
			
		||||
		{
 | 
			
		||||
			const char* cmd_argv[] =
 | 
			
		||||
			{
 | 
			
		||||
				tar,
 | 
			
		||||
				"--extract",
 | 
			
		||||
				"--directory", normalized_path,
 | 
			
		||||
				"--file", input_tarball_path,
 | 
			
		||||
				"--strip-components=1",
 | 
			
		||||
				NULL,
 | 
			
		||||
			};
 | 
			
		||||
			execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
			error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Copy the tarball into the port tix.
 | 
			
		||||
	char* porttix_tarball_path = print_string("%s/%s", porttix_path, tarball_basename);
 | 
			
		||||
	if ( fork_and_wait_or_death() )
 | 
			
		||||
	{
 | 
			
		||||
		const char* cmd_argv[] =
 | 
			
		||||
		{
 | 
			
		||||
			cp,
 | 
			
		||||
			"--",
 | 
			
		||||
			input_tarball_path,
 | 
			
		||||
			porttix_tarball_path,
 | 
			
		||||
			NULL,
 | 
			
		||||
		};
 | 
			
		||||
		execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
		error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
	}
 | 
			
		||||
	fprintf(porttixinfo_fp, "tar_extract %s\n", tarball_basename);
 | 
			
		||||
 | 
			
		||||
	// Create the normalization patch.
 | 
			
		||||
	int normalized_fd = open(normalized_path, O_RDONLY | O_DIRECTORY);
 | 
			
		||||
	if ( normalized_fd < 0 )
 | 
			
		||||
		error(1, errno, "`%s'", normalized_path);
 | 
			
		||||
 | 
			
		||||
	char* patch_normalize_path = join_paths(porttix_path, "patch.normalize");
 | 
			
		||||
	FILE* patch_normalize_fp = fopen(patch_normalize_path, "w");
 | 
			
		||||
	if ( !patch_normalize_fp )
 | 
			
		||||
		error(1, errno, "`%s'", patch_normalize_path);
 | 
			
		||||
 | 
			
		||||
	int pipes[2];
 | 
			
		||||
	if ( pipe(pipes) )
 | 
			
		||||
		error(1, errno, "pipe");
 | 
			
		||||
	pid_t tar_pid = fork_or_death();
 | 
			
		||||
	if ( !tar_pid )
 | 
			
		||||
	{
 | 
			
		||||
		dup2(pipes[1], 1);
 | 
			
		||||
		close(pipes[1]);
 | 
			
		||||
		close(pipes[0]);
 | 
			
		||||
		const char* cmd_argv[] =
 | 
			
		||||
		{
 | 
			
		||||
			tar,
 | 
			
		||||
			"--list",
 | 
			
		||||
			"--file", porttix_tarball_path,
 | 
			
		||||
			"--strip-components=1",
 | 
			
		||||
			NULL
 | 
			
		||||
		};
 | 
			
		||||
		execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
		error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
	}
 | 
			
		||||
	close(pipes[1]);
 | 
			
		||||
	FILE* tar_fp = fdopen(pipes[0], "r");
 | 
			
		||||
 | 
			
		||||
	char* line = NULL;
 | 
			
		||||
	size_t line_size = 0;
 | 
			
		||||
	ssize_t line_len;
 | 
			
		||||
	while ( 0 < (line_len = getline(&line, &line_size, tar_fp)) )
 | 
			
		||||
	{
 | 
			
		||||
		if ( line_len && line[line_len-1] == '\n' )
 | 
			
		||||
			line[--line_len] = '\0';
 | 
			
		||||
		const char* path = line;
 | 
			
		||||
		while ( *path != '/' )
 | 
			
		||||
			path++;
 | 
			
		||||
		if ( *path == '/' )
 | 
			
		||||
			path++;
 | 
			
		||||
		if ( !*path )
 | 
			
		||||
			continue;
 | 
			
		||||
		struct stat st;
 | 
			
		||||
		if ( fstatat(normalized_fd, path, &st, 0) != 0 && errno == ENOENT )
 | 
			
		||||
		{
 | 
			
		||||
			fprintf(patch_normalize_fp, "rm -rf -- '");
 | 
			
		||||
			for ( size_t i = 0; path[i]; i++ )
 | 
			
		||||
				if ( path[i] == '\'' )
 | 
			
		||||
					fprintf(patch_normalize_fp, "'\\''");
 | 
			
		||||
				else
 | 
			
		||||
					fputc(path[i], patch_normalize_fp);
 | 
			
		||||
			fprintf(patch_normalize_fp, "'\n");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	free(line);
 | 
			
		||||
 | 
			
		||||
	fclose(tar_fp);
 | 
			
		||||
	int tar_exit_status;
 | 
			
		||||
	waitpid(tar_pid, &tar_exit_status, 0);
 | 
			
		||||
	if ( !WIFEXITED(tar_exit_status) || WEXITSTATUS(tar_exit_status) != 0 )
 | 
			
		||||
	{
 | 
			
		||||
		error(1, 0, "Unable to list contents of `%s'.", porttix_tarball_path);
 | 
			
		||||
		exit(WEXITSTATUS(tar_exit_status));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fclose(patch_normalize_fp);
 | 
			
		||||
	free(patch_normalize_path);
 | 
			
		||||
	fprintf(porttixinfo_fp, "apply_normalize patch.normalize\n");
 | 
			
		||||
 | 
			
		||||
	close(normalized_fd);
 | 
			
		||||
 | 
			
		||||
	// Create the patch between the source tix and the normalized tree.
 | 
			
		||||
	char* patch_path = join_paths(porttix_path, "patch.patch");
 | 
			
		||||
	if ( fork_and_wait_or_death(false) )
 | 
			
		||||
	{
 | 
			
		||||
		close(1);
 | 
			
		||||
		if ( open(patch_path, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 1 )
 | 
			
		||||
			error(1, errno, "`%s'", patch_path);
 | 
			
		||||
		if ( chdir(tmp_root) != 0 )
 | 
			
		||||
			error(1, errno, "chdir(`%s')", tmp_root);
 | 
			
		||||
		const char* cmd_argv[] =
 | 
			
		||||
		{
 | 
			
		||||
			diff,
 | 
			
		||||
			"--no-dereference",
 | 
			
		||||
			"-Naur",
 | 
			
		||||
			"--",
 | 
			
		||||
			rel_normalized_path,
 | 
			
		||||
			rel_srctix_path,
 | 
			
		||||
			NULL,
 | 
			
		||||
		};
 | 
			
		||||
		execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
		error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
	}
 | 
			
		||||
	free(patch_path);
 | 
			
		||||
	fprintf(porttixinfo_fp, "apply_patch patch.patch\n");
 | 
			
		||||
 | 
			
		||||
	// Created the execpatch between the source tix and the normalized tree.
 | 
			
		||||
	char* patch_exec_path = join_paths(porttix_path, "patch.execpatch");
 | 
			
		||||
	if ( fork_and_wait_or_death(false) )
 | 
			
		||||
	{
 | 
			
		||||
		if ( redirect(patch_exec_path, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 0 )
 | 
			
		||||
			error(1, errno, "`%s'", patch_exec_path);
 | 
			
		||||
		if ( chdir(tmp_root) != 0 )
 | 
			
		||||
			error(1, errno, "chdir(`%s')", tmp_root);
 | 
			
		||||
		const char* cmd_argv[] =
 | 
			
		||||
		{
 | 
			
		||||
			tix_execdiff,
 | 
			
		||||
			"--",
 | 
			
		||||
			rel_normalized_path,
 | 
			
		||||
			rel_srctix_path,
 | 
			
		||||
			NULL,
 | 
			
		||||
		};
 | 
			
		||||
		execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
		error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
	}
 | 
			
		||||
	free(patch_exec_path);
 | 
			
		||||
	fprintf(porttixinfo_fp, "apply_execpatch patch.execpatch\n");
 | 
			
		||||
 | 
			
		||||
	// Close the porttixinfo file.
 | 
			
		||||
	fclose(porttixinfo_fp);
 | 
			
		||||
	free(porttixinfo_path);
 | 
			
		||||
 | 
			
		||||
	// Package up the output archived port tix.
 | 
			
		||||
	if ( fork_and_wait_or_death() )
 | 
			
		||||
	{
 | 
			
		||||
		const char* cmd_argv[] =
 | 
			
		||||
		{
 | 
			
		||||
			tar,
 | 
			
		||||
			"--create",
 | 
			
		||||
			"--xz",
 | 
			
		||||
			"--directory", tmp_root,
 | 
			
		||||
			"--file", output,
 | 
			
		||||
			"--",
 | 
			
		||||
			package_name,
 | 
			
		||||
			NULL,
 | 
			
		||||
		};
 | 
			
		||||
		execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
		error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										321
									
								
								tix/srctix-create.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										321
									
								
								tix/srctix-create.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,321 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2013.
 | 
			
		||||
 | 
			
		||||
    This file is part of Tix.
 | 
			
		||||
 | 
			
		||||
    Tix is free software: you can redistribute it and/or modify it under the
 | 
			
		||||
    terms of the GNU General Public License as published by the Free Software
 | 
			
		||||
    Foundation, either version 3 of the License, or (at your option) any later
 | 
			
		||||
    version.
 | 
			
		||||
 | 
			
		||||
    Tix 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 General Public License for more
 | 
			
		||||
    details.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    Tix. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    srctix-create.cpp
 | 
			
		||||
    Converts an archived port tix into an archived source tix.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#define __STDC_CONSTANT_MACROS
 | 
			
		||||
#define __STDC_LIMIT_MACROS
 | 
			
		||||
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <error.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <libgen.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
void help(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(fp, "Usage: %s [OPTION]... PORT-TIX\n", argv0);
 | 
			
		||||
	fprintf(fp, "Converts an archived port tix into an archived source tix.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void version(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	help(fp, argv0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool is_file_name(const char* path)
 | 
			
		||||
{
 | 
			
		||||
	return !(strchr(path, '/') || !strcmp(path, ".") || !strcmp(path, ".."));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
	char* output_directory = strdup(".");
 | 
			
		||||
	char* output = NULL;
 | 
			
		||||
	char* patch = strdup(getenv_def("PATCH", "patch"));
 | 
			
		||||
	char* tar = strdup(getenv_def("TAR", "tar"));
 | 
			
		||||
	char* tix_execpatch = strdup(getenv_def("TIX_EXECPATCH", "tix-execpatch"));
 | 
			
		||||
	char* tix_rmpatch = strdup(getenv_def("TIX_RMPATCH", "tix-rmpatch"));
 | 
			
		||||
	char* tmp = strdup(getenv_def("TMP", "/tmp"));
 | 
			
		||||
 | 
			
		||||
	const char* argv0 = argv[0];
 | 
			
		||||
	for ( int i = 0; i < argc; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		const char* arg = argv[i];
 | 
			
		||||
		if ( arg[0] != '-' )
 | 
			
		||||
			continue;
 | 
			
		||||
		argv[i] = NULL;
 | 
			
		||||
		if ( !strcmp(arg, "--") )
 | 
			
		||||
			break;
 | 
			
		||||
		if ( arg[1] != '-' )
 | 
			
		||||
		{
 | 
			
		||||
			while ( char c = *++arg ) switch ( c )
 | 
			
		||||
			{
 | 
			
		||||
			default:
 | 
			
		||||
				fprintf(stderr, "%s: unknown option -- `%c'\n", argv0, c);
 | 
			
		||||
				help(stderr, argv0);
 | 
			
		||||
				exit(1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !strcmp(arg, "--help") ) { help(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( !strcmp(arg, "--version") ) { version(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--output-directory", &output_directory) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--output", &output) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--patch", &patch) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--tar", &tar) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--tix-execpatch", &tix_execpatch) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--tix-rmpatch", &tix_rmpatch) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--tmp", &tmp) ) { }
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
 | 
			
		||||
			help(stderr, argv0);
 | 
			
		||||
			exit(1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( argc == 1 )
 | 
			
		||||
	{
 | 
			
		||||
		help(stdout, argv0);
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CompactArguments(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	if ( argc <= 1 )
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "%s: no archived port tix specified\n", argv0);
 | 
			
		||||
		help(stderr, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( 3 <= argc )
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "%s: unexpected extra operand `%s'\n", argv0, argv[2]);
 | 
			
		||||
		help(stderr, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const char* porttix_path = argv[1];
 | 
			
		||||
 | 
			
		||||
	char* tmp_in_root = print_string("%s/tmppid.%ju.in", tmp, (uintmax_t) getpid());
 | 
			
		||||
	if ( mkdir_p(tmp_in_root, 0777) != 0 )
 | 
			
		||||
		error(1, errno, "mkdir: `%s'", tmp_in_root);
 | 
			
		||||
	on_exit(cleanup_file_or_directory, tmp_in_root);
 | 
			
		||||
 | 
			
		||||
	if ( fork_and_wait_or_death() )
 | 
			
		||||
	{
 | 
			
		||||
		const char* cmd_argv[] =
 | 
			
		||||
		{
 | 
			
		||||
			tar,
 | 
			
		||||
			"--extract",
 | 
			
		||||
			"--directory", tmp_in_root,
 | 
			
		||||
			"--file", porttix_path,
 | 
			
		||||
			"--strip-components=1",
 | 
			
		||||
			NULL,
 | 
			
		||||
		};
 | 
			
		||||
		execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
		error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	char* porttixinfo_path = join_paths(tmp_in_root, "porttixinfo");
 | 
			
		||||
	FILE* porttixinfo_fp = fopen(porttixinfo_path, "r");
 | 
			
		||||
	if ( !porttixinfo_fp )
 | 
			
		||||
	{
 | 
			
		||||
		if ( errno == ENOENT )
 | 
			
		||||
			error(0, 0, "`%s' doesn't appear to be an archived port tix",
 | 
			
		||||
			            porttix_path);
 | 
			
		||||
		error(1, errno, "`%s'", porttixinfo_path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	char* tmp_out_root = print_string("%s/tmppid.%ju.out", tmp, (uintmax_t) getpid());
 | 
			
		||||
	if ( mkdir_p(tmp_out_root, 0777) != 0 )
 | 
			
		||||
		error(1, errno, "mkdir: `%s'", tmp_out_root);
 | 
			
		||||
	on_exit(cleanup_file_or_directory, tmp_out_root);
 | 
			
		||||
 | 
			
		||||
	char* package_name = NULL;
 | 
			
		||||
	char* srctix_path = NULL;
 | 
			
		||||
 | 
			
		||||
	char* line = NULL;
 | 
			
		||||
	size_t line_size = 0;
 | 
			
		||||
	ssize_t line_len;
 | 
			
		||||
	while ( 0 < (line_len = getline(&line, &line_size, porttixinfo_fp)) )
 | 
			
		||||
	{
 | 
			
		||||
		if ( line_len && line[line_len-1] == '\n' )
 | 
			
		||||
			line[--line_len] = '\0';
 | 
			
		||||
		char* first_space = strchr(line, ' ');
 | 
			
		||||
		if ( !first_space )
 | 
			
		||||
			error(1, errno, "`%s`: malformed line `%s'",
 | 
			
		||||
			      porttixinfo_path, line);
 | 
			
		||||
		*first_space = '\0';
 | 
			
		||||
		const char* function = line;
 | 
			
		||||
		const char* parameter = first_space + 1;
 | 
			
		||||
 | 
			
		||||
		if ( !strcmp(function, "package_name") )
 | 
			
		||||
		{
 | 
			
		||||
			if ( package_name )
 | 
			
		||||
				error(1, errno, "`%s`: unexpected additional package name `%s'",
 | 
			
		||||
				      porttixinfo_path, parameter);
 | 
			
		||||
			if ( !is_file_name(parameter) )
 | 
			
		||||
				error(1, errno, "`%s`: malformed package name `%s'",
 | 
			
		||||
				      porttixinfo_path, parameter);
 | 
			
		||||
			package_name = strdup(parameter);
 | 
			
		||||
			srctix_path = join_paths(tmp_out_root, package_name);
 | 
			
		||||
			if ( mkdir_p(srctix_path, 0777) != 0 )
 | 
			
		||||
				error(1, errno, "mkdir: `%s'", srctix_path);
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !package_name )
 | 
			
		||||
			error(1, errno, "`%s`: expected package name before `%s'",
 | 
			
		||||
			      porttixinfo_path, function);
 | 
			
		||||
		else if ( !strcmp(function, "tar_extract") )
 | 
			
		||||
		{
 | 
			
		||||
			if ( !is_file_name(parameter) )
 | 
			
		||||
				error(1, errno, "`%s`: malformed tarball filename `%s'",
 | 
			
		||||
				      porttixinfo_path, parameter);
 | 
			
		||||
			char* tarball_path = join_paths(tmp_in_root, parameter);
 | 
			
		||||
			if ( fork_and_wait_or_death() )
 | 
			
		||||
			{
 | 
			
		||||
				const char* cmd_argv[] =
 | 
			
		||||
				{
 | 
			
		||||
					tar,
 | 
			
		||||
					"--extract",
 | 
			
		||||
					"--directory", srctix_path,
 | 
			
		||||
					"--file", tarball_path,
 | 
			
		||||
					"--strip-components=1",
 | 
			
		||||
					NULL,
 | 
			
		||||
				};
 | 
			
		||||
				execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
				error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
			}
 | 
			
		||||
			free(tarball_path);
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !strcmp(function, "apply_normalize") )
 | 
			
		||||
		{
 | 
			
		||||
			if ( !is_file_name(parameter) )
 | 
			
		||||
				error(1, errno, "`%s`: malformed normalize filename `%s'",
 | 
			
		||||
				      porttixinfo_path, parameter);
 | 
			
		||||
			char* rmpatch_path = join_paths(tmp_in_root, parameter);
 | 
			
		||||
			if ( fork_and_wait_or_death() )
 | 
			
		||||
			{
 | 
			
		||||
				const char* cmd_argv[] =
 | 
			
		||||
				{
 | 
			
		||||
					tix_rmpatch,
 | 
			
		||||
					"--directory", srctix_path,
 | 
			
		||||
					"--",
 | 
			
		||||
					rmpatch_path,
 | 
			
		||||
					NULL,
 | 
			
		||||
				};
 | 
			
		||||
				execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
				error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
			}
 | 
			
		||||
			free(rmpatch_path);
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !strcmp(function, "apply_patch") )
 | 
			
		||||
		{
 | 
			
		||||
			if ( !is_file_name(parameter) )
 | 
			
		||||
				error(1, errno, "`%s`: malformed patch filename `%s'",
 | 
			
		||||
				      porttixinfo_path, parameter);
 | 
			
		||||
			char* patch_path = join_paths(tmp_in_root, parameter);
 | 
			
		||||
			if ( fork_and_wait_or_death() )
 | 
			
		||||
			{
 | 
			
		||||
				const char* cmd_argv[] =
 | 
			
		||||
				{
 | 
			
		||||
					patch,
 | 
			
		||||
					"--strip=1",
 | 
			
		||||
					"--silent",
 | 
			
		||||
					"--directory", srctix_path,
 | 
			
		||||
					"--input", patch_path,
 | 
			
		||||
					NULL,
 | 
			
		||||
				};
 | 
			
		||||
				execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
				error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
			}
 | 
			
		||||
			free(patch_path);
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !strcmp(function, "apply_execpatch") )
 | 
			
		||||
		{
 | 
			
		||||
			if ( !is_file_name(parameter) )
 | 
			
		||||
				error(1, errno, "`%s`: malformed execpatch filename `%s'",
 | 
			
		||||
				      porttixinfo_path, parameter);
 | 
			
		||||
			char* execpatch_path = join_paths(tmp_in_root, parameter);
 | 
			
		||||
			if ( fork_and_wait_or_death() )
 | 
			
		||||
			{
 | 
			
		||||
				const char* cmd_argv[] =
 | 
			
		||||
				{
 | 
			
		||||
					tix_execpatch,
 | 
			
		||||
					"--directory", srctix_path,
 | 
			
		||||
					"--",
 | 
			
		||||
					execpatch_path,
 | 
			
		||||
					NULL,
 | 
			
		||||
				};
 | 
			
		||||
				execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
				error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
			}
 | 
			
		||||
			free(execpatch_path);
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			error(1, errno, "`%s`: unsupported function `%s'",
 | 
			
		||||
			      porttixinfo_path, function);
 | 
			
		||||
	}
 | 
			
		||||
	free(line);
 | 
			
		||||
 | 
			
		||||
	fclose(porttixinfo_fp);
 | 
			
		||||
	free(porttixinfo_path);
 | 
			
		||||
 | 
			
		||||
	if ( !output )
 | 
			
		||||
		output = print_string("%s/%s.srctix.tar.xz", output_directory, package_name);
 | 
			
		||||
 | 
			
		||||
	if ( fork_and_wait_or_death() )
 | 
			
		||||
	{
 | 
			
		||||
		const char* cmd_argv[] =
 | 
			
		||||
		{
 | 
			
		||||
			tar,
 | 
			
		||||
			"--create",
 | 
			
		||||
			"--xz",
 | 
			
		||||
			"--directory", tmp_out_root,
 | 
			
		||||
			"--file", output,
 | 
			
		||||
			"--",
 | 
			
		||||
			package_name,
 | 
			
		||||
			NULL,
 | 
			
		||||
		};
 | 
			
		||||
		execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
		error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	free(srctix_path);
 | 
			
		||||
	free(package_name);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										713
									
								
								tix/tix-build.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										713
									
								
								tix/tix-build.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,713 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2013.
 | 
			
		||||
 | 
			
		||||
    This file is part of Tix.
 | 
			
		||||
 | 
			
		||||
    Tix is free software: you can redistribute it and/or modify it under the
 | 
			
		||||
    terms of the GNU General Public License as published by the Free Software
 | 
			
		||||
    Foundation, either version 3 of the License, or (at your option) any later
 | 
			
		||||
    version.
 | 
			
		||||
 | 
			
		||||
    Tix 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 General Public License for more
 | 
			
		||||
    details.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    Tix. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    tix-build.cpp
 | 
			
		||||
    Compile a source tix into a tix suitable for installation.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#define __STDC_CONSTANT_MACROS
 | 
			
		||||
#define __STDC_LIMIT_MACROS
 | 
			
		||||
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <error.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
enum build_step
 | 
			
		||||
{
 | 
			
		||||
	BUILD_STEP_NO_SUCH_STEP,
 | 
			
		||||
	BUILD_STEP_START,
 | 
			
		||||
	BUILD_STEP_PRE_CLEAN,
 | 
			
		||||
	BUILD_STEP_CONFIGURE,
 | 
			
		||||
	BUILD_STEP_BUILD,
 | 
			
		||||
	BUILD_STEP_INSTALL,
 | 
			
		||||
	BUILD_STEP_POST_INSTALL,
 | 
			
		||||
	BUILD_STEP_PACKAGE,
 | 
			
		||||
	BUILD_STEP_POST_CLEAN,
 | 
			
		||||
	BUILD_STEP_END,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
bool should_do_build_step(enum build_step step,
 | 
			
		||||
                   enum build_step start,
 | 
			
		||||
                   enum build_step end)
 | 
			
		||||
{
 | 
			
		||||
	return start <= step && step <= end;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define SHOULD_DO_BUILD_STEP(step, minfo) \
 | 
			
		||||
        should_do_build_step((step), (minfo)->start_step, (minfo)->end_step)
 | 
			
		||||
 | 
			
		||||
enum build_step step_of_step_name(const char* step_name)
 | 
			
		||||
{
 | 
			
		||||
	if ( !strcmp(step_name, "start") )
 | 
			
		||||
		return BUILD_STEP_START;
 | 
			
		||||
	if ( !strcmp(step_name, "pre-clean") )
 | 
			
		||||
		return BUILD_STEP_PRE_CLEAN;
 | 
			
		||||
	if ( !strcmp(step_name, "configure") )
 | 
			
		||||
		return BUILD_STEP_CONFIGURE;
 | 
			
		||||
	if ( !strcmp(step_name, "build") )
 | 
			
		||||
		return BUILD_STEP_BUILD;
 | 
			
		||||
	if ( !strcmp(step_name, "install") )
 | 
			
		||||
		return BUILD_STEP_INSTALL;
 | 
			
		||||
	if ( !strcmp(step_name, "post-install") )
 | 
			
		||||
		return BUILD_STEP_POST_INSTALL;
 | 
			
		||||
	if ( !strcmp(step_name, "post-clean") )
 | 
			
		||||
		return BUILD_STEP_POST_CLEAN;
 | 
			
		||||
	if ( !strcmp(step_name, "package") )
 | 
			
		||||
		return BUILD_STEP_PACKAGE;
 | 
			
		||||
	if ( !strcmp(step_name, "clean") )
 | 
			
		||||
		return BUILD_STEP_POST_CLEAN;
 | 
			
		||||
	if ( !strcmp(step_name, "end") )
 | 
			
		||||
		return BUILD_STEP_END;
 | 
			
		||||
	return BUILD_STEP_NO_SUCH_STEP;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
	char* build;
 | 
			
		||||
	char* build_dir;
 | 
			
		||||
	char* destination;
 | 
			
		||||
	char* host;
 | 
			
		||||
	char* make;
 | 
			
		||||
	char* makeflags;
 | 
			
		||||
	char* package_dir;
 | 
			
		||||
	char* package_info_path;
 | 
			
		||||
	char* package_name;
 | 
			
		||||
	char* prefix;
 | 
			
		||||
	char* sysroot;
 | 
			
		||||
	char* tar;
 | 
			
		||||
	char* target;
 | 
			
		||||
	char* tmp;
 | 
			
		||||
	string_array_t package_info;
 | 
			
		||||
	enum build_step start_step;
 | 
			
		||||
	enum build_step end_step;
 | 
			
		||||
} metainfo_t;
 | 
			
		||||
 | 
			
		||||
void emit_pkg_config_wrapper(metainfo_t* minfo)
 | 
			
		||||
{
 | 
			
		||||
	char* bindir = print_string("%s/tmppid.%ju.bin", minfo->tmp, (uintmax_t) getpid());
 | 
			
		||||
	if ( mkdir_p(bindir, 0777) != 0 )
 | 
			
		||||
		error(1, errno, "mkdir: `%s'", bindir);
 | 
			
		||||
 | 
			
		||||
	on_exit(cleanup_file_or_directory, strdup(bindir));
 | 
			
		||||
 | 
			
		||||
	// Create a pkg-config script for the build system.
 | 
			
		||||
	char* pkg_config_for_build_path = print_string("%s/build-pkg-config", bindir);
 | 
			
		||||
	FILE* pkg_config_for_build = fopen(pkg_config_for_build_path, "w");
 | 
			
		||||
	if ( !pkg_config_for_build )
 | 
			
		||||
		error(1, errno, "`%s'", pkg_config_for_build_path);
 | 
			
		||||
	fprintf(pkg_config_for_build, "#!/bin/sh\n");
 | 
			
		||||
	fprint_shell_variable_assignment(pkg_config_for_build, "PATH", getenv("PATH"));
 | 
			
		||||
	fprint_shell_variable_assignment(pkg_config_for_build, "PKG_CONFIG", getenv("PKG_CONFIG"));
 | 
			
		||||
	fprint_shell_variable_assignment(pkg_config_for_build, "PKG_CONFIG_PATH", getenv("PKG_CONFIG_PATH"));
 | 
			
		||||
	fprint_shell_variable_assignment(pkg_config_for_build, "PKG_CONFIG_SYSROOT_DIR", getenv("PKG_CONFIG_SYSROOT_DIR"));
 | 
			
		||||
	fprint_shell_variable_assignment(pkg_config_for_build, "PKG_CONFIG_FOR_BUILD", getenv("PKG_CONFIG_FOR_BUILD"));
 | 
			
		||||
	fprintf(pkg_config_for_build, "exec ${PKG_CONFIG:-pkg-config} \"$@\"\n");
 | 
			
		||||
	fflush(pkg_config_for_build);
 | 
			
		||||
	fchmod_plus_x(fileno(pkg_config_for_build));
 | 
			
		||||
	fclose(pkg_config_for_build);
 | 
			
		||||
	free(pkg_config_for_build_path);
 | 
			
		||||
 | 
			
		||||
	// Create a pkg-config script for the host system.
 | 
			
		||||
	char* pkg_config_path = print_string("%s/pkg-config", bindir);
 | 
			
		||||
	FILE* pkg_config = fopen(pkg_config_path, "w");
 | 
			
		||||
	if ( !pkg_config )
 | 
			
		||||
		error(1, errno, "`%s'", pkg_config_path);
 | 
			
		||||
	fprintf(pkg_config, "#!/bin/sh\n");
 | 
			
		||||
	fprint_shell_variable_assignment(pkg_config, "PATH", getenv("PATH"));
 | 
			
		||||
	fprint_shell_variable_assignment(pkg_config, "PKG_CONFIG", getenv("PKG_CONFIG"));
 | 
			
		||||
	fprintf(pkg_config, "exec ${PKG_CONFIG:-pkg-config} --static \"$@\"\n");
 | 
			
		||||
	fflush(pkg_config);
 | 
			
		||||
	fchmod_plus_x(fileno(pkg_config));
 | 
			
		||||
	fclose(pkg_config);
 | 
			
		||||
	free(pkg_config_path);
 | 
			
		||||
 | 
			
		||||
	// Point to the correct pkg-config configuration through the environment.
 | 
			
		||||
	char* var_pkg_config = print_string("%s/pkg-config", bindir);
 | 
			
		||||
	char* var_pkg_config_for_build = print_string("%s/build-pkg-config", bindir);
 | 
			
		||||
	char* var_pkg_config_libdir =
 | 
			
		||||
		print_string("%s%s/%s/lib/pkgconfig",
 | 
			
		||||
		             minfo->sysroot, minfo->prefix, minfo->host);
 | 
			
		||||
	char* var_pkg_config_path = print_string("%s", var_pkg_config_libdir);
 | 
			
		||||
	char* var_pkg_config_sysroot_dir = print_string("%s", minfo->sysroot);
 | 
			
		||||
	setenv("PKG_CONFIG", var_pkg_config, 1);
 | 
			
		||||
	setenv("PKG_CONFIG_FOR_BUILD", var_pkg_config_for_build, 1);
 | 
			
		||||
	setenv("PKG_CONFIG_LIBDIR", var_pkg_config_libdir, 1);
 | 
			
		||||
	setenv("PKG_CONFIG_PATH", var_pkg_config_path, 1);
 | 
			
		||||
	setenv("PKG_CONFIG_SYSROOT_DIR", var_pkg_config_sysroot_dir, 1);
 | 
			
		||||
	free(var_pkg_config);
 | 
			
		||||
	free(var_pkg_config_for_build);
 | 
			
		||||
	free(var_pkg_config_libdir);
 | 
			
		||||
	free(var_pkg_config_path);
 | 
			
		||||
	free(var_pkg_config_sysroot_dir);
 | 
			
		||||
 | 
			
		||||
	char* new_path = print_string("%s:%s", bindir, getenv("PATH") ? getenv("PATH") : "");
 | 
			
		||||
	setenv("PATH", new_path, 1);
 | 
			
		||||
	free(new_path);
 | 
			
		||||
 | 
			
		||||
	free(bindir);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Configure(metainfo_t* minfo)
 | 
			
		||||
{
 | 
			
		||||
	if ( fork_and_wait_or_recovery() )
 | 
			
		||||
	{
 | 
			
		||||
		string_array_t* pkg_info = &minfo->package_info;
 | 
			
		||||
		const char* configure_raw =
 | 
			
		||||
			dictionary_get(pkg_info, "pkg.configure.cmd", "./configure");
 | 
			
		||||
		char* configure;
 | 
			
		||||
		if ( strcmp(minfo->build_dir, minfo->package_dir) == 0 )
 | 
			
		||||
			configure = strdup(configure_raw);
 | 
			
		||||
		else
 | 
			
		||||
			configure = print_string("%s/%s", minfo->package_dir, configure_raw);
 | 
			
		||||
		const char* conf_extra_args =
 | 
			
		||||
			dictionary_get(pkg_info, "pkg.configure.args", "");
 | 
			
		||||
		const char* conf_extra_vars =
 | 
			
		||||
			dictionary_get(pkg_info, "pkg.configure.vars", "");
 | 
			
		||||
		bool with_sysroot =
 | 
			
		||||
			parse_boolean(dictionary_get(pkg_info, "pkg.configure.with-sysroot",
 | 
			
		||||
			                                        "false"));
 | 
			
		||||
		bool with_build_sysroot =
 | 
			
		||||
			parse_boolean(dictionary_get(pkg_info, "pkg.configure.with-build-sysroot",
 | 
			
		||||
			                                        "false"));
 | 
			
		||||
		if ( chdir(minfo->build_dir) != 0 )
 | 
			
		||||
			error(1, errno, "chdir: `%s'", minfo->build_dir);
 | 
			
		||||
		string_array_t env_vars = string_array_make();
 | 
			
		||||
		string_array_append_token_string(&env_vars, conf_extra_vars);
 | 
			
		||||
		for ( size_t i = 0; i < env_vars.length; i++ )
 | 
			
		||||
		{
 | 
			
		||||
			char* key = env_vars.strings[i];
 | 
			
		||||
			assert(key);
 | 
			
		||||
			char* assignment = strchr((char*) key, '=');
 | 
			
		||||
			if ( !assignment )
 | 
			
		||||
				continue;
 | 
			
		||||
			*assignment = '\0';
 | 
			
		||||
			char* value = assignment+1;
 | 
			
		||||
			setenv(key, value, 1);
 | 
			
		||||
		}
 | 
			
		||||
		const char* fixed_cmd_argv[] =
 | 
			
		||||
		{
 | 
			
		||||
			configure,
 | 
			
		||||
			print_string("--build=%s", minfo->build),
 | 
			
		||||
			print_string("--host=%s", minfo->host),
 | 
			
		||||
			print_string("--target=%s", minfo->target),
 | 
			
		||||
			print_string("--prefix=%s", minfo->prefix),
 | 
			
		||||
			print_string("--exec-prefix=%s/%s", minfo->prefix, minfo->host),
 | 
			
		||||
			NULL
 | 
			
		||||
		};
 | 
			
		||||
		string_array_t args = string_array_make();
 | 
			
		||||
		for ( size_t i = 0; fixed_cmd_argv[i]; i++ )
 | 
			
		||||
			string_array_append(&args, fixed_cmd_argv[i]);
 | 
			
		||||
		if ( minfo->sysroot && with_build_sysroot )
 | 
			
		||||
		{
 | 
			
		||||
			string_array_append(&args, print_string("--with-build-sysroot=%s",
 | 
			
		||||
			                                        minfo->sysroot));
 | 
			
		||||
			if ( minfo->sysroot && with_sysroot )
 | 
			
		||||
				string_array_append(&args, "--with-sysroot=/");
 | 
			
		||||
			unsetenv("HOST_SYSTEM_ROOT");
 | 
			
		||||
		}
 | 
			
		||||
		else if ( minfo->sysroot && with_sysroot )
 | 
			
		||||
		{
 | 
			
		||||
			string_array_append(&args, print_string("--with-sysroot=%s",
 | 
			
		||||
			                                        minfo->sysroot));
 | 
			
		||||
			unsetenv("HOST_SYSTEM_ROOT");
 | 
			
		||||
		}
 | 
			
		||||
		else if ( minfo->sysroot )
 | 
			
		||||
		{
 | 
			
		||||
			setenv("HOST_SYSTEM_ROOT", minfo->sysroot, 1);
 | 
			
		||||
		}
 | 
			
		||||
		string_array_append_token_string(&args, conf_extra_args);
 | 
			
		||||
		string_array_append(&args, NULL);
 | 
			
		||||
		recovery_execvp(args.strings[0], (char* const*) args.strings);
 | 
			
		||||
		error(127, errno, "`%s'", args.strings[0]);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Make(metainfo_t* minfo, const char* make_target,
 | 
			
		||||
          const char* destdir = NULL, bool die_on_error = true,
 | 
			
		||||
          const char* subdir = NULL)
 | 
			
		||||
{
 | 
			
		||||
	if ( (!die_on_error && fork_and_wait_or_death(die_on_error)) ||
 | 
			
		||||
	     (die_on_error && fork_and_wait_or_recovery()) )
 | 
			
		||||
	{
 | 
			
		||||
		string_array_t* pkg_info = &minfo->package_info;
 | 
			
		||||
		char* make = strdup(minfo->make);
 | 
			
		||||
		const char* override_make = dictionary_get(pkg_info, "pkg.make.cmd");
 | 
			
		||||
		const char* make_extra_args = dictionary_get(pkg_info, "pkg.make.args", "");
 | 
			
		||||
		const char* make_extra_vars = dictionary_get(pkg_info, "pkg.make.vars", "");
 | 
			
		||||
		if ( override_make )
 | 
			
		||||
		{
 | 
			
		||||
			free(make);
 | 
			
		||||
			make = join_paths(minfo->package_dir, override_make);
 | 
			
		||||
		}
 | 
			
		||||
		if ( dictionary_get(pkg_info, "pkg.make.needed-vars.CC", NULL) )
 | 
			
		||||
			setenv("CC", strcmp(minfo->build, minfo->host) ?
 | 
			
		||||
			             print_string("%s-gcc", minfo->host) : "gcc", 1);
 | 
			
		||||
		if ( dictionary_get(pkg_info, "pkg.make.needed-vars.CXX", NULL) )
 | 
			
		||||
			setenv("CXX", strcmp(minfo->build, minfo->host) ?
 | 
			
		||||
			              print_string("%s-g++", minfo->host) : "g++", 1);
 | 
			
		||||
		bool with_sysroot =
 | 
			
		||||
			parse_boolean(dictionary_get(pkg_info, "pkg.configure.with-sysroot",
 | 
			
		||||
			                                        "false"));
 | 
			
		||||
		bool with_build_sysroot =
 | 
			
		||||
			parse_boolean(dictionary_get(pkg_info, "pkg.configure.with-build-sysroot",
 | 
			
		||||
			                                        "false"));
 | 
			
		||||
		if ( chdir(minfo->build_dir) != 0 )
 | 
			
		||||
			error(1, errno, "chdir: `%s'", minfo->build_dir);
 | 
			
		||||
		if ( subdir && chdir(subdir) != 0 )
 | 
			
		||||
			error(1, errno, "chdir: `%s/%s'", minfo->build_dir, subdir);
 | 
			
		||||
		if ( destdir )
 | 
			
		||||
			setenv("DESTDIR", destdir, 1);
 | 
			
		||||
		setenv("BUILD", minfo->build, 1);
 | 
			
		||||
		setenv("HOST", minfo->host, 1);
 | 
			
		||||
		setenv("TARGET", minfo->target, 1);
 | 
			
		||||
		if ( minfo->prefix )
 | 
			
		||||
			setenv("PREFIX", minfo->prefix, 1),
 | 
			
		||||
			setenv("EXEC_PREFIX", join_paths(minfo->prefix, minfo->host), 1);
 | 
			
		||||
		else
 | 
			
		||||
			unsetenv("PREFIX"),
 | 
			
		||||
			unsetenv("EXEC_PREFIX");
 | 
			
		||||
		if ( !(with_sysroot || with_build_sysroot) && minfo->sysroot )
 | 
			
		||||
			setenv("HOST_SYSTEM_ROOT", minfo->sysroot, 1);
 | 
			
		||||
		if ( minfo->makeflags )
 | 
			
		||||
			setenv("MAKEFLAGS", minfo->makeflags, 1);
 | 
			
		||||
		setenv("MAKE", minfo->make, 1);
 | 
			
		||||
		string_array_t env_vars = string_array_make();
 | 
			
		||||
		string_array_append_token_string(&env_vars, make_extra_vars);
 | 
			
		||||
		for ( size_t i = 0; i < env_vars.length; i++ )
 | 
			
		||||
		{
 | 
			
		||||
			char* key = env_vars.strings[i];
 | 
			
		||||
			assert(key);
 | 
			
		||||
			char* assignment = strchr((char*) key, '=');
 | 
			
		||||
			if ( !assignment )
 | 
			
		||||
				continue;
 | 
			
		||||
			*assignment = '\0';
 | 
			
		||||
			char* value = assignment+1;
 | 
			
		||||
			setenv(key, value, 1);
 | 
			
		||||
		}
 | 
			
		||||
		const char* fixed_cmd_argv[] =
 | 
			
		||||
		{
 | 
			
		||||
			make,
 | 
			
		||||
			NULL
 | 
			
		||||
		};
 | 
			
		||||
		string_array_t args = string_array_make();
 | 
			
		||||
		for ( size_t i = 0; fixed_cmd_argv[i]; i++ )
 | 
			
		||||
			string_array_append(&args, fixed_cmd_argv[i]);
 | 
			
		||||
		string_array_append_token_string(&args, make_target);
 | 
			
		||||
		string_array_append_token_string(&args, make_extra_args);
 | 
			
		||||
		string_array_append(&args, NULL);
 | 
			
		||||
		if ( die_on_error )
 | 
			
		||||
			recovery_execvp(args.strings[0], (char* const*) args.strings);
 | 
			
		||||
		else
 | 
			
		||||
			execvp(args.strings[0], (char* const*) args.strings);
 | 
			
		||||
		error(127, errno, "`%s'", args.strings[0]);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BuildPackage(metainfo_t* minfo)
 | 
			
		||||
{
 | 
			
		||||
	// Detect which build system we are interfacing with.
 | 
			
		||||
	string_array_t* pinfo = &minfo->package_info;
 | 
			
		||||
	const char* build_system = dictionary_get(pinfo, "pkg.build-system");
 | 
			
		||||
	assert(build_system);
 | 
			
		||||
 | 
			
		||||
	// Determine whether need to do an out-of-directory build.
 | 
			
		||||
	const char* use_build_dir_var =
 | 
			
		||||
		dictionary_get(pinfo, "pkg.configure.use-build-directory", "false");
 | 
			
		||||
	bool use_build_dir = parse_boolean(use_build_dir_var);
 | 
			
		||||
	if ( use_build_dir )
 | 
			
		||||
	{
 | 
			
		||||
		minfo->build_dir = print_string("%s/tmppid.%ju", minfo->tmp,
 | 
			
		||||
		                                (uintmax_t) getpid());
 | 
			
		||||
		if ( mkdir_p(minfo->build_dir, 0777) != 0 )
 | 
			
		||||
			error(1, errno, "mkdir: `%s'", minfo->build_dir);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
		minfo->build_dir = strdup(minfo->package_dir);
 | 
			
		||||
 | 
			
		||||
	// Reset the build directory if needed.
 | 
			
		||||
	const char* default_clean_target =
 | 
			
		||||
		!strcmp(build_system, "configure") ? "distclean" : "clean";
 | 
			
		||||
	const char* clean_target = dictionary_get(pinfo, "pkg.make.clean-target",
 | 
			
		||||
	                                          default_clean_target);
 | 
			
		||||
	const char* ignore_clean_failure_var =
 | 
			
		||||
		dictionary_get(pinfo, "pkg.make.ignore-clean-failure",
 | 
			
		||||
		               !strcmp(build_system, "configure") ? "true" : "false");
 | 
			
		||||
	bool ignore_clean_failure = parse_boolean(ignore_clean_failure_var);
 | 
			
		||||
 | 
			
		||||
	if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_PRE_CLEAN, minfo) && !use_build_dir )
 | 
			
		||||
		Make(minfo, clean_target, NULL, !ignore_clean_failure);
 | 
			
		||||
 | 
			
		||||
	// Configure the build directory if needed.
 | 
			
		||||
	if ( strcmp(build_system, "configure") == 0 &&
 | 
			
		||||
	     SHOULD_DO_BUILD_STEP(BUILD_STEP_CONFIGURE, minfo) )
 | 
			
		||||
		Configure(minfo);
 | 
			
		||||
 | 
			
		||||
	bool location_independent =
 | 
			
		||||
		parse_boolean(dictionary_get(pinfo, "pkg.location-independent", "false"));
 | 
			
		||||
 | 
			
		||||
	const char* subdir = dictionary_get(pinfo, "pkg.subdir", NULL);
 | 
			
		||||
 | 
			
		||||
	const char* build_target = dictionary_get(pinfo, "pkg.make.build-target", "all");
 | 
			
		||||
	const char* install_target = dictionary_get(pinfo, "pkg.make.install-target", "install");
 | 
			
		||||
 | 
			
		||||
	if ( !location_independent && !minfo->prefix )
 | 
			
		||||
		error(1, 0, "error: %s is not location independent and you need to "
 | 
			
		||||
		            "specify the intended destination prefix using --prefix or "
 | 
			
		||||
		            "PREFIX", minfo->package_name);
 | 
			
		||||
 | 
			
		||||
	if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_BUILD, minfo) )
 | 
			
		||||
		Make(minfo, build_target, NULL, true, subdir);
 | 
			
		||||
 | 
			
		||||
	char* tardir_rel = print_string("%s/%s", minfo->tmp, "tmp-tixbuild");
 | 
			
		||||
	char* destdir_rel = print_string("%s/%s", minfo->tmp, "tmp-tixbuild/data");
 | 
			
		||||
	char* tixdir_rel = print_string("%s/%s", minfo->tmp, "tmp-tixbuild/tix");
 | 
			
		||||
	char* tixinfo_rel = print_string("%s/%s", minfo->tmp, "tmp-tixbuild/tix/tixinfo");
 | 
			
		||||
 | 
			
		||||
	while ( mkdir(tardir_rel, 0777) != 0 )
 | 
			
		||||
	{
 | 
			
		||||
		if ( errno != EEXIST )
 | 
			
		||||
			error(1, errno, "mkdir: `%s'", tardir_rel);
 | 
			
		||||
		if ( rmdir(tardir_rel) != 0 )
 | 
			
		||||
			error(1, errno, "rmdir: `%s'", tardir_rel);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( mkdir(destdir_rel, 0777) != 0 )
 | 
			
		||||
		error(1, errno, "mkdir: `%s'", destdir_rel);
 | 
			
		||||
	if ( mkdir(tixdir_rel, 0777) != 0 )
 | 
			
		||||
		error(1, errno, "mkdir: `%s'", tixdir_rel);
 | 
			
		||||
 | 
			
		||||
	char* destdir = canonicalize_file_name(destdir_rel);
 | 
			
		||||
	if ( !destdir )
 | 
			
		||||
		error(1, errno, "canonicalize_file_name: `%s'", destdir_rel);
 | 
			
		||||
 | 
			
		||||
	if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_INSTALL, minfo) )
 | 
			
		||||
		Make(minfo, install_target, destdir, true, subdir);
 | 
			
		||||
 | 
			
		||||
	const char* post_install_cmd = dictionary_get(pinfo, "pkg.post-install.cmd");
 | 
			
		||||
 | 
			
		||||
	if ( post_install_cmd &&
 | 
			
		||||
	     SHOULD_DO_BUILD_STEP(BUILD_STEP_POST_INSTALL, minfo) &&
 | 
			
		||||
	     fork_and_wait_or_recovery() )
 | 
			
		||||
	{
 | 
			
		||||
		if ( chdir(minfo->package_dir) != 0 )
 | 
			
		||||
			error(1, errno, "chdir: `%s'", minfo->package_dir);
 | 
			
		||||
		setenv("TIX_BUILD_DIR", minfo->build_dir, 1);
 | 
			
		||||
		setenv("TIX_SOURCE_DIR", minfo->package_dir, 1);
 | 
			
		||||
		setenv("TIX_INSTALL_DIR", destdir, 1);
 | 
			
		||||
		setenv("BUILD", minfo->build, 1);
 | 
			
		||||
		setenv("HOST", minfo->host, 1);
 | 
			
		||||
		setenv("TARGET", minfo->target, 1);
 | 
			
		||||
		if ( minfo->prefix )
 | 
			
		||||
			setenv("PREFIX", minfo->prefix, 1),
 | 
			
		||||
			setenv("EXEC_PREFIX", join_paths(minfo->prefix, minfo->host), 1);
 | 
			
		||||
		else
 | 
			
		||||
			unsetenv("PREFIX"),
 | 
			
		||||
			unsetenv("EXEC_PREFIX");
 | 
			
		||||
		const char* cmd_argv[] =
 | 
			
		||||
		{
 | 
			
		||||
			post_install_cmd,
 | 
			
		||||
			NULL
 | 
			
		||||
		};
 | 
			
		||||
		recovery_execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
		error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const char* tix_ext = ".tix.tar.xz";
 | 
			
		||||
	char* package_tix = print_string("%s/%s%s", minfo->destination,
 | 
			
		||||
	                                 minfo->package_name, tix_ext);
 | 
			
		||||
 | 
			
		||||
	FILE* tixinfo_fp = fopen(tixinfo_rel, "w");
 | 
			
		||||
	if ( !tixinfo_fp )
 | 
			
		||||
		error(1, errno, "`%s'", tixinfo_rel);
 | 
			
		||||
 | 
			
		||||
	const char* runtime_deps = dictionary_get(pinfo, "pkg.runtime-deps");
 | 
			
		||||
 | 
			
		||||
	fprintf(tixinfo_fp, "tix.version=1\n");
 | 
			
		||||
	fprintf(tixinfo_fp, "tix.class=tix\n");
 | 
			
		||||
	fprintf(tixinfo_fp, "tix.platform=%s\n", minfo->host);
 | 
			
		||||
	fprintf(tixinfo_fp, "pkg.name=%s\n", minfo->package_name);
 | 
			
		||||
	if ( runtime_deps )
 | 
			
		||||
		fprintf(tixinfo_fp, "pkg.runtime-deps=%s\n", runtime_deps);
 | 
			
		||||
	if ( location_independent )
 | 
			
		||||
		fprintf(tixinfo_fp, "pkg.location-independent=true\n");
 | 
			
		||||
	else
 | 
			
		||||
		fprintf(tixinfo_fp, "pkg.prefix=%s\n", minfo->prefix);
 | 
			
		||||
 | 
			
		||||
	if ( ferror(tixinfo_fp) )
 | 
			
		||||
		error(1, errno, "write: `%s'", tixinfo_rel);
 | 
			
		||||
 | 
			
		||||
	fclose(tixinfo_fp);
 | 
			
		||||
 | 
			
		||||
	if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_PACKAGE, minfo) )
 | 
			
		||||
	{
 | 
			
		||||
		printf("Creating `%s'...\n", package_tix);
 | 
			
		||||
		if ( fork_and_wait_or_recovery() )
 | 
			
		||||
		{
 | 
			
		||||
			const char* cmd_argv[] =
 | 
			
		||||
			{
 | 
			
		||||
				minfo->tar,
 | 
			
		||||
				"-C", tardir_rel,
 | 
			
		||||
				"--remove-files",
 | 
			
		||||
				"--create",
 | 
			
		||||
				"--xz",
 | 
			
		||||
				"--file", package_tix,
 | 
			
		||||
				"tix",
 | 
			
		||||
				"data",
 | 
			
		||||
				NULL
 | 
			
		||||
			};
 | 
			
		||||
			recovery_execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
			error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	unlink(tixinfo_rel);
 | 
			
		||||
	rmdir(destdir_rel);
 | 
			
		||||
	rmdir(tixdir_rel);
 | 
			
		||||
	rmdir(tardir_rel);
 | 
			
		||||
 | 
			
		||||
	free(tardir_rel);
 | 
			
		||||
	free(destdir_rel);
 | 
			
		||||
	free(tixdir_rel);
 | 
			
		||||
	free(tixinfo_rel);
 | 
			
		||||
 | 
			
		||||
	free(package_tix);
 | 
			
		||||
 | 
			
		||||
	// Clean the build directory after the successful build.
 | 
			
		||||
	if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_POST_CLEAN, minfo) )
 | 
			
		||||
		Make(minfo, clean_target, NULL, !ignore_clean_failure);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void VerifySourceTixInformation(metainfo_t* minfo)
 | 
			
		||||
{
 | 
			
		||||
	const char* pipath = minfo->package_info_path;
 | 
			
		||||
	string_array_t* pinfo = &minfo->package_info;
 | 
			
		||||
	const char* tix_version = VerifyInfoVariable(pinfo, "tix.version", pipath);
 | 
			
		||||
	if ( atoi(tix_version) != 1 )
 | 
			
		||||
		error(1, 0, "error: `%s': tix version `%s' not supported", pipath,
 | 
			
		||||
		            tix_version);
 | 
			
		||||
	const char* tix_class = VerifyInfoVariable(pinfo, "tix.class", pipath);
 | 
			
		||||
	if ( !strcmp(tix_class, "tix") )
 | 
			
		||||
		error(1, 0, "error: `%s': this object is a binary tix and is already "
 | 
			
		||||
		            "compiled.\n", pipath);
 | 
			
		||||
	if ( strcmp(tix_class, "srctix") )
 | 
			
		||||
		error(1, 0, "error: `%s': tix class `%s' is not `srctix': this object "
 | 
			
		||||
		            "is not suitable for compilation.", pipath, tix_class);
 | 
			
		||||
	VerifyInfoVariable(pinfo, "pkg.name", pipath);
 | 
			
		||||
	VerifyInfoVariable(pinfo, "pkg.build-system", pipath);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: The MAKEFLAGS variable is actually not in the same format as the token
 | 
			
		||||
//       string language. It appears that GNU make doesn't escape " characters,
 | 
			
		||||
//       but instead consider them normal characters. This should work as
 | 
			
		||||
//       expected, though, as long as the MAKEFLAGS variable doesn't contain any
 | 
			
		||||
//       quote characters.
 | 
			
		||||
void PurifyMakeflags()
 | 
			
		||||
{
 | 
			
		||||
	const char* makeflags_environment = getenv("MAKEFLAGS");
 | 
			
		||||
	if ( !makeflags_environment )
 | 
			
		||||
		return;
 | 
			
		||||
	string_array_t makeflags = string_array_make();
 | 
			
		||||
	string_array_append_token_string(&makeflags, makeflags_environment);
 | 
			
		||||
	for ( size_t i = 0; i < makeflags.length; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		char* flag = makeflags.strings[i];
 | 
			
		||||
		assert(flag);
 | 
			
		||||
		if ( flag[0] == '-' )
 | 
			
		||||
			continue;
 | 
			
		||||
		if ( !strchr(flag, '=') )
 | 
			
		||||
			continue;
 | 
			
		||||
		free(flag);
 | 
			
		||||
		for ( size_t n = i + 1; n < makeflags.length; n++ )
 | 
			
		||||
			makeflags.strings[n-1] = makeflags.strings[n];
 | 
			
		||||
		makeflags.length--;
 | 
			
		||||
	}
 | 
			
		||||
	char* new_makeflags_environment = token_string_of_string_array(&makeflags);
 | 
			
		||||
	assert(new_makeflags_environment);
 | 
			
		||||
	setenv("MAKEFLAGS", new_makeflags_environment, 1);
 | 
			
		||||
	free(new_makeflags_environment);
 | 
			
		||||
	string_array_reset(&makeflags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Usage(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(fp, "Usage: %s [OPTION]... PACKAGE\n", argv0);
 | 
			
		||||
	fprintf(fp, "Compile a source tix into a tix suitable for installation.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Help(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	Usage(fp, argv0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Version(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	Usage(fp, argv0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
	metainfo_t minfo;
 | 
			
		||||
	minfo.build = NULL;
 | 
			
		||||
	minfo.destination = strdup(getenv_def("TIX_BUILD_DESTINATION", "."));
 | 
			
		||||
	minfo.host = NULL;
 | 
			
		||||
	minfo.makeflags = strdup_null(getenv_def("MAKEFLAGS", NULL));
 | 
			
		||||
	minfo.make = strdup(getenv_def("MAKE", "make"));
 | 
			
		||||
	minfo.prefix = strdup_null(getenv_def("PREFIX", NULL));
 | 
			
		||||
	minfo.sysroot = strdup_null(getenv_def("HOST_SYSTEM_ROOT", NULL));
 | 
			
		||||
	minfo.target = NULL;
 | 
			
		||||
	minfo.tar = strdup(getenv_def("TAR", "tar"));
 | 
			
		||||
	minfo.tmp = strdup(getenv_def("BUILDTMP", "."));
 | 
			
		||||
	char* start_step_string = strdup("start");
 | 
			
		||||
	char* end_step_string = strdup("end");
 | 
			
		||||
 | 
			
		||||
	const char* argv0 = argv[0];
 | 
			
		||||
	for ( int i = 0; i < argc; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		const char* arg = argv[i];
 | 
			
		||||
		if ( arg[0] != '-' )
 | 
			
		||||
			continue;
 | 
			
		||||
		argv[i] = NULL;
 | 
			
		||||
		if ( !strcmp(arg, "--") )
 | 
			
		||||
			break;
 | 
			
		||||
		if ( arg[1] != '-' )
 | 
			
		||||
		{
 | 
			
		||||
			while ( char c = *++arg ) switch ( c )
 | 
			
		||||
			{
 | 
			
		||||
			default:
 | 
			
		||||
				fprintf(stderr, "%s: unknown option -- `%c'\n", argv0, c);
 | 
			
		||||
				Usage(stderr, argv0);
 | 
			
		||||
				exit(1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !strcmp(arg, "--help") ) { Help(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( !strcmp(arg, "--usage") ) { Usage(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( !strcmp(arg, "--version") ) { Version(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--build", &minfo.build) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--destination", &minfo.destination) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--end", &end_step_string) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--host", &minfo.host) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--makeflags", &minfo.makeflags) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--make", &minfo.make) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--prefix", &minfo.prefix) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--start", &start_step_string) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--sysroot", &minfo.sysroot) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--target", &minfo.target) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--tar", &minfo.tar) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--tmp", &minfo.tmp) ) { }
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
 | 
			
		||||
			Usage(stderr, argv0);
 | 
			
		||||
			exit(1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( !(minfo.start_step = step_of_step_name(start_step_string)) )
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "%s: no such step `%s'\n", argv0, start_step_string);
 | 
			
		||||
		Usage(stderr, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( !(minfo.end_step = step_of_step_name(end_step_string)) )
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "%s: no such step `%s'\n", argv0, end_step_string);
 | 
			
		||||
		Usage(stderr, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( argc == 1 )
 | 
			
		||||
	{
 | 
			
		||||
		Usage(stdout, argv0);
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CompactArguments(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	PurifyMakeflags();
 | 
			
		||||
 | 
			
		||||
	if ( minfo.prefix && !strcmp(minfo.prefix, "/") )
 | 
			
		||||
		minfo.prefix[0] = '\0';
 | 
			
		||||
 | 
			
		||||
	if ( argc < 2 )
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "%s: no package specified\n", argv0);
 | 
			
		||||
		Usage(stderr, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( 2 < argc )
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "%s: unexpected extra operand `%s'\n", argv0, argv[2]);
 | 
			
		||||
		Usage(stderr, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	minfo.package_dir = canonicalize_file_name(argv[1]);
 | 
			
		||||
	if ( !minfo.package_dir )
 | 
			
		||||
		error(1, errno, "canonicalize_file_name: `%s'", argv[1]);
 | 
			
		||||
 | 
			
		||||
	if ( !minfo.build && !(minfo.build = GetBuildTriplet()) )
 | 
			
		||||
		error(1, errno, "unable to determine host, use --host or BUILD");
 | 
			
		||||
	if ( !minfo.host && !(minfo.host = strdup_null(getenv("HOST"))) )
 | 
			
		||||
		minfo.host = strdup(minfo.build);
 | 
			
		||||
	if ( !minfo.target && !(minfo.target = strdup_null(getenv("TARGET"))) )
 | 
			
		||||
		minfo.target = strdup(minfo.host);
 | 
			
		||||
 | 
			
		||||
	if ( !IsDirectory(minfo.package_dir) )
 | 
			
		||||
		error(1, errno, "`%s'", minfo.package_dir);
 | 
			
		||||
 | 
			
		||||
	minfo.package_info_path = print_string("%s/tixbuildinfo",
 | 
			
		||||
	                                       minfo.package_dir);
 | 
			
		||||
 | 
			
		||||
	string_array_t* package_info = &(minfo.package_info = string_array_make());
 | 
			
		||||
	if ( !dictionary_append_file_path(package_info, minfo.package_info_path) )
 | 
			
		||||
	{
 | 
			
		||||
		if ( errno == ENOENT )
 | 
			
		||||
			fprintf(stderr, "%s: `%s' doesn't appear to be a source .tix:\n",
 | 
			
		||||
			                argv0, minfo.package_dir);
 | 
			
		||||
		error(1, errno, "`%s'", minfo.package_info_path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	VerifySourceTixInformation(&minfo);
 | 
			
		||||
	minfo.package_name = strdup(dictionary_get(package_info, "pkg.name"));
 | 
			
		||||
 | 
			
		||||
	emit_pkg_config_wrapper(&minfo);
 | 
			
		||||
 | 
			
		||||
	BuildPackage(&minfo);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										175
									
								
								tix/tix-collection.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								tix/tix-collection.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,175 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2013.
 | 
			
		||||
 | 
			
		||||
    This file is part of Tix.
 | 
			
		||||
 | 
			
		||||
    Tix is free software: you can redistribute it and/or modify it under the
 | 
			
		||||
    terms of the GNU General Public License as published by the Free Software
 | 
			
		||||
    Foundation, either version 3 of the License, or (at your option) any later
 | 
			
		||||
    version.
 | 
			
		||||
 | 
			
		||||
    Tix 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 General Public License for more
 | 
			
		||||
    details.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    Tix. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    tix-collection.cpp
 | 
			
		||||
    Administer and configure a tix collection.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#define __STDC_CONSTANT_MACROS
 | 
			
		||||
#define __STDC_LIMIT_MACROS
 | 
			
		||||
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <error.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
void Usage(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(fp, "Usage: %s PREFIX [OPTION]... COMMAND\n", argv0);
 | 
			
		||||
	fprintf(fp, "Administer and configure a tix collection.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Help(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	Usage(fp, argv0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Version(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	Usage(fp, argv0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
	char* collection = strdup_null(getenv_def("TIX_COLLECTION", NULL));
 | 
			
		||||
	char* platform = NULL;
 | 
			
		||||
	char* prefix = NULL;
 | 
			
		||||
 | 
			
		||||
	const char* argv0 = argv[0];
 | 
			
		||||
	for ( int i = 0; i < argc; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		const char* arg = argv[i];
 | 
			
		||||
		if ( arg[0] != '-' )
 | 
			
		||||
			continue;
 | 
			
		||||
		argv[i] = NULL;
 | 
			
		||||
		if ( !strcmp(arg, "--") )
 | 
			
		||||
			break;
 | 
			
		||||
		if ( arg[1] != '-' )
 | 
			
		||||
		{
 | 
			
		||||
			while ( char c = *++arg ) switch ( c )
 | 
			
		||||
			{
 | 
			
		||||
			default:
 | 
			
		||||
				fprintf(stderr, "%s: unknown option -- `%c'\n", argv0, c);
 | 
			
		||||
				Usage(stderr, argv0);
 | 
			
		||||
				exit(1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !strcmp(arg, "--help") ) { Help(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( !strcmp(arg, "--usage") ) { Usage(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( !strcmp(arg, "--version") ) { Version(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--collection", &collection) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--platform", &platform) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--prefix", &prefix) ) { }
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
 | 
			
		||||
			Usage(stderr, argv0);
 | 
			
		||||
			exit(1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( argc == 1 )
 | 
			
		||||
	{
 | 
			
		||||
		Usage(stdout, argv0);
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CompactArguments(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	ParseOptionalCommandLineCollectionPrefix(&collection, &argc, &argv);
 | 
			
		||||
	VerifyCommandLineCollection(&collection);
 | 
			
		||||
 | 
			
		||||
	if ( !prefix )
 | 
			
		||||
		prefix = strdup(collection);
 | 
			
		||||
 | 
			
		||||
	if ( argc == 1 )
 | 
			
		||||
	{
 | 
			
		||||
		error(0, 0, "error: no command specified.");
 | 
			
		||||
		Usage(stderr, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const char* cmd = argv[1];
 | 
			
		||||
	if ( !strcmp(cmd, "create") )
 | 
			
		||||
	{
 | 
			
		||||
		if ( !platform && !(platform = GetBuildTriplet()) )
 | 
			
		||||
			error(1, errno, "unable to determine platform, use --platform");
 | 
			
		||||
 | 
			
		||||
		char* tix_path = join_paths(collection, "tix");
 | 
			
		||||
		if ( mkdir_p(tix_path, 0777) != 0 )
 | 
			
		||||
			error(1, errno, "mkdir: `%s'", tix_path);
 | 
			
		||||
 | 
			
		||||
		char* tixdb_path = join_paths(tix_path, platform);
 | 
			
		||||
		if ( mkdir_p(tixdb_path, 0777) != 0 )
 | 
			
		||||
			error(1, errno, "mkdir: `%s'", tixdb_path);
 | 
			
		||||
 | 
			
		||||
		char* collection_conf_path = join_paths(tixdb_path, "collection.conf");
 | 
			
		||||
		FILE* conf_fp = fopen(collection_conf_path, "wx");
 | 
			
		||||
		if ( !conf_fp && errno == EEXIST )
 | 
			
		||||
			error(1, 0, "error: `%s' already exists, a tix collection is "
 | 
			
		||||
			            "already installed at `%s'.", collection_conf_path,
 | 
			
		||||
		                collection);
 | 
			
		||||
		fprintf(conf_fp, "tix.version=1\n");
 | 
			
		||||
		fprintf(conf_fp, "tix.class=collection\n");
 | 
			
		||||
		fprintf(conf_fp, "collection.prefix=%s\n", !strcmp(prefix, "/") ? "" :
 | 
			
		||||
		                                           prefix);
 | 
			
		||||
		fprintf(conf_fp, "collection.platform=%s\n", platform);
 | 
			
		||||
		fclose(conf_fp);
 | 
			
		||||
		free(collection_conf_path);
 | 
			
		||||
 | 
			
		||||
		const char* repo_list_path = join_paths(tixdb_path, "repository.list");
 | 
			
		||||
		FILE* repo_list_fp = fopen(repo_list_path, "w");
 | 
			
		||||
		if ( !repo_list_fp )
 | 
			
		||||
			error(1, errno, "`%s'", repo_list_path);
 | 
			
		||||
		fclose(repo_list_fp);
 | 
			
		||||
 | 
			
		||||
		const char* inst_list_path = join_paths(tixdb_path, "installed.list");
 | 
			
		||||
		FILE* inst_list_fp = fopen(inst_list_path, "w");
 | 
			
		||||
		if ( !inst_list_fp )
 | 
			
		||||
			error(1, errno, "`%s'", inst_list_path);
 | 
			
		||||
		fclose(inst_list_fp);
 | 
			
		||||
 | 
			
		||||
		printf("Created empty tix collection at `%s' with no repositories.\n",
 | 
			
		||||
		       collection);
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "%s: unknown command: `%s'\n", argv0, cmd);
 | 
			
		||||
		Usage(stderr, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								tix/tix-eradicate-libtool-la
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										28
									
								
								tix/tix-eradicate-libtool-la
									
										
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
#!/bin/sh
 | 
			
		||||
################################################################################
 | 
			
		||||
#
 | 
			
		||||
#   Copyright(C) Jonas 'Sortie' Termansen 2013.
 | 
			
		||||
#
 | 
			
		||||
#   This file is part of Tix.
 | 
			
		||||
#
 | 
			
		||||
#   Tix is free software: you can redistribute it and/or modify it under the
 | 
			
		||||
#   terms of the GNU General Public License as published by the Free Software
 | 
			
		||||
#   Foundation, either version 3 of the License, or (at your option) any later
 | 
			
		||||
#   version.
 | 
			
		||||
#
 | 
			
		||||
#   Tix 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 General Public License for more
 | 
			
		||||
#   details.
 | 
			
		||||
#
 | 
			
		||||
#   You should have received a copy of the GNU General Public License along with
 | 
			
		||||
#   Tix. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
#
 | 
			
		||||
#   tix-eradicate-libtool-la
 | 
			
		||||
#   Deletes useless libtool .la files from the staging directory.
 | 
			
		||||
#
 | 
			
		||||
################################################################################
 | 
			
		||||
 | 
			
		||||
set -e
 | 
			
		||||
cd "$TIX_INSTALL_DIR"
 | 
			
		||||
find | grep '\.la$' | while read la; do rm -f "$la"; done
 | 
			
		||||
							
								
								
									
										224
									
								
								tix/tix-execdiff.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								tix/tix-execdiff.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,224 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2013.
 | 
			
		||||
 | 
			
		||||
    This file is part of Tix.
 | 
			
		||||
 | 
			
		||||
    Tix is free software: you can redistribute it and/or modify it under the
 | 
			
		||||
    terms of the GNU General Public License as published by the Free Software
 | 
			
		||||
    Foundation, either version 3 of the License, or (at your option) any later
 | 
			
		||||
    version.
 | 
			
		||||
 | 
			
		||||
    Tix 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 General Public License for more
 | 
			
		||||
    details.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    Tix. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    tix-execdiff.cpp
 | 
			
		||||
    Reports which files have had the executable bit changed between two trees.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#define __STDC_CONSTANT_MACROS
 | 
			
		||||
#define __STDC_LIMIT_MACROS
 | 
			
		||||
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <error.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <libgen.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
DIR* fdopendupdir(int fd)
 | 
			
		||||
{
 | 
			
		||||
	int newfd = dup(fd);
 | 
			
		||||
	if ( newfd < 0 )
 | 
			
		||||
		return NULL;
 | 
			
		||||
	DIR* result = fdopendir(newfd);
 | 
			
		||||
	if ( !result )
 | 
			
		||||
		return close(newfd), (DIR*) NULL;
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ftestexecutableat(int dirfd, const char* path)
 | 
			
		||||
{
 | 
			
		||||
	struct stat st;
 | 
			
		||||
	if ( fstatat(dirfd, path, &st, AT_SYMLINK_NOFOLLOW) != 0 )
 | 
			
		||||
		return -1;
 | 
			
		||||
	if ( !S_ISREG(st.st_mode) )
 | 
			
		||||
		return errno = EISDIR, -1;
 | 
			
		||||
	if ( faccessat(dirfd, path, X_OK, AT_EACCESS | AT_SYMLINK_NOFOLLOW) == 0 )
 | 
			
		||||
		return 1;
 | 
			
		||||
	else if ( errno == EACCES )
 | 
			
		||||
		return 0;
 | 
			
		||||
	else
 | 
			
		||||
		return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void execdiff(int tree_a, const char* tree_a_path,
 | 
			
		||||
              int tree_b, const char* tree_b_path,
 | 
			
		||||
              const char* relpath)
 | 
			
		||||
{
 | 
			
		||||
	DIR* dir_b = fdopendupdir(tree_b);
 | 
			
		||||
	if ( !dir_b )
 | 
			
		||||
		error(1, errno, "fdopendupdir(`%s`)", tree_b_path);
 | 
			
		||||
	while ( struct dirent* entry = readdir(dir_b) )
 | 
			
		||||
	{
 | 
			
		||||
		if ( !strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..") )
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		char* subrelpath = join_paths(relpath, entry->d_name);
 | 
			
		||||
 | 
			
		||||
		int diropenflags = O_RDONLY | O_DIRECTORY | O_NOFOLLOW;
 | 
			
		||||
		int subtree_b = openat(tree_b, entry->d_name, diropenflags);
 | 
			
		||||
		if ( 0 <= subtree_b )
 | 
			
		||||
		{
 | 
			
		||||
			char* subtree_b_path = join_paths(tree_b_path, entry->d_name);
 | 
			
		||||
			int subtree_a = openat(tree_a, entry->d_name, diropenflags);
 | 
			
		||||
			if ( subtree_a < 0 )
 | 
			
		||||
			{
 | 
			
		||||
				if ( !(errno == ENOTDIR || errno == ELOOP || errno == ENOENT) )
 | 
			
		||||
					error(1, errno, "`%s/%s`", tree_b_path, entry->d_name);
 | 
			
		||||
				execdiff(-1, NULL, subtree_b, subtree_b_path, subrelpath);
 | 
			
		||||
				free(subtree_b_path);
 | 
			
		||||
				close(subtree_b);
 | 
			
		||||
				free(subrelpath);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			char* subtree_a_path = join_paths(tree_a_path, entry->d_name);
 | 
			
		||||
			execdiff(subtree_a, subtree_a_path, subtree_b, subtree_b_path,
 | 
			
		||||
			         subrelpath);
 | 
			
		||||
			free(subtree_a_path);
 | 
			
		||||
			close(subtree_a);
 | 
			
		||||
			free(subtree_b_path);
 | 
			
		||||
			close(subtree_b);
 | 
			
		||||
			free(subrelpath);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !(errno == ENOTDIR || errno == ELOOP) )
 | 
			
		||||
			error(1, errno, "`%s/%s`", tree_b_path, entry->d_name);
 | 
			
		||||
 | 
			
		||||
		int a_executableness = ftestexecutableat(tree_a, entry->d_name);
 | 
			
		||||
		int b_executableness = ftestexecutableat(tree_b, entry->d_name);
 | 
			
		||||
 | 
			
		||||
		if ( (a_executableness == 1) && (b_executableness == 0) )
 | 
			
		||||
		{
 | 
			
		||||
			printf("chmod -x -- '");
 | 
			
		||||
			for ( size_t i = 0; subrelpath[i]; i++ )
 | 
			
		||||
				if ( subrelpath[i] == '\'' )
 | 
			
		||||
					printf("'\\''");
 | 
			
		||||
				else
 | 
			
		||||
					putchar(subrelpath[i]);
 | 
			
		||||
			printf("'\n");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if ( (a_executableness != 1) && (b_executableness == 1) )
 | 
			
		||||
		{
 | 
			
		||||
			printf("chmod +x -- '");
 | 
			
		||||
			for ( size_t i = 0; subrelpath[i]; i++ )
 | 
			
		||||
				if ( subrelpath[i] == '\'' )
 | 
			
		||||
					printf("'\\''");
 | 
			
		||||
				else
 | 
			
		||||
					putchar(subrelpath[i]);
 | 
			
		||||
			printf("'\n");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		free(subrelpath);
 | 
			
		||||
		continue;
 | 
			
		||||
	}
 | 
			
		||||
	closedir(dir_b);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void help(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(fp, "Usage: %s [OPTION]... TREE-A TREE-B\n", argv0);
 | 
			
		||||
	fprintf(fp, "Reports which files have had the executable bit changed between two trees.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void version(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	help(fp, argv0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
	const char* argv0 = argv[0];
 | 
			
		||||
	for ( int i = 0; i < argc; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		const char* arg = argv[i];
 | 
			
		||||
		if ( arg[0] != '-' )
 | 
			
		||||
			continue;
 | 
			
		||||
		argv[i] = NULL;
 | 
			
		||||
		if ( !strcmp(arg, "--") )
 | 
			
		||||
			break;
 | 
			
		||||
		if ( arg[1] != '-' )
 | 
			
		||||
		{
 | 
			
		||||
			while ( char c = *++arg ) switch ( c )
 | 
			
		||||
			{
 | 
			
		||||
			default:
 | 
			
		||||
				fprintf(stderr, "%s: unknown option -- `%c'\n", argv0, c);
 | 
			
		||||
				help(stderr, argv0);
 | 
			
		||||
				exit(1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !strcmp(arg, "--help") ) { help(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( !strcmp(arg, "--version") ) { version(stdout, argv0); exit(0); }
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
 | 
			
		||||
			help(stderr, argv0);
 | 
			
		||||
			exit(1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( argc == 1 )
 | 
			
		||||
	{
 | 
			
		||||
		help(stdout, argv0);
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CompactArguments(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	if ( argc < 3 )
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "%s: you need to specify two directories\n", argv0);
 | 
			
		||||
		help(stderr, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( 3 < argc )
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "%s: unexpected extra operand `%s'\n", argv0, argv[2]);
 | 
			
		||||
		help(stderr, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const char* tree_a_path = argv[1];
 | 
			
		||||
	int tree_a = open(tree_a_path, O_RDONLY | O_DIRECTORY);
 | 
			
		||||
	if ( tree_a < 0 )
 | 
			
		||||
		error(1, errno, "`%s'", tree_a_path);
 | 
			
		||||
 | 
			
		||||
	const char* tree_b_path = argv[2];
 | 
			
		||||
	int tree_b = open(tree_b_path, O_RDONLY | O_DIRECTORY);
 | 
			
		||||
	if ( tree_b < 0 )
 | 
			
		||||
		error(1, errno, "`%s'", tree_b_path);
 | 
			
		||||
 | 
			
		||||
	execdiff(tree_a, tree_a_path, tree_b, tree_b_path, ".");
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										246
									
								
								tix/tix-execpatch.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								tix/tix-execpatch.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,246 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2013.
 | 
			
		||||
 | 
			
		||||
    This file is part of Tix.
 | 
			
		||||
 | 
			
		||||
    Tix is free software: you can redistribute it and/or modify it under the
 | 
			
		||||
    terms of the GNU General Public License as published by the Free Software
 | 
			
		||||
    Foundation, either version 3 of the License, or (at your option) any later
 | 
			
		||||
    version.
 | 
			
		||||
 | 
			
		||||
    Tix 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 General Public License for more
 | 
			
		||||
    details.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    Tix. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    tix-execpatch.cpp
 | 
			
		||||
    Patches the executable bits of files in the current source directory.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#define __STDC_CONSTANT_MACROS
 | 
			
		||||
#define __STDC_LIMIT_MACROS
 | 
			
		||||
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <error.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <libgen.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
int fgetc_or_die(FILE* input, const char* input_path, size_t* line,
 | 
			
		||||
                 size_t* column)
 | 
			
		||||
{
 | 
			
		||||
	int result = fgetc(input);
 | 
			
		||||
	if ( result == '\n' )
 | 
			
		||||
		(*line)++, *column = 0;
 | 
			
		||||
	else
 | 
			
		||||
		(*column)++;
 | 
			
		||||
	if ( result == EOF && ferror(input) )
 | 
			
		||||
		error(1, errno, "read: `%s'", input_path);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int fgetc_or_die_eof(FILE* input, const char* input_path, size_t* line,
 | 
			
		||||
                     size_t* column)
 | 
			
		||||
{
 | 
			
		||||
	int result = fgetc_or_die(input, input_path, line, column);
 | 
			
		||||
	if ( result == EOF )
 | 
			
		||||
		error(1, errno, "%s:%zu:%zu: unexpected end of file",
 | 
			
		||||
		      input_path, *line, *column);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool check_eof(FILE* input, const char* input_path)
 | 
			
		||||
{
 | 
			
		||||
	size_t line = 0;
 | 
			
		||||
	size_t column = 0;
 | 
			
		||||
	int c = fgetc_or_die(input, input_path, &line, &column);
 | 
			
		||||
	if ( c != EOF )
 | 
			
		||||
	{
 | 
			
		||||
		ungetc(c, input);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void parse_fixed(const char* text, FILE* input, const char* input_path,
 | 
			
		||||
                 size_t* line, size_t* column)
 | 
			
		||||
{
 | 
			
		||||
	for ( size_t i = 0; text[i]; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		int ic = fgetc_or_die(input, input_path, line, column);
 | 
			
		||||
		if ( ic == EOF )
 | 
			
		||||
			error(1, errno, "%s:%zu:%zu: unexpected end of file, expected `%s'",
 | 
			
		||||
			      input_path, *line, *column, text + i);
 | 
			
		||||
		if ( ic != (unsigned char) text[i] )
 | 
			
		||||
			error(1, errno, "%s:%zu:%zu: parse error, expected `%s'", input_path,
 | 
			
		||||
			      *line, *column, text + i);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool execpatch(FILE* input, const char* input_path, bool check)
 | 
			
		||||
{
 | 
			
		||||
	char* buffer = NULL;
 | 
			
		||||
	size_t buffer_used = 0;
 | 
			
		||||
	size_t buffer_length = 0;
 | 
			
		||||
 | 
			
		||||
	bool result = true;
 | 
			
		||||
	size_t line = 1;
 | 
			
		||||
	while ( !check_eof(input, input_path) )
 | 
			
		||||
	{
 | 
			
		||||
		buffer_used = 0;
 | 
			
		||||
		size_t column = 0;
 | 
			
		||||
		parse_fixed("chmod ", input, input_path, &line, &column);
 | 
			
		||||
		bool plus;
 | 
			
		||||
		switch ( fgetc_or_die(input, input_path, &line, &column) )
 | 
			
		||||
		{
 | 
			
		||||
		case '-': plus = false; break;
 | 
			
		||||
		case '+': plus = true; break;
 | 
			
		||||
		default:
 | 
			
		||||
			error(1, errno, "%s:%zu:%zu: parse error, expected '-' or '+'",
 | 
			
		||||
			      input_path, line, column);
 | 
			
		||||
		}
 | 
			
		||||
		parse_fixed("x -- '", input, input_path, &line, &column);
 | 
			
		||||
		while ( true )
 | 
			
		||||
		{
 | 
			
		||||
			int ic = fgetc_or_die_eof(input, input_path, &line, &column);
 | 
			
		||||
			if ( ic == '\'' )
 | 
			
		||||
			{
 | 
			
		||||
				ic = fgetc_or_die(input, input_path, &line, &column);
 | 
			
		||||
				if ( ic == EOF || ic == '\n' )
 | 
			
		||||
					break;
 | 
			
		||||
				ungetc(ic, input);
 | 
			
		||||
				parse_fixed("\\''", input, input_path, &line, &column);
 | 
			
		||||
				ic = '\'';
 | 
			
		||||
			}
 | 
			
		||||
			if ( buffer_used == buffer_length )
 | 
			
		||||
			{
 | 
			
		||||
				size_t new_length = buffer_length ? 2 * buffer_length : 16;
 | 
			
		||||
				buffer = (char*) realloc(buffer, sizeof(char) * (new_length + 1));
 | 
			
		||||
				buffer_length = new_length;
 | 
			
		||||
			}
 | 
			
		||||
			buffer[buffer_used++] = ic;
 | 
			
		||||
		}
 | 
			
		||||
		if ( !buffer_used )
 | 
			
		||||
			error(1, errno, "%s:%zu: unexpected empty path", input_path, line);
 | 
			
		||||
		assert(buffer_length);
 | 
			
		||||
		buffer[buffer_used] = '\0';
 | 
			
		||||
		if ( buffer[0] == '/' )
 | 
			
		||||
			error(1, errno, "%s:%zu: unexpected absolute path", input_path, line);
 | 
			
		||||
		if ( does_path_contain_dotdot(buffer) )
 | 
			
		||||
			error(1, errno, "%s:%zu: unexpected path with ..", input_path, line);
 | 
			
		||||
		if ( check )
 | 
			
		||||
			continue;
 | 
			
		||||
		struct stat st;
 | 
			
		||||
		if ( fstatat(AT_FDCWD, buffer, &st, AT_SYMLINK_NOFOLLOW) != 0 )
 | 
			
		||||
		{
 | 
			
		||||
			error(0, errno, "chmod %cx: `%s'", plus ? '+' : '-', buffer);
 | 
			
		||||
			result = false;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		mode_t new_mode = st.st_mode;
 | 
			
		||||
		if ( plus )
 | 
			
		||||
			new_mode |= 0111 & ~get_umask_value();
 | 
			
		||||
		else
 | 
			
		||||
			new_mode &= ~0111;
 | 
			
		||||
		if ( fchmodat(AT_FDCWD, buffer, new_mode, 0) != 0 )
 | 
			
		||||
		{
 | 
			
		||||
			error(0, errno, "chmod %cx: `%s'", plus ? '+' : '-', buffer);
 | 
			
		||||
			result = false;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void help(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(fp, "Usage: %s [OPTION]... [PATCH]\n", argv0);
 | 
			
		||||
	fprintf(fp, "Patches the executable bits of files in the current source directory.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void version(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	help(fp, argv0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
	bool check = false;
 | 
			
		||||
	char* directory = NULL;
 | 
			
		||||
 | 
			
		||||
	const char* argv0 = argv[0];
 | 
			
		||||
	for ( int i = 0; i < argc; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		const char* arg = argv[i];
 | 
			
		||||
		if ( arg[0] != '-' )
 | 
			
		||||
			continue;
 | 
			
		||||
		argv[i] = NULL;
 | 
			
		||||
		if ( !strcmp(arg, "--") )
 | 
			
		||||
			break;
 | 
			
		||||
		if ( arg[1] != '-' )
 | 
			
		||||
		{
 | 
			
		||||
			while ( char c = *++arg ) switch ( c )
 | 
			
		||||
			{
 | 
			
		||||
			case 'c': check = true; break;
 | 
			
		||||
			default:
 | 
			
		||||
				fprintf(stderr, "%s: unknown option -- `%c'\n", argv0, c);
 | 
			
		||||
				help(stderr, argv0);
 | 
			
		||||
				exit(1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !strcmp(arg, "--help") ) { help(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( !strcmp(arg, "--version") ) { version(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( !strcmp(arg, "--check") ) { check = true; }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--directory", &directory) ) { }
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
 | 
			
		||||
			help(stderr, argv0);
 | 
			
		||||
			exit(1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CompactArguments(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	if ( 2 < argc )
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "%s: unexpected extra operand `%s'\n", argv0, argv[2]);
 | 
			
		||||
		help(stderr, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const char* input_path = "<stdin>";
 | 
			
		||||
	FILE* input = stdin;
 | 
			
		||||
 | 
			
		||||
	if ( argc == 2 )
 | 
			
		||||
	{
 | 
			
		||||
		input_path = argv[1];
 | 
			
		||||
		if ( !(input = fopen(input_path, "r")) )
 | 
			
		||||
			error(1, errno, "`%s'", input_path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( directory && chdir(directory) != 0 )
 | 
			
		||||
		error(1, errno, "chdir: `%s'", directory);
 | 
			
		||||
	free(directory);
 | 
			
		||||
 | 
			
		||||
	return execpatch(input, input_path, check) ? 0 : 1;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										342
									
								
								tix/tix-install.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										342
									
								
								tix/tix-install.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,342 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2013.
 | 
			
		||||
 | 
			
		||||
    This file is part of Tix.
 | 
			
		||||
 | 
			
		||||
    Tix is free software: you can redistribute it and/or modify it under the
 | 
			
		||||
    terms of the GNU General Public License as published by the Free Software
 | 
			
		||||
    Foundation, either version 3 of the License, or (at your option) any later
 | 
			
		||||
    version.
 | 
			
		||||
 | 
			
		||||
    Tix 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 General Public License for more
 | 
			
		||||
    details.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    Tix. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    tix-install.cpp
 | 
			
		||||
    Install a tix into a tix collection.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#define __STDC_CONSTANT_MACROS
 | 
			
		||||
#define __STDC_LIMIT_MACROS
 | 
			
		||||
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <error.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
void TipTixCollection(const char* prefix)
 | 
			
		||||
{
 | 
			
		||||
	error(0, 0, "error: `%s' isn't a tix collection, use tix-collection before "
 | 
			
		||||
                "installing packages.", prefix);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TipTixWrongPlatform(const char* prefix, const char* platform)
 | 
			
		||||
{
 | 
			
		||||
	error(0, 0, "error: `%s' isn't a tix collection for platform `%s', use "
 | 
			
		||||
                "tix-collection to add this platform before installing "
 | 
			
		||||
                "packages.", prefix, platform);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void VerifyTixCollection(const char* prefix)
 | 
			
		||||
{
 | 
			
		||||
	if ( !IsDirectory(prefix) )
 | 
			
		||||
	{
 | 
			
		||||
		if ( errno == ENOENT )
 | 
			
		||||
			TipTixCollection(prefix);
 | 
			
		||||
		error(1, errno, "error: tix collection unavailable: `%s'", prefix);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void VerifyTixDirectory(const char* prefix, const char* tix_dir)
 | 
			
		||||
{
 | 
			
		||||
	if ( !IsDirectory(tix_dir) )
 | 
			
		||||
	{
 | 
			
		||||
		if ( errno == ENOENT )
 | 
			
		||||
			TipTixCollection(prefix);
 | 
			
		||||
		error(1, errno, "error: tix database unavailable: `%s'", tix_dir);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void VerifyTixDatabase(const char* prefix,
 | 
			
		||||
                       const char* tixdb_path,
 | 
			
		||||
                       const char* platform)
 | 
			
		||||
{
 | 
			
		||||
	if ( !IsDirectory(tixdb_path) )
 | 
			
		||||
	{
 | 
			
		||||
		if ( errno == ENOENT )
 | 
			
		||||
			TipTixWrongPlatform(prefix, platform);
 | 
			
		||||
		error(1, errno, "error: tix database for platform `%s' unavailable: "
 | 
			
		||||
		                "`%s'", platform, tixdb_path);
 | 
			
		||||
	}
 | 
			
		||||
	char* info_path = join_paths(tixdb_path, "collection.conf");
 | 
			
		||||
	if ( !IsFile(info_path) )
 | 
			
		||||
	{
 | 
			
		||||
		if ( errno == ENOENT )
 | 
			
		||||
			TipTixCollection(prefix);
 | 
			
		||||
		error(1, errno, "error: tix collection information unavailable: `%s'",
 | 
			
		||||
		                info_path);
 | 
			
		||||
	}
 | 
			
		||||
	char* installed_list_path = join_paths(tixdb_path, "installed.list");
 | 
			
		||||
	FILE* installed_list_fp = fopen(installed_list_path, "a");
 | 
			
		||||
	if ( !installed_list_fp )
 | 
			
		||||
	{
 | 
			
		||||
		error(0, errno, "error: unable to open `%s' for writing",
 | 
			
		||||
		                installed_list_path);
 | 
			
		||||
		error(1, 0, "error: `%s': do you have sufficient permissions to "
 | 
			
		||||
		            "administer this tix collection?", prefix);
 | 
			
		||||
	}
 | 
			
		||||
	fclose(installed_list_fp);
 | 
			
		||||
	free(installed_list_path);
 | 
			
		||||
	free(info_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool IsPackageInstalled(const char* tixdb_path, const char* package)
 | 
			
		||||
{
 | 
			
		||||
	char* installed_list_path = join_paths(tixdb_path, "installed.list");
 | 
			
		||||
	FILE* installed_list_fp = fopen(installed_list_path, "r");
 | 
			
		||||
	if ( !installed_list_fp )
 | 
			
		||||
		error(1, errno, "`%s'", installed_list_path);
 | 
			
		||||
 | 
			
		||||
	bool ret = false;
 | 
			
		||||
	char* line = NULL;
 | 
			
		||||
	size_t line_size;
 | 
			
		||||
	ssize_t line_len;
 | 
			
		||||
	while ( 0 < (line_len = getline(&line, &line_size, installed_list_fp)) )
 | 
			
		||||
	{
 | 
			
		||||
		if ( line_len && line[line_len-1] == '\n' )
 | 
			
		||||
			line[--line_len] = '\0';
 | 
			
		||||
		if ( !strcmp(line, package) )
 | 
			
		||||
		{
 | 
			
		||||
			ret = true;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	free(line);
 | 
			
		||||
 | 
			
		||||
	fclose(installed_list_fp);
 | 
			
		||||
	free(installed_list_path);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MarkPackageAsInstalled(const char* tixdb_path, const char* package)
 | 
			
		||||
{
 | 
			
		||||
	char* installed_list_path = join_paths(tixdb_path, "installed.list");
 | 
			
		||||
	FILE* installed_list_fp = fopen(installed_list_path, "a");
 | 
			
		||||
	if ( !installed_list_fp )
 | 
			
		||||
		error(1, errno, "`%s'", installed_list_path);
 | 
			
		||||
 | 
			
		||||
	fprintf(installed_list_fp, "%s\n", package);
 | 
			
		||||
	fflush(installed_list_fp);
 | 
			
		||||
 | 
			
		||||
	if ( ferror(installed_list_fp) || fclose(installed_list_fp) == EOF )
 | 
			
		||||
		error(1, errno, "`%s'", installed_list_path);
 | 
			
		||||
	free(installed_list_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Usage(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(fp, "Usage: %s [OPTION]... --collection=PREFIX PACKAGE\n", argv0);
 | 
			
		||||
	fprintf(fp, "Install a tix into a tix collection.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Help(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	Usage(fp, argv0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Version(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	Usage(fp, argv0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
	char* collection = strdup_null(getenv_def("TIX_COLLECTION", NULL));
 | 
			
		||||
	char* prefix = strdup_null(getenv_def("TIX_COLLECTION_PREFIX", NULL));
 | 
			
		||||
	char* tar = strdup(getenv_def("TAR", "tar"));
 | 
			
		||||
	bool reinstall = false;
 | 
			
		||||
 | 
			
		||||
	const char* argv0 = argv[0];
 | 
			
		||||
	for ( int i = 0; i < argc; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		const char* arg = argv[i];
 | 
			
		||||
		if ( arg[0] != '-' )
 | 
			
		||||
			continue;
 | 
			
		||||
		argv[i] = NULL;
 | 
			
		||||
		if ( !strcmp(arg, "--") )
 | 
			
		||||
			break;
 | 
			
		||||
		if ( arg[1] != '-' )
 | 
			
		||||
		{
 | 
			
		||||
			while ( char c = *++arg ) switch ( c )
 | 
			
		||||
			{
 | 
			
		||||
			default:
 | 
			
		||||
				fprintf(stderr, "%s: unknown option -- `%c'\n", argv0, c);
 | 
			
		||||
				Usage(stderr, argv0);
 | 
			
		||||
				exit(1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !strcmp(arg, "--help") ) { Help(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( !strcmp(arg, "--usage") ) { Usage(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( !strcmp(arg, "--version") ) { Version(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--collection", &collection) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--prefix", &prefix) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--tar", &tar) ) { }
 | 
			
		||||
		else if ( !strcmp(arg, "--reinstall") ) { reinstall = true; }
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
 | 
			
		||||
			Usage(stderr, argv0);
 | 
			
		||||
			exit(1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( argc == 1 )
 | 
			
		||||
	{
 | 
			
		||||
		Usage(stdout, argv0);
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CompactArguments(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	if ( argc <= 1 )
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "%s: no package specified\n", argv0);
 | 
			
		||||
		Usage(stderr, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( !collection && prefix )
 | 
			
		||||
		collection = strdup(prefix);
 | 
			
		||||
 | 
			
		||||
	if ( !collection )
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "%s: no collection prefix specified, use --collection "
 | 
			
		||||
		                "or TIX_COLLECTION to specify where the package will "
 | 
			
		||||
		                "installed.\n", argv0);
 | 
			
		||||
		Usage(stderr, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( !prefix )
 | 
			
		||||
		prefix = strdup(collection);
 | 
			
		||||
 | 
			
		||||
	if ( strcmp(collection, prefix) != 0 )
 | 
			
		||||
		error(1, 0, "error: desired collection `%s' isn't equal to desired "
 | 
			
		||||
		            "prefix `%s', which isn't supported (and dangerous).\n",
 | 
			
		||||
		             collection, prefix);
 | 
			
		||||
 | 
			
		||||
	if ( !*collection )
 | 
			
		||||
		collection = strdup("/");
 | 
			
		||||
 | 
			
		||||
	VerifyTixCollection(collection);
 | 
			
		||||
 | 
			
		||||
	char* tix_directory_path = join_paths(collection, "tix");
 | 
			
		||||
 | 
			
		||||
	VerifyTixDirectory(collection, tix_directory_path);
 | 
			
		||||
 | 
			
		||||
	char* tix_path = strdup(argv[1]);
 | 
			
		||||
	if ( !IsFile(tix_path) )
 | 
			
		||||
		error(1, errno, "`%s'", tix_path);
 | 
			
		||||
 | 
			
		||||
	const char* tixinfo_path = "tix/tixinfo";
 | 
			
		||||
	if ( !TarContainsFile(tar, tix_path, tixinfo_path) )
 | 
			
		||||
		error(1, 0, "`%s' doesn't contain a `%s' file", tix_path, tixinfo_path);
 | 
			
		||||
 | 
			
		||||
	string_array_t tixinfo = string_array_make();
 | 
			
		||||
	FILE* tixinfo_fp = TarOpenFile(tar, tix_path, tixinfo_path);
 | 
			
		||||
	dictionary_append_file(&tixinfo, tixinfo_fp);
 | 
			
		||||
	fclose(tixinfo_fp);
 | 
			
		||||
 | 
			
		||||
	const char* package_name = dictionary_get(&tixinfo, "pkg.name");
 | 
			
		||||
	assert(package_name);
 | 
			
		||||
 | 
			
		||||
	const char* package_prefix = dictionary_get(&tixinfo, "pkg.prefix");
 | 
			
		||||
 | 
			
		||||
	const char* package_platform = dictionary_get(&tixinfo, "tix.platform");
 | 
			
		||||
	assert(package_platform);
 | 
			
		||||
 | 
			
		||||
	char* tixdb_path = join_paths(tix_directory_path, package_platform);
 | 
			
		||||
	free(tix_directory_path);
 | 
			
		||||
 | 
			
		||||
	VerifyTixDatabase(collection, tixdb_path, package_platform);
 | 
			
		||||
 | 
			
		||||
	char* coll_conf_path = join_paths(tixdb_path, "collection.conf");
 | 
			
		||||
	string_array_t coll_conf = string_array_make();
 | 
			
		||||
	if ( !dictionary_append_file_path(&coll_conf, coll_conf_path) )
 | 
			
		||||
		error(1, errno, "`%s'", coll_conf_path);
 | 
			
		||||
	VerifyTixCollectionConfiguration(&coll_conf, coll_conf_path);
 | 
			
		||||
	free(coll_conf_path);
 | 
			
		||||
 | 
			
		||||
	const char* coll_prefix = dictionary_get(&coll_conf, "collection.prefix");
 | 
			
		||||
	assert(coll_prefix);
 | 
			
		||||
	const char* coll_platform = dictionary_get(&coll_conf, "collection.platform");
 | 
			
		||||
	assert(coll_platform);
 | 
			
		||||
 | 
			
		||||
	bool already_installed = IsPackageInstalled(tixdb_path, package_name);
 | 
			
		||||
	if ( already_installed && !reinstall )
 | 
			
		||||
		error(1, 0, "error: package `%s' is already installed.", package_name);
 | 
			
		||||
 | 
			
		||||
	if ( package_prefix && strcmp(coll_prefix, package_prefix) != 0 )
 | 
			
		||||
	{
 | 
			
		||||
		error(0, errno, "error: `%s' is compiled with the prefix `%s', but the "
 | 
			
		||||
		                "destination collection has the prefix `%s'.", tix_path,
 | 
			
		||||
		                package_prefix, coll_prefix);
 | 
			
		||||
		error(1, errno, "you need to recompile the package with "
 | 
			
		||||
		                "--prefix=\"%s\".", coll_prefix);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( strcmp(coll_platform, package_platform) != 0 )
 | 
			
		||||
	{
 | 
			
		||||
		error(0, errno, "error: `%s' is compiled with the platform `%s', but "
 | 
			
		||||
		                "the destination collection has the platform `%s'.",
 | 
			
		||||
		                tix_path, package_platform, coll_platform);
 | 
			
		||||
		error(1, errno, "you need to recompile the package with "
 | 
			
		||||
		                "--host=%s\".", coll_platform);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	printf("Installing `%s' into `%s'...\n", package_name, prefix);
 | 
			
		||||
	char* data_and_prefix = package_prefix && prefix[0] ?
 | 
			
		||||
	                        print_string("data%s", package_prefix) :
 | 
			
		||||
	                        strdup("data");
 | 
			
		||||
	if ( fork_and_wait_or_death() )
 | 
			
		||||
	{
 | 
			
		||||
		size_t num_strips = count_tar_components(data_and_prefix);
 | 
			
		||||
		const char* cmd_argv[] =
 | 
			
		||||
		{
 | 
			
		||||
			tar,
 | 
			
		||||
			print_string("--strip-components=%zu", num_strips),
 | 
			
		||||
			"-C", prefix,
 | 
			
		||||
			"--extract",
 | 
			
		||||
			"--file", tix_path,
 | 
			
		||||
			data_and_prefix,
 | 
			
		||||
			NULL
 | 
			
		||||
		};
 | 
			
		||||
		execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
		error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
	}
 | 
			
		||||
	free(data_and_prefix);
 | 
			
		||||
 | 
			
		||||
	if ( !already_installed )
 | 
			
		||||
		MarkPackageAsInstalled(tixdb_path, package_name);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										427
									
								
								tix/tix-object-insert.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										427
									
								
								tix/tix-object-insert.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,427 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2013.
 | 
			
		||||
 | 
			
		||||
    This file is part of Tix.
 | 
			
		||||
 | 
			
		||||
    Tix is free software: you can redistribute it and/or modify it under the
 | 
			
		||||
    terms of the GNU General Public License as published by the Free Software
 | 
			
		||||
    Foundation, either version 3 of the License, or (at your option) any later
 | 
			
		||||
    version.
 | 
			
		||||
 | 
			
		||||
    Tix 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 General Public License for more
 | 
			
		||||
    details.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    Tix. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    tix-object-insert.cpp
 | 
			
		||||
    Inserts files into a tix object database.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <error.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include <nettle/sha.h>
 | 
			
		||||
 | 
			
		||||
int create_and_open_directory_at(int dirfd, const char* path, mode_t mode)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	int ret = openat(dirfd, path, O_RDONLY | O_DIRECTORY);
 | 
			
		||||
	if ( ret < 0 && errno == EEXIST )
 | 
			
		||||
	{
 | 
			
		||||
		if ( mkdirat(dirfd, path, mode) != 0 )
 | 
			
		||||
			return -1;
 | 
			
		||||
		return openat(dirfd, path, O_RDONLY | O_DIRECTORY);
 | 
			
		||||
	}
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct options
 | 
			
		||||
{
 | 
			
		||||
	bool hash;
 | 
			
		||||
	bool quiet;
 | 
			
		||||
	bool link;
 | 
			
		||||
	bool symlink;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
bool insert_object(struct options* opts,
 | 
			
		||||
                   const char* file_path,
 | 
			
		||||
                   const char* tmp,
 | 
			
		||||
                   int database_fd, const char* database_path,
 | 
			
		||||
                   const char* digest,
 | 
			
		||||
                   int dir_fd, const char* dir_name)
 | 
			
		||||
{
 | 
			
		||||
	const char* entry_name = digest + 2;
 | 
			
		||||
	if ( linkat(database_fd, tmp, dir_fd, entry_name, 0) &&
 | 
			
		||||
	     errno != EEXIST )
 | 
			
		||||
	{
 | 
			
		||||
		error(0, errno, "`%s/%s' -> `%s/%s/%s'", database_path, dir_name,
 | 
			
		||||
		      database_path, dir_name, entry_name);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( opts->hash )
 | 
			
		||||
		printf("%s\n", digest);
 | 
			
		||||
	else if ( !opts->quiet )
 | 
			
		||||
		printf("`%s' -> `%s/%s/%s'\n", file_path,
 | 
			
		||||
		                               database_path, dir_name, entry_name);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool insert_object(struct options* opts,
 | 
			
		||||
                   const char* file_path,
 | 
			
		||||
                   const char* tmp,
 | 
			
		||||
                   int database_fd, const char* database_path,
 | 
			
		||||
                   const char* digest)
 | 
			
		||||
{
 | 
			
		||||
	char dir_name[3] = { digest[0], digest[1], '\0' };
 | 
			
		||||
	int dir_fd = create_and_open_directory_at(database_fd, dir_name, 0777);
 | 
			
		||||
	if ( dir_fd < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		error(0, errno, "`%s/%s'", database_path, dir_name);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool success = insert_object(opts, file_path, tmp, database_fd, database_path, digest, dir_fd, dir_name);
 | 
			
		||||
 | 
			
		||||
	close(dir_fd);
 | 
			
		||||
 | 
			
		||||
	return success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool copy_and_hash(FILE* fpin, const char* file_path,
 | 
			
		||||
                   FILE* fpout, const char* tmp,
 | 
			
		||||
                   int /*database_fd*/, const char* database_path,
 | 
			
		||||
                   char* digest)
 | 
			
		||||
{
 | 
			
		||||
	struct sha256_ctx shactx;
 | 
			
		||||
	sha256_init(&shactx);
 | 
			
		||||
 | 
			
		||||
	uint8_t buffer[SHA256_DATA_SIZE];
 | 
			
		||||
	while ( size_t num_bytes = fread(buffer, 1, sizeof(buffer), fpin) )
 | 
			
		||||
	{
 | 
			
		||||
		assert(num_bytes <= UINT_MAX);
 | 
			
		||||
		sha256_update(&shactx, num_bytes, buffer);
 | 
			
		||||
		if ( fwrite(buffer, 1, num_bytes, fpout) != num_bytes )
 | 
			
		||||
		{
 | 
			
		||||
			error(0, errno, "`%s/%s'", database_path, tmp);
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( ferror(fpin) )
 | 
			
		||||
	{
 | 
			
		||||
		error(0, errno, "`%s'", file_path);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( fflush(fpout) != 0 )
 | 
			
		||||
	{
 | 
			
		||||
		error(0, errno, "`%s/%s'", database_path, tmp);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uint8_t binary_digest[SHA256_DIGEST_SIZE];
 | 
			
		||||
	sha256_digest(&shactx, sizeof(binary_digest), binary_digest);
 | 
			
		||||
 | 
			
		||||
	for ( size_t n = 0; n < SHA256_DIGEST_SIZE; n++ )
 | 
			
		||||
		snprintf(digest + 2 * n, 3, "%02x", binary_digest[n]);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool insert_object(struct options* opts,
 | 
			
		||||
                   FILE* fpin, const char* file_path,
 | 
			
		||||
                   FILE* fpout, const char* tmp,
 | 
			
		||||
                   int database_fd, const char* database_path)
 | 
			
		||||
{
 | 
			
		||||
	char digest[2 * SHA256_DIGEST_SIZE + 1];
 | 
			
		||||
 | 
			
		||||
	if ( !copy_and_hash(fpin, file_path, fpout, tmp, database_fd,
 | 
			
		||||
	                    database_path, digest) )
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return insert_object(opts, file_path, tmp, database_fd, database_path, digest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: Preferably use O_TMPFILE to avoid naming the file until we have made a
 | 
			
		||||
//       copy whose hash we know.
 | 
			
		||||
bool insert_object(struct options* opts,
 | 
			
		||||
                   FILE* fpin, const char* file_path,
 | 
			
		||||
                   int database_fd, const char* database_path)
 | 
			
		||||
{
 | 
			
		||||
	char tmp[8 + 1 + sizeof(pid_t) * 3 + 1];
 | 
			
		||||
	snprintf(tmp, sizeof(tmp), "incoming.%ji", (intmax_t) getpid());
 | 
			
		||||
	int tmp_fd = openat(database_fd, tmp, O_CREAT | O_WRONLY | O_EXCL, 0444);
 | 
			
		||||
	if ( tmp_fd < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		error(0, errno, "`%s/%s'", database_path, tmp);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	FILE* fpout = fdopen(tmp_fd, "w");
 | 
			
		||||
	if ( !fpout )
 | 
			
		||||
	{
 | 
			
		||||
		error(0, errno, "fdopen(%i)", tmp_fd);
 | 
			
		||||
		close(tmp_fd);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool success = insert_object(opts, fpin, file_path, fpout, tmp, database_fd, database_path);
 | 
			
		||||
 | 
			
		||||
	fclose(fpout);
 | 
			
		||||
 | 
			
		||||
	unlinkat(database_fd, tmp, 0);
 | 
			
		||||
 | 
			
		||||
	return success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool insert_object(struct options* opts,
 | 
			
		||||
                   const char* file_path,
 | 
			
		||||
                   int database_fd, const char* database_path)
 | 
			
		||||
{
 | 
			
		||||
	if ( !strcmp(file_path, "-") )
 | 
			
		||||
		return insert_object(opts, stdin, file_path, database_fd, database_path);
 | 
			
		||||
 | 
			
		||||
	FILE* fpin = fopen(file_path, "r");
 | 
			
		||||
	if ( !fpin )
 | 
			
		||||
		error(1, errno, "`%s'", file_path);
 | 
			
		||||
 | 
			
		||||
	bool success = insert_object(opts, fpin, file_path, database_fd, database_path);
 | 
			
		||||
 | 
			
		||||
	fclose(fpin);
 | 
			
		||||
 | 
			
		||||
	return success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void help(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(fp, "Usage: %s [OPTION]... [-l | -s] --database DATABASE FILE...\n", argv0);
 | 
			
		||||
	fprintf(fp, "Inserts files into a tix object database.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void usage(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	help(fp, argv0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void version(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	help(fp, argv0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
	const char* argv0 = argv[0];
 | 
			
		||||
	const char* database_path = NULL;
 | 
			
		||||
	bool hash = false;
 | 
			
		||||
	bool quiet = false;
 | 
			
		||||
	bool do_link = false;
 | 
			
		||||
	bool do_symlink = false;
 | 
			
		||||
 | 
			
		||||
	for ( int i = 0; i < argc; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		const char* arg = argv[i];
 | 
			
		||||
		if ( arg[0] != '-' )
 | 
			
		||||
			continue;
 | 
			
		||||
		argv[i] = NULL;
 | 
			
		||||
		if ( !strcmp(arg, "--") )
 | 
			
		||||
			break;
 | 
			
		||||
		if ( arg[1] != '-' )
 | 
			
		||||
		{
 | 
			
		||||
			while ( char c = *++arg ) switch ( c )
 | 
			
		||||
			{
 | 
			
		||||
			case 'h': hash = true;
 | 
			
		||||
			case 'l': do_link = true;
 | 
			
		||||
			case 'q': quiet = true;
 | 
			
		||||
			case 's': do_link = true;
 | 
			
		||||
			default:
 | 
			
		||||
				fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
 | 
			
		||||
				usage(stderr, argv0);
 | 
			
		||||
				exit(1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !strcmp(arg, "--help") )
 | 
			
		||||
			help(stdout, argv0), exit(0);
 | 
			
		||||
		else if ( !strcmp(arg, "--usage") )
 | 
			
		||||
			usage(stdout, argv0), exit(0);
 | 
			
		||||
		else if ( !strcmp(arg, "--version") )
 | 
			
		||||
			version(stdout, argv0), exit(0);
 | 
			
		||||
		else if ( !strcmp(arg, "--database") )
 | 
			
		||||
		{
 | 
			
		||||
			if ( i + 1 == argc )
 | 
			
		||||
				error(1, 0, "`--database' expected argument");
 | 
			
		||||
			database_path = argv[++i], argv[i] = NULL;
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !strcmp(arg, "--hash") )
 | 
			
		||||
			hash = true;
 | 
			
		||||
		else if ( !strcmp(arg, "--link") )
 | 
			
		||||
			do_link = true;
 | 
			
		||||
		else if ( !strcmp(arg, "--quiet") )
 | 
			
		||||
			quiet = true;
 | 
			
		||||
		else if ( !strcmp(arg, "--symlink") )
 | 
			
		||||
			do_symlink = true;
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
 | 
			
		||||
			usage(stderr, argv0);
 | 
			
		||||
			exit(1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( argc == 1 )
 | 
			
		||||
		usage(stdout, argv0), exit(0);
 | 
			
		||||
 | 
			
		||||
	if ( do_link && do_symlink )
 | 
			
		||||
	{
 | 
			
		||||
		error(0, 0, "error: the -l and -s options are mutually exclusive");
 | 
			
		||||
		usage(stdout, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( !database_path )
 | 
			
		||||
	{
 | 
			
		||||
		error(1, 0, "no `--database' option given, don't know what database");
 | 
			
		||||
		usage(stdout, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int database_fd = open(database_path, O_RDONLY | O_DIRECTORY);
 | 
			
		||||
	if ( database_fd < 0 )
 | 
			
		||||
		error(1, errno, "`%s'", database_path);
 | 
			
		||||
 | 
			
		||||
	for ( int i = 1; i < argc; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		const char* file_path = argv[i];
 | 
			
		||||
		if ( !file_path )
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		FILE* fpin = fopen(file_path, "r");
 | 
			
		||||
		if ( !fpin )
 | 
			
		||||
			error(1, errno, "`%s'", file_path);
 | 
			
		||||
 | 
			
		||||
		char tmp[8 + 1 + sizeof(pid_t) * 3 + 1];
 | 
			
		||||
		snprintf(tmp, sizeof(tmp), "incoming.%ju", (uintmax_t) getpid());
 | 
			
		||||
		int tmp_fd = openat(database_fd, tmp, O_CREAT | O_WRONLY | O_EXCL, 0444);
 | 
			
		||||
		if ( tmp_fd < 0 )
 | 
			
		||||
			error(1, errno, "`%s/%s'", database_path, tmp);
 | 
			
		||||
		FILE* fpout = fdopen(tmp_fd, "w");
 | 
			
		||||
 | 
			
		||||
		struct sha256_ctx shactx;
 | 
			
		||||
		sha256_init(&shactx);
 | 
			
		||||
 | 
			
		||||
		uint8_t buffer[SHA256_DATA_SIZE];
 | 
			
		||||
		while ( size_t num_bytes = fread(buffer, 1, sizeof(buffer), fpin) )
 | 
			
		||||
		{
 | 
			
		||||
			assert(num_bytes <= UINT_MAX);
 | 
			
		||||
			sha256_update(&shactx, num_bytes, buffer);
 | 
			
		||||
			if ( fwrite(buffer, 1, num_bytes, fpout) != num_bytes )
 | 
			
		||||
			{
 | 
			
		||||
				unlinkat(database_fd, tmp, 0);
 | 
			
		||||
				error(1, errno, "`%s/%s'", database_path, tmp);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if ( ferror(fpin) )
 | 
			
		||||
		{
 | 
			
		||||
			unlinkat(database_fd, tmp, 0);
 | 
			
		||||
			error(1, errno, "`%s'", file_path);
 | 
			
		||||
		}
 | 
			
		||||
		fclose(fpin);
 | 
			
		||||
 | 
			
		||||
		if ( fflush(fpout) != 0 )
 | 
			
		||||
		{
 | 
			
		||||
			unlinkat(database_fd, tmp, 0);
 | 
			
		||||
			error(1, errno, "`%s/%s'", database_path, tmp);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fclose(fpout);
 | 
			
		||||
 | 
			
		||||
		uint8_t binary_digest[SHA256_DIGEST_SIZE];
 | 
			
		||||
		sha256_digest(&shactx, sizeof(binary_digest), binary_digest);
 | 
			
		||||
		char digest[2 * SHA256_DIGEST_SIZE + 1];
 | 
			
		||||
		for ( size_t n = 0; n < SHA256_DIGEST_SIZE; n++ )
 | 
			
		||||
			snprintf(digest + 2 * n, 3, "%02x", binary_digest[n]);
 | 
			
		||||
 | 
			
		||||
		char dir_name[3] = { digest[0], digest[1], '\0' };
 | 
			
		||||
		if ( mkdirat(database_fd, dir_name, 0777) != 0 &&
 | 
			
		||||
		     errno != EEXIST )
 | 
			
		||||
		{
 | 
			
		||||
			unlinkat(database_fd, tmp, 0);
 | 
			
		||||
			error(1, errno, "`%s/%s'", database_path, dir_name);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		int dir_fd = openat(database_fd, dir_name, O_RDONLY | O_DIRECTORY);
 | 
			
		||||
		if ( dir_fd < 0 )
 | 
			
		||||
		{
 | 
			
		||||
			unlinkat(database_fd, tmp, 0);
 | 
			
		||||
			error(1, errno, "`%s/%s'", database_path, dir_name);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const char* entry_name = digest + 2;
 | 
			
		||||
		if ( linkat(database_fd, tmp, dir_fd, entry_name, 0) &&
 | 
			
		||||
		     errno != EEXIST )
 | 
			
		||||
		{
 | 
			
		||||
			unlinkat(database_fd, tmp, 0);
 | 
			
		||||
			error(1, errno, "`%s/%s' -> `%s/%s/%s'", database_path, dir_name,
 | 
			
		||||
			      database_path, dir_name, entry_name);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		close(dir_fd);
 | 
			
		||||
 | 
			
		||||
		unlinkat(database_fd, tmp, 0);
 | 
			
		||||
 | 
			
		||||
		if ( hash )
 | 
			
		||||
			printf("%s\n", digest);
 | 
			
		||||
		else if ( !quiet )
 | 
			
		||||
			printf("`%s' -> `%s/%s/%s'\n", file_path,
 | 
			
		||||
			                               database_path, dir_name, entry_name);
 | 
			
		||||
 | 
			
		||||
		if ( do_link || do_symlink )
 | 
			
		||||
		{
 | 
			
		||||
			if ( unlink(file_path) < 0 )
 | 
			
		||||
				error(1, errno, "cannot unlink: `%s'", file_path);
 | 
			
		||||
 | 
			
		||||
			size_t link_dest_length = strlen(database_path) + 1 + strlen(dir_name) + strlen(entry_name);
 | 
			
		||||
			char* link_dest = (char*) malloc(sizeof(char) * (link_dest_length + 1));
 | 
			
		||||
 | 
			
		||||
			stpcpy(stpcpy(stpcpy(stpcpy(stpcpy(link_dest, database_path), "/"), dir_name), "/"), entry_name);
 | 
			
		||||
 | 
			
		||||
			if ( do_symlink )
 | 
			
		||||
			{
 | 
			
		||||
				if ( symlink(link_dest, file_path) < 0 )
 | 
			
		||||
					error(1, errno, "cannot symlink `%s' to `%s'", file_path,
 | 
			
		||||
					                link_dest);
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				if ( link(link_dest, file_path) < 0 )
 | 
			
		||||
					error(1, errno, "cannot link `%s' to `%s'", file_path,
 | 
			
		||||
					                link_dest);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			free(link_dest);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	close(database_fd);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										240
									
								
								tix/tix-rmpatch.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								tix/tix-rmpatch.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,240 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2013.
 | 
			
		||||
 | 
			
		||||
    This file is part of Tix.
 | 
			
		||||
 | 
			
		||||
    Tix is free software: you can redistribute it and/or modify it under the
 | 
			
		||||
    terms of the GNU General Public License as published by the Free Software
 | 
			
		||||
    Foundation, either version 3 of the License, or (at your option) any later
 | 
			
		||||
    version.
 | 
			
		||||
 | 
			
		||||
    Tix 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 General Public License for more
 | 
			
		||||
    details.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    Tix. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    tix-rmpatch.cpp
 | 
			
		||||
    Removes files from the current source directory.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#define __STDC_CONSTANT_MACROS
 | 
			
		||||
#define __STDC_LIMIT_MACROS
 | 
			
		||||
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <error.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <libgen.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
int fgetc_or_die(FILE* input, const char* input_path, size_t* line,
 | 
			
		||||
                 size_t* column)
 | 
			
		||||
{
 | 
			
		||||
	int result = fgetc(input);
 | 
			
		||||
	if ( result == '\n' )
 | 
			
		||||
		(*line)++, *column = 0;
 | 
			
		||||
	else
 | 
			
		||||
		(*column)++;
 | 
			
		||||
	if ( result == EOF && ferror(input) )
 | 
			
		||||
		error(1, errno, "read: `%s'", input_path);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int fgetc_or_die_eof(FILE* input, const char* input_path, size_t* line,
 | 
			
		||||
                     size_t* column)
 | 
			
		||||
{
 | 
			
		||||
	int result = fgetc_or_die(input, input_path, line, column);
 | 
			
		||||
	if ( result == EOF )
 | 
			
		||||
		error(1, errno, "%s:%zu:%zu: unexpected end of file",
 | 
			
		||||
		      input_path, *line, *column);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool check_eof(FILE* input, const char* input_path)
 | 
			
		||||
{
 | 
			
		||||
	size_t line = 0;
 | 
			
		||||
	size_t column = 0;
 | 
			
		||||
	int c = fgetc_or_die(input, input_path, &line, &column);
 | 
			
		||||
	if ( c != EOF )
 | 
			
		||||
	{
 | 
			
		||||
		ungetc(c, input);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void parse_fixed(const char* text, FILE* input, const char* input_path,
 | 
			
		||||
                 size_t* line, size_t* column)
 | 
			
		||||
{
 | 
			
		||||
	for ( size_t i = 0; text[i]; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		int ic = fgetc_or_die(input, input_path, line, column);
 | 
			
		||||
		if ( ic == EOF )
 | 
			
		||||
			error(1, errno, "%s:%zu:%zu: unexpected end of file, expected `%s'",
 | 
			
		||||
			      input_path, *line, *column, text + i);
 | 
			
		||||
		if ( ic != (unsigned char) text[i] )
 | 
			
		||||
			error(1, errno, "%s:%zu:%zu: parse error, expected `%s'", input_path,
 | 
			
		||||
			      *line, *column, text + i);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool rmpatch(FILE* input, const char* input_path, bool check)
 | 
			
		||||
{
 | 
			
		||||
	char* buffer = NULL;
 | 
			
		||||
	size_t buffer_used = 0;
 | 
			
		||||
	size_t buffer_length = 0;
 | 
			
		||||
 | 
			
		||||
	bool result = true;
 | 
			
		||||
	size_t line = 1;
 | 
			
		||||
	while ( !check_eof(input, input_path) )
 | 
			
		||||
	{
 | 
			
		||||
		buffer_used = 0;
 | 
			
		||||
		size_t column = 0;
 | 
			
		||||
		parse_fixed("rm -rf -- '", input, input_path, &line, &column);
 | 
			
		||||
		while ( true )
 | 
			
		||||
		{
 | 
			
		||||
			int ic = fgetc_or_die_eof(input, input_path, &line, &column);
 | 
			
		||||
			if ( ic == '\'' )
 | 
			
		||||
			{
 | 
			
		||||
				ic = fgetc_or_die(input, input_path, &line, &column);
 | 
			
		||||
				if ( ic == EOF || ic == '\n' )
 | 
			
		||||
					break;
 | 
			
		||||
				ungetc(ic, input);
 | 
			
		||||
				parse_fixed("\\''", input, input_path, &line, &column);
 | 
			
		||||
				ic = '\'';
 | 
			
		||||
			}
 | 
			
		||||
			if ( buffer_used == buffer_length )
 | 
			
		||||
			{
 | 
			
		||||
				size_t new_length = buffer_length ? 2 * buffer_length : 16;
 | 
			
		||||
				buffer = (char*) realloc(buffer, sizeof(char) * (new_length + 1));
 | 
			
		||||
				buffer_length = new_length;
 | 
			
		||||
			}
 | 
			
		||||
			buffer[buffer_used++] = ic;
 | 
			
		||||
		}
 | 
			
		||||
		if ( !buffer_used )
 | 
			
		||||
			error(1, errno, "%s:%zu: unexpected empty path", input_path, line);
 | 
			
		||||
		assert(buffer_length);
 | 
			
		||||
		buffer[buffer_used] = '\0';
 | 
			
		||||
		if ( buffer[0] == '/' )
 | 
			
		||||
			error(1, errno, "%s:%zu: unexpected absolute path", input_path, line);
 | 
			
		||||
		if ( does_path_contain_dotdot(buffer) )
 | 
			
		||||
			error(1, errno, "%s:%zu: unexpected path with ..", input_path, line);
 | 
			
		||||
		if ( check )
 | 
			
		||||
			continue;
 | 
			
		||||
		if ( pid_t child_pid = fork_or_death() )
 | 
			
		||||
		{
 | 
			
		||||
			int status;
 | 
			
		||||
			waitpid(child_pid, &status, 0);
 | 
			
		||||
			if ( WIFSIGNALED(status) )
 | 
			
		||||
				error(128 + WTERMSIG(status), 0, "child with pid %ju was killed by "
 | 
			
		||||
					  "signal %i (%s).", (uintmax_t) child_pid, WTERMSIG(status),
 | 
			
		||||
				      strsignal(WTERMSIG(status)));
 | 
			
		||||
			if ( WIFEXITED(status) && WEXITSTATUS(status) != 0 )
 | 
			
		||||
				result = false;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		const char* cmd_argv[] =
 | 
			
		||||
		{
 | 
			
		||||
			"rm",
 | 
			
		||||
			"-rf",
 | 
			
		||||
			"--",
 | 
			
		||||
			buffer,
 | 
			
		||||
			NULL,
 | 
			
		||||
		};
 | 
			
		||||
		execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
		error(127, errno, "%s", cmd_argv[0]);
 | 
			
		||||
	}
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void help(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(fp, "Usage: %s [OPTION]... [PATCH]\n", argv0);
 | 
			
		||||
	fprintf(fp, "Removes files from the current source directory.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void version(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	help(fp, argv0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
	bool check = false;
 | 
			
		||||
	char* directory = NULL;
 | 
			
		||||
 | 
			
		||||
	const char* argv0 = argv[0];
 | 
			
		||||
	for ( int i = 0; i < argc; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		const char* arg = argv[i];
 | 
			
		||||
		if ( arg[0] != '-' )
 | 
			
		||||
			continue;
 | 
			
		||||
		argv[i] = NULL;
 | 
			
		||||
		if ( !strcmp(arg, "--") )
 | 
			
		||||
			break;
 | 
			
		||||
		if ( arg[1] != '-' )
 | 
			
		||||
		{
 | 
			
		||||
			while ( char c = *++arg ) switch ( c )
 | 
			
		||||
			{
 | 
			
		||||
			case 'c': check = true; break;
 | 
			
		||||
			default:
 | 
			
		||||
				fprintf(stderr, "%s: unknown option -- `%c'\n", argv0, c);
 | 
			
		||||
				help(stderr, argv0);
 | 
			
		||||
				exit(1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !strcmp(arg, "--help") ) { help(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( !strcmp(arg, "--version") ) { version(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( !strcmp(arg, "--check") ) { check = true; }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--directory", &directory) ) { }
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
 | 
			
		||||
			help(stderr, argv0);
 | 
			
		||||
			exit(1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CompactArguments(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	if ( 2 < argc )
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "%s: unexpected extra operand `%s'\n", argv0, argv[2]);
 | 
			
		||||
		help(stderr, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const char* input_path = "<stdin>";
 | 
			
		||||
	FILE* input = stdin;
 | 
			
		||||
 | 
			
		||||
	if ( argc == 2 )
 | 
			
		||||
	{
 | 
			
		||||
		input_path = argv[1];
 | 
			
		||||
		if ( !(input = fopen(input_path, "r")) )
 | 
			
		||||
			error(1, errno, "`%s'", input_path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( directory && chdir(directory) != 0 )
 | 
			
		||||
		error(1, errno, "chdir: `%s'", directory);
 | 
			
		||||
	free(directory);
 | 
			
		||||
 | 
			
		||||
	return rmpatch(input, input_path, check) ? 0 : 1;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										285
									
								
								tix/tix.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										285
									
								
								tix/tix.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,285 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2013.
 | 
			
		||||
 | 
			
		||||
    This file is part of Tix.
 | 
			
		||||
 | 
			
		||||
    Tix is free software: you can redistribute it and/or modify it under the
 | 
			
		||||
    terms of the GNU General Public License as published by the Free Software
 | 
			
		||||
    Foundation, either version 3 of the License, or (at your option) any later
 | 
			
		||||
    version.
 | 
			
		||||
 | 
			
		||||
    Tix 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 General Public License for more
 | 
			
		||||
    details.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    Tix. If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    tix.cpp
 | 
			
		||||
    Front end to the Tix package management system.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#define __STDC_CONSTANT_MACROS
 | 
			
		||||
#define __STDC_LIMIT_MACROS
 | 
			
		||||
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <error.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
	char* collection;
 | 
			
		||||
	char* tar;
 | 
			
		||||
	char* tixdb_path;
 | 
			
		||||
	char* tix_install;
 | 
			
		||||
	string_array_t coll_conf;
 | 
			
		||||
	string_array_t repo_list;
 | 
			
		||||
	string_array_t inst_list;
 | 
			
		||||
} params_t;
 | 
			
		||||
 | 
			
		||||
char* FindPackageInRepository(const char* repo, const char* pkg_name)
 | 
			
		||||
{
 | 
			
		||||
	char* repo_index_path = join_paths(repo, "repository.index");
 | 
			
		||||
	string_array_t repo_index = string_array_make();
 | 
			
		||||
	if ( !dictionary_append_file_path(&repo_index, repo_index_path) )
 | 
			
		||||
		error(1, errno, "bad repository: `%s'", repo_index_path);
 | 
			
		||||
	free(repo_index_path);
 | 
			
		||||
	const char* pkg_path_rel = dictionary_get(&repo_index, pkg_name);
 | 
			
		||||
	char* ret = pkg_path_rel ? join_paths(repo, pkg_path_rel) : NULL;
 | 
			
		||||
	string_array_reset(&repo_index);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char* FindPackage(string_array_t* repositories, const char* pkg_name)
 | 
			
		||||
{
 | 
			
		||||
	for ( size_t i = 0; i < repositories->length; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		const char* repo = repositories->strings[i];
 | 
			
		||||
		char* ret = FindPackageInRepository(repo, pkg_name);
 | 
			
		||||
		if ( ret )
 | 
			
		||||
			return ret;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
string_array_t GetPackageDependencies(params_t* params, const char* pkg_name)
 | 
			
		||||
{
 | 
			
		||||
	string_array_t ret = string_array_make();
 | 
			
		||||
 | 
			
		||||
	char* pkg_path = FindPackage(¶ms->repo_list, pkg_name);
 | 
			
		||||
	if ( !pkg_path )
 | 
			
		||||
		error(1, errno, "unable to locate package `%s'", pkg_name);
 | 
			
		||||
 | 
			
		||||
	const char* tixinfo_path = "tix/tixinfo";
 | 
			
		||||
	if ( !TarContainsFile(params->tar, pkg_path, tixinfo_path) )
 | 
			
		||||
		error(1, 0, "`%s' doesn't contain a `%s' file", pkg_path, tixinfo_path);
 | 
			
		||||
 | 
			
		||||
	string_array_t tixinfo = string_array_make();
 | 
			
		||||
	FILE* tixinfo_fp = TarOpenFile(params->tar, pkg_path, tixinfo_path);
 | 
			
		||||
	dictionary_append_file(&tixinfo, tixinfo_fp);
 | 
			
		||||
	fclose(tixinfo_fp);
 | 
			
		||||
 | 
			
		||||
	VerifyTixInformation(&tixinfo, pkg_path);
 | 
			
		||||
 | 
			
		||||
	const char* deps = dictionary_get(&tixinfo, "pkg.runtime-deps", "");
 | 
			
		||||
	string_array_append_token_string(&ret, deps);
 | 
			
		||||
 | 
			
		||||
	string_array_reset(&tixinfo);
 | 
			
		||||
 | 
			
		||||
	free(pkg_path);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GetPackageRecursiveDependencies(params_t* params, string_array_t* sofar,
 | 
			
		||||
                                     const char* pkg_name)
 | 
			
		||||
{
 | 
			
		||||
	if ( string_array_contains(sofar, pkg_name) )
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	// Avoid endless recursion by adding our package before the recursive call,
 | 
			
		||||
	// in case we need to satisfy cyclic dependencies.
 | 
			
		||||
	string_array_append(sofar, pkg_name);
 | 
			
		||||
 | 
			
		||||
	string_array_t pkg_deps = GetPackageDependencies(params, pkg_name);
 | 
			
		||||
	for ( size_t i = 0; i < pkg_deps.length; i++ )
 | 
			
		||||
		if ( !string_array_contains(sofar, pkg_deps.strings[i]) )
 | 
			
		||||
			GetPackageRecursiveDependencies(params, sofar, pkg_deps.strings[i]);
 | 
			
		||||
	string_array_reset(&pkg_deps);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void InstallPackageOfName(params_t* params, const char* pkg_name)
 | 
			
		||||
{
 | 
			
		||||
	char* pkg_path = FindPackage(¶ms->repo_list, pkg_name);
 | 
			
		||||
	if ( !pkg_path )
 | 
			
		||||
		error(1, errno, "unable to locate package `%s'", pkg_name);
 | 
			
		||||
 | 
			
		||||
	if ( fork_and_wait_or_death() )
 | 
			
		||||
	{
 | 
			
		||||
		const char* cmd_argv[] =
 | 
			
		||||
		{
 | 
			
		||||
			params->tix_install,
 | 
			
		||||
			"--collection", params->collection,
 | 
			
		||||
			"--tar", params->tar,
 | 
			
		||||
			"--", pkg_path,
 | 
			
		||||
			NULL
 | 
			
		||||
		};
 | 
			
		||||
		execvp(cmd_argv[0], (char* const*) cmd_argv);
 | 
			
		||||
		error(127, errno, "`%s'", cmd_argv[0]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	free(pkg_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Usage(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(fp, "Usage: %s PREFIX COMMAND [OPTION]...\n", argv0);
 | 
			
		||||
	fprintf(fp, "Front end to the Tix package management system.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Help(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	Usage(fp, argv0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Version(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	Usage(fp, argv0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
	params_t params;
 | 
			
		||||
	params.collection = strdup_null(getenv_def("TIX_COLLECTION", NULL));
 | 
			
		||||
	params.tar = strdup(getenv_def("TAR", "tar"));
 | 
			
		||||
	params.tix_install = strdup("tix-install");
 | 
			
		||||
 | 
			
		||||
	const char* argv0 = argv[0];
 | 
			
		||||
	for ( int i = 0; i < argc; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		const char* arg = argv[i];
 | 
			
		||||
		if ( arg[0] != '-' )
 | 
			
		||||
			continue;
 | 
			
		||||
		argv[i] = NULL;
 | 
			
		||||
		if ( !strcmp(arg, "--") )
 | 
			
		||||
			break;
 | 
			
		||||
		if ( arg[1] != '-' )
 | 
			
		||||
		{
 | 
			
		||||
			while ( char c = *++arg ) switch ( c )
 | 
			
		||||
			{
 | 
			
		||||
			default:
 | 
			
		||||
				fprintf(stderr, "%s: unknown option -- `%c'\n", argv0, c);
 | 
			
		||||
				Usage(stderr, argv0);
 | 
			
		||||
				exit(1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !strcmp(arg, "--help") ) { Help(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( !strcmp(arg, "--usage") ) { Usage(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( !strcmp(arg, "--version") ) { Version(stdout, argv0); exit(0); }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--collection", ¶ms.collection) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--tar", ¶ms.tar) ) { }
 | 
			
		||||
		else if ( GET_OPTION_VARIABLE("--tix-install", ¶ms.tix_install) ) { }
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
 | 
			
		||||
			Usage(stderr, argv0);
 | 
			
		||||
			exit(1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( argc == 1 )
 | 
			
		||||
	{
 | 
			
		||||
		Usage(stdout, argv0);
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CompactArguments(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	ParseOptionalCommandLineCollectionPrefix(¶ms.collection, &argc, &argv);
 | 
			
		||||
	VerifyCommandLineCollection(¶ms.collection);
 | 
			
		||||
 | 
			
		||||
	params.tixdb_path = join_paths(params.collection, "tix");
 | 
			
		||||
 | 
			
		||||
	char* coll_conf_path = join_paths(params.tixdb_path, "collection.conf");
 | 
			
		||||
	params.coll_conf = string_array_make();
 | 
			
		||||
	if ( !dictionary_append_file_path(¶ms.coll_conf, coll_conf_path) )
 | 
			
		||||
		error(1, errno, "`%s'", coll_conf_path);
 | 
			
		||||
	VerifyTixCollectionConfiguration(¶ms.coll_conf, coll_conf_path);
 | 
			
		||||
	free(coll_conf_path);
 | 
			
		||||
 | 
			
		||||
	char* repo_list_path = join_paths(params.tixdb_path, "repository.list");
 | 
			
		||||
	params.repo_list = string_array_make();
 | 
			
		||||
	if ( !string_array_append_file_path(¶ms.repo_list, repo_list_path) )
 | 
			
		||||
		error(1, errno, "`%s'", repo_list_path);
 | 
			
		||||
	free(repo_list_path);
 | 
			
		||||
 | 
			
		||||
	char* inst_list_path = join_paths(params.tixdb_path, "installed.list");
 | 
			
		||||
	params.inst_list = string_array_make();
 | 
			
		||||
	if ( !string_array_append_file_path(¶ms.inst_list, inst_list_path) )
 | 
			
		||||
		error(1, errno, "`%s'", inst_list_path);
 | 
			
		||||
	free(inst_list_path);
 | 
			
		||||
 | 
			
		||||
	if ( argc == 1 )
 | 
			
		||||
	{
 | 
			
		||||
		error(0, 0, "error: no command specified.");
 | 
			
		||||
		Usage(stderr, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const char* cmd = argv[1];
 | 
			
		||||
	if ( !strcmp(cmd, "install") )
 | 
			
		||||
	{
 | 
			
		||||
		if ( argc == 2 )
 | 
			
		||||
		{
 | 
			
		||||
			error(0, 0, "expected list of packages to install after `install'");
 | 
			
		||||
			Usage(stderr, argv0);
 | 
			
		||||
			exit(1);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		string_array_t work = string_array_make();
 | 
			
		||||
 | 
			
		||||
		for ( int i = 2; i < argc; i++ )
 | 
			
		||||
		{
 | 
			
		||||
			const char* pkg_name = argv[i];
 | 
			
		||||
			if ( string_array_contains(¶ms.inst_list, pkg_name) )
 | 
			
		||||
			{
 | 
			
		||||
				printf("Package `%s' is already installed.\n", pkg_name);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			GetPackageRecursiveDependencies(¶ms, &work, pkg_name);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for ( size_t i = 0; i < work.length; i++ )
 | 
			
		||||
			InstallPackageOfName(¶ms, work.strings[i]);
 | 
			
		||||
 | 
			
		||||
		string_array_reset(&work);
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "%s: unknown command: `%s'\n", argv0, cmd);
 | 
			
		||||
		Usage(stderr, argv0);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1033
									
								
								tix/util.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1033
									
								
								tix/util.h
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue