From 0fb20a657d44f0afd5f1feed096cfe434fb6b56f Mon Sep 17 00:00:00 2001 From: midipix Date: Sep 24 2016 23:47:58 +0000 Subject: uninstall mode: initial implementation. --- diff --git a/include/slibtool/slibtool.h b/include/slibtool/slibtool.h index d16c16f..3ea06a9 100644 --- a/include/slibtool/slibtool.h +++ b/include/slibtool/slibtool.h @@ -261,6 +261,7 @@ slbt_api int slbt_exec_compile (const struct slbt_driver_ctx *, struct slbt_ex slbt_api int slbt_exec_execute (const struct slbt_driver_ctx *, struct slbt_exec_ctx *); slbt_api int slbt_exec_install (const struct slbt_driver_ctx *, struct slbt_exec_ctx *); slbt_api int slbt_exec_link (const struct slbt_driver_ctx *, struct slbt_exec_ctx *); +slbt_api int slbt_exec_uninstall (const struct slbt_driver_ctx *, struct slbt_exec_ctx *); slbt_api int slbt_set_alternate_host (const struct slbt_driver_ctx *, const char * host, const char * flavor); slbt_api void slbt_reset_alternate_host (const struct slbt_driver_ctx *); @@ -282,6 +283,7 @@ slbt_api int slbt_output_compile (const struct slbt_driver_ctx *, const struct slbt_api int slbt_output_execute (const struct slbt_driver_ctx *, const struct slbt_exec_ctx *); slbt_api int slbt_output_install (const struct slbt_driver_ctx *, const struct slbt_exec_ctx *); slbt_api int slbt_output_link (const struct slbt_driver_ctx *, const struct slbt_exec_ctx *); +slbt_api int slbt_output_uninstall (const struct slbt_driver_ctx *, const struct slbt_exec_ctx *); slbt_api int slbt_output_error_record (const struct slbt_driver_ctx *, const struct slbt_error_info *); slbt_api int slbt_output_error_vector (const struct slbt_driver_ctx *); diff --git a/project/common.mk b/project/common.mk index 56121f2..47c5d74 100644 --- a/project/common.mk +++ b/project/common.mk @@ -14,12 +14,14 @@ COMMON_SRCS = \ src/logic/slbt_exec_execute.c \ src/logic/slbt_exec_install.c \ src/logic/slbt_exec_link.c \ + src/logic/slbt_exec_uninstall.c \ src/logic/slbt_map_input.c \ src/output/slbt_output_config.c \ src/output/slbt_output_error.c \ src/output/slbt_output_exec.c \ src/skin/slbt_skin_default.c \ src/skin/slbt_skin_install.c \ + src/skin/slbt_skin_uninstall.c \ APP_SRCS = \ src/slibtool.c diff --git a/project/headers.mk b/project/headers.mk index 20fa046..0a518a8 100644 --- a/project/headers.mk +++ b/project/headers.mk @@ -12,5 +12,6 @@ INTERNAL_HEADERS = \ $(PROJECT_DIR)/src/internal/$(PACKAGE)_readlink_impl.h \ $(PROJECT_DIR)/src/internal/$(PACKAGE)_spawn_impl.h \ $(PROJECT_DIR)/src/internal/$(PACKAGE)_symlink_impl.h \ + $(PROJECT_DIR)/src/internal/$(PACKAGE)_uninstall_impl.h \ ALL_HEADERS = $(API_HEADERS) $(INTERNAL_HEADERS) diff --git a/src/driver/slbt_amain.c b/src/driver/slbt_amain.c index 8c29e8d..25a1159 100644 --- a/src/driver/slbt_amain.c +++ b/src/driver/slbt_amain.c @@ -67,6 +67,9 @@ static void slbt_perform_driver_actions(struct slbt_driver_ctx * dctx) if (dctx->cctx->mode == SLBT_MODE_LINK) slbt_exec_link(dctx,0); + + if (dctx->cctx->mode == SLBT_MODE_UNINSTALL) + slbt_exec_uninstall(dctx,0); } static void slbt_perform_unit_actions(struct slbt_unit_ctx * uctx) diff --git a/src/internal/slibtool_uninstall_impl.h b/src/internal/slibtool_uninstall_impl.h new file mode 100644 index 0000000..b78bf37 --- /dev/null +++ b/src/internal/slibtool_uninstall_impl.h @@ -0,0 +1,25 @@ +#ifndef SLIBTOOL_UNINSTALL_IMPL_H +#define SLIBTOOL_UNINSTALL_IMPL_H + +#include "argv/argv.h" + +extern const struct argv_option slbt_uninstall_options[]; + +enum uninstall_tags { + TAG_UNINSTALL_HELP, + TAG_UNINSTALL_VERSION, + TAG_UNINSTALL_FORCE, + TAG_UNINSTALL_RMDIR, + TAG_UNINSTALL_VERBOSE, + TAG_UNINSTALL_FORBIDDEN, + TAG_UNINSTALL_RECURSIVE = TAG_UNINSTALL_FORBIDDEN, +}; + +#define SLBT_UNINSTALL_HELP 0x0001 +#define SLBT_UNINSTALL_VERSION 0x0002 +#define SLBT_UNINSTALL_FORCE 0x0004 +#define SLBT_UNINSTALL_RMDIR 0x0008 +#define SLBT_UNINSTALL_VERBOSE 0x0010 +#define SLBT_UNINSTALL_FORBIDDEN 0x8000 + +#endif diff --git a/src/logic/slbt_exec_uninstall.c b/src/logic/slbt_exec_uninstall.c new file mode 100644 index 0000000..2e54f79 --- /dev/null +++ b/src/logic/slbt_exec_uninstall.c @@ -0,0 +1,348 @@ +/*******************************************************************/ +/* slibtool: a skinny libtool implementation, written in C */ +/* Copyright (C) 2016 Z. Gilboa */ +/* Released under the Standard MIT License; see COPYING.SLIBTOOL. */ +/*******************************************************************/ + +#include +#include +#include +#include +#include +#include + +#define ARGV_DRIVER + +#include +#include "slibtool_uninstall_impl.h" +#include "slibtool_readlink_impl.h" +#include "slibtool_errinfo_impl.h" +#include "argv/argv.h" + +static int slbt_uninstall_usage( + const char * program, + const char * arg, + const struct argv_option * options, + struct argv_meta * meta) +{ + char header[512]; + + snprintf(header,sizeof(header), + "Usage: %s --mode=uninstall [options] [DEST]...\n" + "Options:\n", + program); + + argv_usage(stdout,header,options,arg); + argv_free(meta); + + return SLBT_USAGE; +} + +static int slbt_exec_uninstall_fail( + struct slbt_exec_ctx * actx, + struct argv_meta * meta, + int ret) +{ + argv_free(meta); + slbt_free_exec_ctx(actx); + return ret; +} + +static int slbt_exec_uninstall_fs_entry( + const struct slbt_driver_ctx * dctx, + struct slbt_exec_ctx * ectx, + char ** parg, + char * path, + uint32_t flags) +{ + struct stat st; + char * slash; + char dpath[PATH_MAX]; + + /* needed? */ + if (stat(path,&st) && (errno == ENOENT)) + if (lstat(path,&st) && (errno == ENOENT)) + return 0; + + /* output */ + *parg = path; + + if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT)) + if (slbt_output_uninstall(dctx,ectx)) + return SLBT_NESTED_ERROR(dctx); + + /* directory? */ + if (S_ISDIR(st.st_mode)) { + if (!(rmdir(path))) + return 0; + + else if ((errno == EEXIST) || (errno == ENOTEMPTY)) + return 0; + + else + return SLBT_SYSTEM_ERROR(dctx); + } + + /* remove file or symlink entry */ + if (unlink(path)) + return SLBT_SYSTEM_ERROR(dctx); + + /* remove empty containing directory? */ + if (flags & SLBT_UNINSTALL_RMDIR) { + strcpy(dpath,path); + + /* invalid (current) directory? */ + if (!(slash = strrchr(dpath,'/'))) + return 0; + + *slash = 0; + + if (rmdir(dpath)) + return SLBT_SYSTEM_ERROR(dctx); + } + + return 0; +} + +static int slbt_exec_uninstall_versioned_library( + const struct slbt_driver_ctx * dctx, + struct slbt_exec_ctx * ectx, + char ** parg, + char * rpath, + char * lpath, + const char * suffix, + uint32_t flags) +{ + char * slash; + char * dot; + char * path; + char apath[PATH_MAX]; + + /* normalize library link path */ + if (lpath[0] == '/') { + path = lpath; + + } else if (!(slash = strrchr(rpath,'/'))) { + path = lpath; + + } else { + strcpy(apath,rpath); + strcpy(&apath[slash-rpath+1],lpath); + path = apath; + } + + /* delete associated version files */ + while ((dot = strrchr(path,'.')) && (strcmp(dot,suffix))) { + if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags)) + return SLBT_NESTED_ERROR(dctx); + + *dot = 0; + } + + return 0; +} + +static int slbt_exec_uninstall_entry( + const struct slbt_driver_ctx * dctx, + struct slbt_exec_ctx * ectx, + struct argv_entry * entry, + char ** parg, + uint32_t flags) +{ + char path [PATH_MAX]; + char lpath[PATH_MAX]; + char * dot; + + if ((size_t)snprintf(path,PATH_MAX,"%s", + entry->arg) >= PATH_MAX-8) + return SLBT_BUFFER_ERROR(dctx); + + *parg = (char *)entry->arg; + + /* remove explicit argument */ + if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags)) + return SLBT_NESTED_ERROR(dctx); + + /* non-.la-wrapper argument? */ + if (!(dot = strrchr(path,'.'))) + return 0; + + else if (strcmp(dot,".la")) + return 0; + + /* remove .a archive as needed */ + strcpy(dot,".a"); + + if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags)) + return SLBT_NESTED_ERROR(dctx); + + /* .so symlink? */ + strcpy(dot,".so"); + + if (!(slbt_readlink(path,lpath,sizeof(lpath)))) + if (slbt_exec_uninstall_versioned_library( + dctx,ectx,parg, + path,lpath, + ".so",flags)) + return SLBT_NESTED_ERROR(dctx); + + /* .lib.a symlink? */ + strcpy(dot,".lib.a"); + + if (!(slbt_readlink(path,lpath,sizeof(lpath)))) + if (slbt_exec_uninstall_versioned_library( + dctx,ectx,parg, + path,lpath, + ".lib.a",flags)) + return SLBT_NESTED_ERROR(dctx); + + /* .dll symlink? */ + strcpy(dot,".dll"); + + if (!(slbt_readlink(path,lpath,sizeof(lpath)))) + if (slbt_exec_uninstall_versioned_library( + dctx,ectx,parg, + path,lpath, + ".dll",flags)) + return SLBT_NESTED_ERROR(dctx); + + /* remove .so library as needed */ + strcpy(dot,".so"); + + if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags)) + return SLBT_NESTED_ERROR(dctx); + + /* remove .lib.a import library as needed */ + strcpy(dot,".lib.a"); + + if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags)) + return SLBT_NESTED_ERROR(dctx); + + /* remove .dll library as needed */ + strcpy(dot,".dll"); + + if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags)) + return SLBT_NESTED_ERROR(dctx); + + /* remove .exe image as needed */ + strcpy(dot,".exe"); + + if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags)) + return SLBT_NESTED_ERROR(dctx); + + /* remove binary image as needed */ + *dot = 0; + + if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags)) + return SLBT_NESTED_ERROR(dctx); + + return 0; +} + +int slbt_exec_uninstall( + const struct slbt_driver_ctx * dctx, + struct slbt_exec_ctx * ectx) +{ + int ret; + char ** argv; + char ** iargv; + uint32_t flags; + struct slbt_exec_ctx * actx; + struct argv_meta * meta; + struct argv_entry * entry; + const struct argv_option * options = slbt_uninstall_options; + + /* dry run */ + if (dctx->cctx->drvflags & SLBT_DRIVER_DRY_RUN) + return 0; + + /* context */ + if (ectx) + actx = 0; + else if ((ret = slbt_get_exec_ctx(dctx,&ectx))) + return ret; + else + actx = ectx; + + /* initial state, uninstall mode skin */ + slbt_reset_arguments(ectx); + slbt_disable_placeholders(ectx); + iargv = ectx->cargv; + + /* work around non-conforming uses of --mode=uninstall */ + if (!(strcmp(iargv[0],"/bin/sh")) || !strcmp(iargv[0],"/bin/bash")) + iargv++; + + /* missing arguments? */ + if (!iargv[1] && (dctx->cctx->drvflags & SLBT_DRIVER_VERBOSITY_USAGE)) + return slbt_uninstall_usage(dctx->program,0,options,0); + + /* argv meta */ + if (!(meta = argv_get( + iargv, + options, + dctx->cctx->drvflags & SLBT_DRIVER_VERBOSITY_ERRORS + ? ARGV_VERBOSITY_ERRORS + : ARGV_VERBOSITY_NONE))) + return slbt_exec_uninstall_fail( + actx,meta, + SLBT_CUSTOM_ERROR(dctx,0)); + + /* dest, alternate argument vector options */ + argv = ectx->altv; + flags = 0; + + *argv++ = iargv[0]; + + for (entry=meta->entries; entry->fopt || entry->arg; entry++) { + if (entry->fopt) { + switch (entry->tag) { + case TAG_UNINSTALL_HELP: + flags |= SLBT_UNINSTALL_HELP; + break; + + case TAG_UNINSTALL_VERSION: + flags |= SLBT_UNINSTALL_VERSION; + break; + + case TAG_UNINSTALL_FORCE: + *argv++ = "-f"; + flags |= SLBT_UNINSTALL_FORCE; + break; + + case TAG_UNINSTALL_RMDIR: + *argv++ = "-d"; + flags |= SLBT_UNINSTALL_RMDIR; + break; + + case TAG_UNINSTALL_VERBOSE: + *argv++ = "-v"; + flags |= SLBT_UNINSTALL_VERBOSE; + break; + } + } + } + + /* --help */ + if (flags & SLBT_UNINSTALL_HELP) { + slbt_uninstall_usage(dctx->program,0,options,meta); + return 0; + } + + /* uninstall */ + ectx->argv = ectx->altv; + ectx->program = ectx->altv[0]; + + /* uninstall entries one at a time */ + for (entry=meta->entries; entry->fopt || entry->arg; entry++) + if (!entry->fopt) + if (slbt_exec_uninstall_entry(dctx,ectx,entry,argv,flags)) + return slbt_exec_uninstall_fail( + actx,meta, + SLBT_NESTED_ERROR(dctx)); + + argv_free(meta); + slbt_free_exec_ctx(actx); + + return 0; +} diff --git a/src/output/slbt_output_exec.c b/src/output/slbt_output_exec.c index 6360ca2..7ef4d88 100644 --- a/src/output/slbt_output_exec.c +++ b/src/output/slbt_output_exec.c @@ -125,3 +125,10 @@ int slbt_output_link( { return slbt_output_exec(dctx,ectx,"link"); } + +int slbt_output_uninstall( + const struct slbt_driver_ctx * dctx, + const struct slbt_exec_ctx * ectx) +{ + return slbt_output_exec(dctx,ectx,"uninstall"); +} diff --git a/src/skin/slbt_skin_uninstall.c b/src/skin/slbt_skin_uninstall.c new file mode 100644 index 0000000..e34c81e --- /dev/null +++ b/src/skin/slbt_skin_uninstall.c @@ -0,0 +1,25 @@ +#include "slibtool_uninstall_impl.h" +#include "argv/argv.h" + +const struct argv_option slbt_uninstall_options[] = { + {"help", 'h',TAG_UNINSTALL_HELP,ARGV_OPTARG_NONE,0,0,0, + "display uninstall mode help"}, + + {"version", 0,TAG_UNINSTALL_VERSION,ARGV_OPTARG_NONE,0,0,0, + "display version information"}, + + {"force", 'f',TAG_UNINSTALL_FORCE,ARGV_OPTARG_NONE,0,0,0, + "force file removal"}, + + {"dir", 'd',TAG_UNINSTALL_RMDIR,ARGV_OPTARG_NONE,0,0,0, + "remove empty directories"}, + + {"recursive", 'r',TAG_UNINSTALL_RECURSIVE,ARGV_OPTARG_NONE,0,0,0, + "remove directories recursively; " + "passing this argument to slibtool is forbidden"}, + + {"verbose", 'v',TAG_UNINSTALL_VERBOSE,ARGV_OPTARG_NONE,0,0,0, + "spray the terminal with colorful information"}, + + {0,0,0,0,0,0,0,0} +};