/*******************************************************************/
/* slibtool: a skinny libtool implementation, written in C */
/* Copyright (C) 2016--2024 SysDeer Technologies, LLC */
/* Released under the Standard MIT License; see COPYING.SLIBTOOL. */
/*******************************************************************/
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <slibtool/slibtool.h>
#include "slibtool_version.h"
#include "slibtool_driver_impl.h"
#include "slibtool_objlist_impl.h"
#include "slibtool_errinfo_impl.h"
#include "slibtool_visibility_impl.h"
#include "slibtool_ar_impl.h"
#include "argv/argv.h"
static char * slbt_default_cargv[] = {"cc",0};
slbt_hidden int slbt_split_argv(
char ** argv,
uint64_t flags,
struct slbt_split_vector * sargv,
struct slbt_obj_list ** aobjlistv,
int fderr,
int fdcwd)
{
int i;
int argc;
int objc;
const char * program;
char * compiler;
char * csysroot;
char ** dargv;
char ** targv;
char ** cargv;
char ** objp;
struct slbt_obj_list * objlistv;
struct slbt_obj_list * objlistp;
char * dst;
bool flast;
bool fcopy;
bool altmode;
size_t size;
const char * base;
struct argv_meta * meta;
struct argv_entry * entry;
struct argv_entry * mode;
struct argv_entry * help;
struct argv_entry * version;
struct argv_entry * info;
struct argv_entry * config;
struct argv_entry * finish;
struct argv_entry * features;
struct argv_entry * ccwrap;
struct argv_entry * dumpmachine;
struct argv_entry * aropt;
const struct argv_option ** popt;
const struct argv_option ** optout;
const struct argv_option * optv[SLBT_OPTV_ELEMENTS];
struct argv_ctx ctx = {ARGV_VERBOSITY_NONE,
ARGV_MODE_SCAN,
0,0,0,0,0,0,0};
program = slbt_program_name(argv[0]);
/* missing arguments? */
if ((altmode = (flags & SLBT_DRIVER_MODE_AR))) {
slbt_optv_init(slbt_ar_options,optv);
} else {
slbt_optv_init(slbt_default_options,optv);
}
if (!argv[1] && !altmode && (flags & SLBT_DRIVER_VERBOSITY_USAGE))
return slbt_driver_usage(
fderr,program,
0,optv,0,sargv,0,
!!getenv("NO_COLOR"));
/* initial argv scan: ... --mode=xxx ... <compiler> ... */
slbt_argv_scan(argv,optv,&ctx,0);
/* invalid slibtool arguments? */
if (ctx.erridx && !ctx.unitidx && altmode) {
if (flags & SLBT_DRIVER_VERBOSITY_ERRORS)
slbt_argv_get(
argv,optv,
slbt_argv_flags(flags),
fderr);
return -1;
}
/* error possibly due to an altmode argument? */
if (ctx.erridx && !ctx.unitidx)
ctx.unitidx = ctx.erridx;
/* obtain slibtool's own arguments */
if (ctx.unitidx) {
compiler = argv[ctx.unitidx];
argv[ctx.unitidx] = 0;
meta = slbt_argv_get(argv,optv,ARGV_VERBOSITY_NONE,fderr);
argv[ctx.unitidx] = compiler;
} else {
meta = slbt_argv_get(argv,optv,ARGV_VERBOSITY_NONE,fderr);
}
if (!meta) {
if (flags & SLBT_DRIVER_VERBOSITY_ERRORS)
slbt_argv_get(
argv,optv,
slbt_argv_flags(flags),
fderr);
return -1;
}
/* missing all of --mode, --help, --version, --info, --config, --dumpmachine, --features, and --finish? */
mode = help = version = info = config = finish = features = ccwrap = dumpmachine = aropt = 0;
for (entry=meta->entries; entry->fopt; entry++)
if (entry->tag == TAG_MODE)
mode = entry;
else if (entry->tag == TAG_HELP)
help = entry;
else if (entry->tag == TAG_VERSION)
version = entry;
else if (entry->tag == TAG_INFO)
info = entry;
else if (entry->tag == TAG_CONFIG)
config = entry;
else if (entry->tag == TAG_FINISH)
finish = entry;
else if (entry->tag == TAG_FEATURES)
features = entry;
else if (entry->tag == TAG_CCWRAP)
ccwrap = entry;
else if (entry->tag == TAG_DUMPMACHINE)
dumpmachine = entry;
/* alternate execusion mode? */
if (!altmode && mode && !strcmp(mode->arg,"ar"))
aropt = mode;
/* release temporary argv meta context */
slbt_argv_free(meta);
/* error not due to an altmode argument? */
if (!aropt && ctx.erridx && (ctx.erridx == ctx.unitidx)) {
if (flags & SLBT_DRIVER_VERBOSITY_ERRORS)
slbt_argv_get(
argv,optv,
slbt_argv_flags(flags),
fderr);
return -1;
}
if (!mode && !help && !version && !info && !config && !finish && !features && !dumpmachine && !altmode) {
slbt_dprintf(fderr,
"%s: error: --mode must be specified.\n",
program);
return -1;
}
/* missing compiler? */
if (!ctx.unitidx && !help && !info && !config && !version && !finish && !features && !dumpmachine && !altmode && !aropt) {
if (flags & SLBT_DRIVER_VERBOSITY_ERRORS)
slbt_dprintf(fderr,
"%s: error: <compiler> is missing.\n",
program);
return -1;
}
/* clone and normalize the argv vector */
for (argc=0,size=0,dargv=argv; *dargv; argc++,dargv++)
size += strlen(*dargv) + 1;
if (!(sargv->dargv = calloc(argc+1,sizeof(char *))))
return -1;
else if (!(sargv->dargs = calloc(1,size+1)))
return -1;
else if (!(*aobjlistv = calloc(argc,sizeof(**aobjlistv)))) {
free(sargv->dargv);
free(sargv->dargs);
return -1;
}
objlistv = *aobjlistv;
objlistp = objlistv;
csysroot = 0;
for (i=0,flast=false,dargv=sargv->dargv,dst=sargv->dargs; i<argc; i++) {
if ((fcopy = (flast || altmode || aropt))) {
(void)0;
} else if (!strcmp(argv[i],"--")) {
flast = true;
fcopy = true;
} else if (!strcmp(argv[i],"-I")) {
*dargv++ = dst;
*dst++ = '-';
*dst++ = 'I';
strcpy(dst,argv[++i]);
dst += strlen(dst)+1;
} else if (!strncmp(argv[i],"-I",2)) {
fcopy = true;
} else if (!strcmp(argv[i],"-l")) {
*dargv++ = dst;
*dst++ = '-';
*dst++ = 'l';
strcpy(dst,argv[++i]);
dst += strlen(dst)+1;
} else if (!strncmp(argv[i],"-l",2)) {
fcopy = true;
} else if (!strcmp(argv[i],"--library")) {
*dargv++ = dst;
*dst++ = '-';
*dst++ = 'l';
strcpy(dst,argv[++i]);
dst += strlen(dst)+1;
} else if (!strncmp(argv[i],"--library=",10)) {
*dargv++ = dst;
*dst++ = '-';
*dst++ = 'l';
strcpy(dst,&argv[++i][10]);
dst += strlen(dst)+1;
} else if (!strcmp(argv[i],"-L")) {
*dargv++ = dst;
*dst++ = '-';
*dst++ = 'L';
strcpy(dst,argv[++i]);
dst += strlen(dst)+1;
} else if (!strncmp(argv[i],"-L",2)) {
fcopy = true;
} else if (!strcmp(argv[i],"-Xlinker")) {
*dargv++ = dst;
*dst++ = '-';
*dst++ = 'W';
*dst++ = 'l';
*dst++ = ',';
strcpy(dst,argv[++i]);
dst += strlen(dst)+1;
} else if (!strcmp(argv[i],"--library-path")) {
*dargv++ = dst;
*dst++ = '-';
*dst++ = 'L';
strcpy(dst,argv[++i]);
dst += strlen(dst)+1;
} else if (!strncmp(argv[i],"--library-path=",15)) {
*dargv++ = dst;
*dst++ = '-';
*dst++ = 'L';
strcpy(dst,&argv[i][15]);
dst += strlen(dst)+1;
} else if (!strcmp(argv[i],"--sysroot") && (i<ctx.unitidx)) {
*dargv++ = dst;
csysroot = dst;
strcpy(dst,argv[i]);
dst[9] = '=';
strcpy(&dst[10],argv[++i]);
dst += strlen(dst)+1;
ctx.unitidx--;
} else if (!strncmp(argv[i],"--sysroot=",10) && (i<ctx.unitidx)) {
*dargv++ = dst;
csysroot = dst;
strcpy(dst,argv[i]);
dst += strlen(dst)+1;
} else if (!strcmp(argv[i],"-objectlist")) {
*dargv++ = dst;
strcpy(dst,argv[i++]);
dst += strlen(dst)+1;
objlistp->name = dst;
objlistp++;
fcopy = true;
} else {
fcopy = true;
}
if (fcopy) {
*dargv++ = dst;
strcpy(dst,argv[i]);
dst += strlen(dst)+1;
}
}
/* update argc,argv */
argc = dargv - sargv->dargv;
argv = sargv->dargv;
/* iterate through the object list vector: map, parse, store */
for (objlistp=objlistv; objlistp->name; objlistp++)
if (slbt_objlist_read(fdcwd,objlistp) < 0)
return -1;
for (objc=0,objlistp=objlistv; objlistp->name; objlistp++)
objc += objlistp->objc;
/* allocate split vectors, account for cargv's added sysroot */
if ((sargv->targv = calloc(objc + 2*(argc+3),sizeof(char *))))
sargv->cargv = sargv->targv + argc + 2;
else
return -1;
/* --features and no <compiler>? */
if (ctx.unitidx) {
(void)0;
} else if (help || version || features || info || config || dumpmachine || altmode) {
for (i=0; i<argc; i++)
sargv->targv[i] = argv[i];
sargv->cargv = altmode ? sargv->targv : slbt_default_cargv;
return 0;
}
/* --mode=ar and no ar-specific arguments? */
if (aropt && !ctx.unitidx)
ctx.unitidx = argc;
/* split vectors: slibtool's own options */
for (i=0; i<ctx.unitidx; i++)
sargv->targv[i] = argv[i];
/* split vector marks */
targv = sargv->targv + i;
cargv = sargv->cargv;
/* known wrappers */
if (ctx.unitidx && !ccwrap && !aropt) {
if ((base = strrchr(argv[i],'/')))
base++;
else if ((base = strrchr(argv[i],'\\')))
base++;
else
base = argv[i];
if (!strcmp(base,"ccache")
|| !strcmp(base,"distcc")
|| !strcmp(base,"compiler")
|| !strcmp(base,"purify")) {
*targv++ = "--ccwrap";
*targv++ = argv[i++];
}
}
/* split vectors: legacy mixture */
for (optout=optv; optout[0] && (optout[0]->tag != TAG_OUTPUT); optout++)
(void)0;
/* compiler, archiver, etc. */
if (altmode) {
i = 0;
} else if (aropt) {
*cargv++ = argv[0];
} else {
*cargv++ = argv[i++];
}
/* sysroot */
if (csysroot)
*cargv++ = csysroot;
/* remaining vector */
for (objlistp=objlistv; i<argc; i++) {
if (aropt && (i >= ctx.unitidx)) {
*cargv++ = argv[i];
} else if (argv[i][0] != '-') {
if (argv[i+1] && (argv[i+1][0] == '+')
&& (argv[i+1][1] == '=')
&& (argv[i+1][2] == 0)
&& !(strrchr(argv[i],'.')))
/* libfoo_la_LDFLAGS += -Wl,.... */
i++;
else
*cargv++ = argv[i];
/* must capture -objectlist prior to -o */
} else if (!(strcmp("objectlist",&argv[i][1]))) {
for (objp=objlistp->objv; *objp; objp++)
*cargv++ = *objp;
i++;
objlistp++;
} else if (argv[i][1] == 'o') {
*targv++ = argv[i];
if (argv[i][2] == 0)
*targv++ = argv[++i];
} else if ((argv[i][1] == 'W') && (argv[i][2] == 'c')) {
*cargv++ = argv[i];
} else if (!(strcmp("Xcompiler",&argv[i][1]))) {
*cargv++ = argv[++i];
} else if (!(strcmp("XCClinker",&argv[i][1]))) {
*cargv++ = argv[++i];
} else if ((argv[i][1] == 'R') && (argv[i][2] == 0)) {
*targv++ = argv[i++];
*targv++ = argv[i];
} else if (argv[i][1] == 'R') {
*targv++ = argv[i];
} else if (!(strncmp("-target=",&argv[i][1],8))) {
*cargv++ = argv[i];
*targv++ = argv[i];
} else if (!(strcmp("-target",&argv[i][1]))) {
*cargv++ = argv[i];
*targv++ = argv[i++];
*cargv++ = argv[i];
*targv++ = argv[i];
} else if (!(strcmp("target",&argv[i][1]))) {
*cargv++ = argv[i];
*targv++ = argv[i++];
*cargv++ = argv[i];
*targv++ = argv[i];
} else if (!(strncmp("-sysroot=",&argv[i][1],9))) {
*cargv++ = argv[i];
*targv++ = argv[i];
} else if (!(strcmp("-sysroot",&argv[i][1]))) {
*cargv++ = argv[i];
*targv++ = argv[i++];
*cargv++ = argv[i];
*targv++ = argv[i];
} else if (!(strcmp("bindir",&argv[i][1]))) {
*targv++ = argv[i++];
*targv++ = argv[i];
} else if (!(strcmp("shrext",&argv[i][1]))) {
*targv++ = argv[i++];
*targv++ = argv[i];
} else if (!(strcmp("rpath",&argv[i][1]))) {
*targv++ = argv[i++];
*targv++ = argv[i];
} else if (!(strcmp("release",&argv[i][1]))) {
*targv++ = argv[i++];
*targv++ = argv[i];
} else if (!(strcmp("dlopen",&argv[i][1]))) {
*targv++ = argv[i++];
*targv++ = argv[i];
} else if (!(strcmp("weak",&argv[i][1]))) {
*targv++ = argv[i++];
*targv++ = argv[i];
} else if (!(strcmp("static-libtool-libs",&argv[i][1]))) {
*targv++ = argv[i];
} else if (!(strcmp("export-dynamic",&argv[i][1]))) {
*targv++ = argv[i];
} else if (!(strcmp("export-symbols",&argv[i][1]))) {
*targv++ = argv[i++];
*targv++ = argv[i];
} else if (!(strcmp("export-symbols-regex",&argv[i][1]))) {
*targv++ = argv[i++];
*targv++ = argv[i];
} else if (!(strcmp("version-info",&argv[i][1]))) {
*targv++ = argv[i++];
*targv++ = argv[i];
} else if (!(strcmp("version-number",&argv[i][1]))) {
*targv++ = argv[i++];
*targv++ = argv[i];
} else if (!(strcmp("dlpreopen",&argv[i][1]))) {
(void)0;
} else {
for (popt=optout; popt[0] && popt[0]->long_name; popt++)
if (!(strcmp(popt[0]->long_name,&argv[i][1])))
break;
if (popt[0] && popt[0]->long_name)
*targv++ = argv[i];
else
*cargv++ = argv[i];
}
}
return 0;
}