Add the Tix package management system.

This commit is contained in:
Jonas 'Sortie' Termansen 2013-03-26 12:40:43 +01:00
parent dce618af93
commit b0d07b9142
16 changed files with 4532 additions and 4 deletions

View File

@ -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
View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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(&params->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(&params->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", &params.collection) ) { }
else if ( GET_OPTION_VARIABLE("--tar", &params.tar) ) { }
else if ( GET_OPTION_VARIABLE("--tix-install", &params.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(&params.collection, &argc, &argv);
VerifyCommandLineCollection(&params.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(&params.coll_conf, coll_conf_path) )
error(1, errno, "`%s'", coll_conf_path);
VerifyTixCollectionConfiguration(&params.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(&params.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(&params.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(&params.inst_list, pkg_name) )
{
printf("Package `%s' is already installed.\n", pkg_name);
continue;
}
GetPackageRecursiveDependencies(&params, &work, pkg_name);
}
for ( size_t i = 0; i < work.length; i++ )
InstallPackageOfName(&params, 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

File diff suppressed because it is too large Load Diff