/*******************************************************************/ /* 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_install_impl.h" #include "slibtool_spawn_impl.h" #include "slibtool_symlink_impl.h" #include "argv/argv.h" static int slbt_install_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=install [options] [SOURCE]... DEST\n" "Options:\n", program); argv_usage(stdout,header,options,arg); argv_free(meta); return SLBT_USAGE; } static int slbt_exec_install_fail( struct slbt_exec_ctx * actx, struct argv_meta * meta) { argv_free(meta); slbt_free_exec_ctx(actx); return -1; } static int slbt_exec_install_init_dstdir( struct argv_entry * dest, struct argv_entry * last, char * dstdir) { struct stat st; char * slash; size_t len; if (dest) last = dest; /* dstdir: initial string */ if ((size_t)snprintf(dstdir,PATH_MAX,"%s", last->arg) >= PATH_MAX) return -1; /* dstdir might end with a slash */ len = strlen(dstdir); if (dstdir[--len] == '/') dstdir[len] = '\0'; /* -t DSTDIR? */ if (dest) return 0; /* is DEST a directory? */ if (!(stat(dstdir,&st))) if (S_ISDIR(st.st_mode)) return 0; /* remove last path component */ if ((slash = strrchr(dstdir,'/'))) *slash = '\0'; return 0; } static int slbt_exec_install_entry( const struct slbt_driver_ctx * dctx, struct slbt_exec_ctx * ectx, struct argv_entry * entry, struct argv_entry * last, struct argv_entry * dest, char * dstdir, char ** src, char ** dst) { int ret; char * dot; char * base; char * slash; char target [PATH_MAX]; char srcfile [PATH_MAX]; char dstfile [PATH_MAX]; char slnkname[PATH_MAX]; char dlnkname[PATH_MAX]; char lasource[PATH_MAX]; bool fexe = false; struct stat st; /* executable wrapper? */ if ((size_t)snprintf(slnkname,sizeof(slnkname),"%s.exe.wrapper", entry->arg) >= sizeof(slnkname)) return -1; fexe = stat(slnkname,&st) ? false : true; /* .la ? */ if (!fexe && (!(dot = strrchr(entry->arg,'.')) || strcmp(dot,".la"))) { *src = (char *)entry->arg; *dst = dest ? 0 : (char *)last->arg; if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT)) if (slbt_output_install(dctx,ectx)) return -1; return (((ret = slbt_spawn(ectx,true)) < 0) || ectx->exitcode) ? -1 : 0; } /* srcfile */ if (strlen(entry->arg) + strlen(".libs/") >= (PATH_MAX-1)) return -1; strcpy(lasource,entry->arg); if ((slash = strrchr(lasource,'/'))) { *slash++ = '\0'; sprintf(srcfile,"%s/.libs/%s",lasource,slash); } else sprintf(srcfile,".libs/%s",lasource); /* executable? */ if (fexe) { *src = srcfile; *dst = dest ? 0 : (char *)last->arg; if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT)) if (slbt_output_install(dctx,ectx)) return -1; return (((ret = slbt_spawn(ectx,true)) < 0) || ectx->exitcode) ? -1 : 0; } /* libfoo.la --> libfoo.so */ strcpy(slnkname,srcfile); dot = strrchr(slnkname,'.'); sprintf(dot,dctx->cctx->settings.dsosuffix); /* basename */ if ((base = strrchr(slnkname,'/'))) base++; else base = slnkname; /* source (build) symlink target */ if (readlink(slnkname,target,sizeof(target)) <= 0) return -1; /* srcfile: .libs/libfoo.so.x.y.z */ slash = strrchr(srcfile,'/'); strcpy(++slash,target); /* dstfile */ if (!dest) if ((size_t)snprintf(dstfile,sizeof(dstfile),"%s/%s", dstdir,target) >= sizeof(dstfile)) return -1; /* spawn */ *src = srcfile; *dst = dest ? 0 : dstfile; if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT)) if (slbt_output_install(dctx,ectx)) return -1; if (((ret = slbt_spawn(ectx,true)) < 0) || ectx->exitcode) return -1; /* destination symlink: dstdir/libfoo.so */ if ((size_t)snprintf(dlnkname,sizeof(dlnkname),"%s/%s", dstdir,base) >= sizeof(dlnkname)) return -1; /* create symlink: libfoo.so --> libfoo.so.x.y.z */ if (slbt_create_symlink( dctx,ectx, target,dlnkname, false)) return -1; /* libfoo.so.x.y.z --> libfoo.so.x */ strcpy(slnkname,target); if ((dot = strrchr(slnkname,'.'))) *dot = '\0'; else return -1; if ((dot = strrchr(slnkname,'.'))) *dot = '\0'; else return -1; /* destination symlink: dstdir/libfoo.so.x */ if ((size_t)snprintf(dlnkname,sizeof(dlnkname),"%s/%s", dstdir,slnkname) >= sizeof(dlnkname)) return -1; /* create symlink: libfoo.so.x --> libfoo.so.x.y.z */ if (slbt_create_symlink( dctx,ectx, target,dlnkname, false)) return -1; return 0; } int slbt_exec_install( const struct slbt_driver_ctx * dctx, struct slbt_exec_ctx * ectx) { int ret; char ** argv; char ** src; char ** dst; struct slbt_exec_ctx * actx; struct argv_meta * meta; struct argv_entry * entry; struct argv_entry * copy; struct argv_entry * dest; struct argv_entry * last; char dstdir[PATH_MAX]; const struct argv_option * options = slbt_install_options; /* context */ if (ectx) actx = 0; else if ((ret = slbt_get_exec_ctx(dctx,&ectx))) return ret; else actx = ectx; /* initial state, install mode skin */ slbt_reset_arguments(ectx); slbt_disable_placeholders(ectx); argv = ectx->cargv; /* missing arguments? */ if (!argv[1] && (dctx->cctx->drvflags & SLBT_DRIVER_VERBOSITY_USAGE)) return slbt_install_usage(dctx->program,0,options,0); /* argv meta */ if (!(meta = argv_get( argv, options, dctx->cctx->drvflags & SLBT_DRIVER_VERBOSITY_ERRORS ? ARGV_VERBOSITY_ERRORS : ARGV_VERBOSITY_NONE))) return slbt_exec_install_fail(actx,meta); /* dest, alternate argument vector options */ argv = ectx->altv; copy = meta->entries; dest = 0; last = 0; *argv++ = ectx->cargv[0]; for (entry=meta->entries; entry->fopt || entry->arg; entry++) { if (entry->fopt) { switch (entry->tag) { case TAG_INSTALL_COPY: *argv++ = "-c"; copy = entry; break; case TAG_INSTALL_MKDIR: *argv++ = "-d"; copy = 0; break; case TAG_INSTALL_TARGET_MKDIR: *argv++ = "-D"; copy = 0; break; case TAG_INSTALL_STRIP: *argv++ = "-s"; break; case TAG_INSTALL_PRESERVE: *argv++ = "-p"; break; case TAG_INSTALL_USER: *argv++ = "-o"; break; case TAG_INSTALL_GROUP: *argv++ = "-g"; break; case TAG_INSTALL_MODE: *argv++ = "-m"; break; case TAG_INSTALL_DSTDIR: *argv++ = "-t"; dest = entry; break; } if (entry->fval) *argv++ = (char *)entry->arg; } else last = entry; } /* install */ if (copy) { /* using alternate argument vector */ ectx->argv = ectx->altv; ectx->program = ectx->altv[0]; /* marks */ src = argv++; dst = argv++; /* dstdir */ if (slbt_exec_install_init_dstdir(dest,last,dstdir)) return slbt_exec_install_fail(actx,meta); /* install entries one at a time */ for (entry=meta->entries; entry->fopt || entry->arg; entry++) if (!entry->fopt && (dest || (entry != last))) if (slbt_exec_install_entry( dctx,ectx, entry,last, dest,dstdir, src,dst)) return slbt_exec_install_fail(actx,meta); } else { /* using original argument vector */ ectx->argv = ectx->cargv; ectx->program = ectx->cargv[0]; /* spawn */ if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT)) if (slbt_output_install(dctx,ectx)) return -1; if (((ret = slbt_spawn(ectx,true)) < 0) || ectx->exitcode) return slbt_exec_install_fail(actx,meta); } argv_free(meta); slbt_free_exec_ctx(actx); return 0; }