|
|
0801fe |
/*******************************************************************/
|
|
|
eac61a |
/* slibtool: a strong libtool implementation, written in C */
|
|
|
49181b |
/* Copyright (C) 2016--2024 SysDeer Technologies, LLC */
|
|
|
0801fe |
/* Released under the Standard MIT License; see COPYING.SLIBTOOL. */
|
|
|
0801fe |
/*******************************************************************/
|
|
|
0801fe |
|
|
|
20aece |
#include <ctype.h>
|
|
|
0801fe |
#include <fcntl.h>
|
|
|
0801fe |
#include <stdio.h>
|
|
|
0801fe |
#include <stdint.h>
|
|
|
0801fe |
#include <stdbool.h>
|
|
|
0801fe |
#include <unistd.h>
|
|
|
0801fe |
#include <sys/mman.h>
|
|
|
0801fe |
#include <sys/stat.h>
|
|
|
0801fe |
|
|
|
0801fe |
#include "slibtool_lconf_impl.h"
|
|
|
0801fe |
#include "slibtool_driver_impl.h"
|
|
|
0801fe |
#include "slibtool_errinfo_impl.h"
|
|
|
0801fe |
#include "slibtool_symlink_impl.h"
|
|
|
0801fe |
#include "slibtool_readlink_impl.h"
|
|
|
d4b2a5 |
#include "slibtool_realpath_impl.h"
|
|
|
4b56de |
#include "slibtool_visibility_impl.h"
|
|
|
0801fe |
|
|
|
0801fe |
enum slbt_lconf_opt {
|
|
|
0801fe |
SLBT_LCONF_OPT_UNKNOWN,
|
|
|
0801fe |
SLBT_LCONF_OPT_NO,
|
|
|
0801fe |
SLBT_LCONF_OPT_YES,
|
|
|
0801fe |
};
|
|
|
0801fe |
|
|
|
c58607 |
static const char aclr_reset[] = "\x1b[0m";
|
|
|
c58607 |
static const char aclr_bold[] = "\x1b[1m";
|
|
|
c58607 |
|
|
|
c58607 |
static const char aclr_red[] = "\x1b[31m";
|
|
|
c58607 |
static const char aclr_green[] = "\x1b[32m";
|
|
|
c58607 |
static const char aclr_yellow[] = "\x1b[33m";
|
|
|
c58607 |
static const char aclr_blue[] = "\x1b[34m";
|
|
|
c58607 |
static const char aclr_magenta[] = "\x1b[35m";
|
|
|
c58607 |
|
|
|
0801fe |
static void slbt_lconf_close(int fdcwd, int fdlconfdir)
|
|
|
0801fe |
{
|
|
|
0801fe |
if (fdlconfdir != fdcwd)
|
|
|
0801fe |
close(fdlconfdir);
|
|
|
0801fe |
}
|
|
|
0801fe |
|
|
|
c58607 |
static int slbt_lconf_trace_lconf_plain(
|
|
|
c58607 |
struct slbt_driver_ctx * dctx,
|
|
|
c58607 |
const char * lconf)
|
|
|
c58607 |
{
|
|
|
c58607 |
int fderr = slbt_driver_fderr(dctx);
|
|
|
c58607 |
|
|
|
c58607 |
if (slbt_dprintf(
|
|
|
c58607 |
fderr,
|
|
|
c58607 |
"%s: %s: {.name=%c%s%c}.\n",
|
|
|
c58607 |
dctx->program,
|
|
|
c58607 |
"lconf",
|
|
|
c58607 |
'"',lconf,'"') < 0)
|
|
|
c58607 |
return -1;
|
|
|
c58607 |
|
|
|
c58607 |
return 0;
|
|
|
c58607 |
}
|
|
|
c58607 |
|
|
|
c58607 |
static int slbt_lconf_trace_lconf_annotated(
|
|
|
c58607 |
struct slbt_driver_ctx * dctx,
|
|
|
c58607 |
const char * lconf)
|
|
|
c58607 |
{
|
|
|
c58607 |
int fderr = slbt_driver_fderr(dctx);
|
|
|
c58607 |
|
|
|
c58607 |
if (slbt_dprintf(
|
|
|
c58607 |
fderr,
|
|
|
c58607 |
"%s%s%s%s: %s%s%s: {.name=%s%s%c%s%c%s}.\n",
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,aclr_magenta,
|
|
|
c58607 |
dctx->program,
|
|
|
c58607 |
aclr_reset,
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,
|
|
|
c58607 |
"lconf",
|
|
|
c58607 |
aclr_reset,
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,aclr_green,
|
|
|
c58607 |
'"',lconf,'"',
|
|
|
c58607 |
aclr_reset) < 0)
|
|
|
c58607 |
return -1;
|
|
|
c58607 |
|
|
|
c58607 |
return 0;
|
|
|
c58607 |
}
|
|
|
c58607 |
|
|
|
c58607 |
static int slbt_lconf_trace_openat_silent(
|
|
|
c58607 |
struct slbt_driver_ctx * dctx,
|
|
|
c58607 |
int fdat,
|
|
|
c58607 |
const char * path,
|
|
|
c58607 |
int oflag,
|
|
|
c58607 |
int mode)
|
|
|
c58607 |
{
|
|
|
c58607 |
(void)dctx;
|
|
|
c58607 |
return openat(fdat,path,oflag,mode);
|
|
|
c58607 |
}
|
|
|
c58607 |
|
|
|
c58607 |
static int slbt_lconf_trace_openat_plain(
|
|
|
c58607 |
struct slbt_driver_ctx * dctx,
|
|
|
c58607 |
int fdat,
|
|
|
c58607 |
const char * path,
|
|
|
c58607 |
int oflag,
|
|
|
c58607 |
int mode)
|
|
|
c58607 |
{
|
|
|
c58607 |
char scwd[20];
|
|
|
c58607 |
char serr[512];
|
|
|
c58607 |
|
|
|
c58607 |
int ret = openat(fdat,path,oflag,mode);
|
|
|
c58607 |
int fderr = slbt_driver_fderr(dctx);
|
|
|
c58607 |
|
|
|
c58607 |
if (fdat == AT_FDCWD) {
|
|
|
c58607 |
strcpy(scwd,"AT_FDCWD");
|
|
|
c58607 |
} else {
|
|
|
c58607 |
sprintf(scwd,"%d",fdat);
|
|
|
c58607 |
}
|
|
|
c58607 |
|
|
|
c58607 |
if ((ret < 0) && (errno == ENOENT)) {
|
|
|
c58607 |
strcpy(serr," [ENOENT]");
|
|
|
c58607 |
} else if (ret < 0) {
|
|
|
c58607 |
memset(serr,0,sizeof(serr));
|
|
|
c58607 |
strerror_r(errno,&serr[2],sizeof(serr)-4);
|
|
|
c58607 |
serr[0] = ' ';
|
|
|
c58607 |
serr[1] = '(';
|
|
|
c58607 |
serr[strlen(serr)] = ')';
|
|
|
c58607 |
} else {
|
|
|
c58607 |
serr[0] = 0;
|
|
|
c58607 |
}
|
|
|
c58607 |
|
|
|
c58607 |
slbt_dprintf(
|
|
|
c58607 |
fderr,
|
|
|
c58607 |
"%s: %s: openat(%s,%c%s%c,%s,%d) = %d%s.\n",
|
|
|
c58607 |
dctx->program,
|
|
|
c58607 |
"lconf",
|
|
|
c58607 |
scwd,
|
|
|
c58607 |
'"',path,'"',
|
|
|
c58607 |
(oflag == O_DIRECTORY) ? "O_DIRECTORY" : "O_RDONLY",
|
|
|
c58607 |
mode,ret,serr);
|
|
|
c58607 |
|
|
|
c58607 |
return ret;
|
|
|
c58607 |
}
|
|
|
c58607 |
|
|
|
c58607 |
static int slbt_lconf_trace_openat_annotated(
|
|
|
c58607 |
struct slbt_driver_ctx * dctx,
|
|
|
c58607 |
int fdat,
|
|
|
c58607 |
const char * path,
|
|
|
c58607 |
int oflag,
|
|
|
c58607 |
int mode)
|
|
|
c58607 |
{
|
|
|
c58607 |
char scwd[20];
|
|
|
c58607 |
char serr[512];
|
|
|
c58607 |
|
|
|
c58607 |
int ret = openat(fdat,path,oflag,mode);
|
|
|
c58607 |
int fderr = slbt_driver_fderr(dctx);
|
|
|
c58607 |
|
|
|
c58607 |
if (fdat == AT_FDCWD) {
|
|
|
c58607 |
strcpy(scwd,"AT_FDCWD");
|
|
|
c58607 |
} else {
|
|
|
c58607 |
sprintf(scwd,"%d",fdat);
|
|
|
c58607 |
}
|
|
|
c58607 |
|
|
|
c58607 |
if ((ret < 0) && (errno == ENOENT)) {
|
|
|
c58607 |
strcpy(serr," [ENOENT]");
|
|
|
c58607 |
} else if (ret < 0) {
|
|
|
c58607 |
memset(serr,0,sizeof(serr));
|
|
|
c58607 |
strerror_r(errno,&serr[2],sizeof(serr)-4);
|
|
|
c58607 |
serr[0] = ' ';
|
|
|
c58607 |
serr[1] = '(';
|
|
|
c58607 |
serr[strlen(serr)] = ')';
|
|
|
c58607 |
} else {
|
|
|
c58607 |
serr[0] = 0;
|
|
|
c58607 |
}
|
|
|
c58607 |
|
|
|
c58607 |
slbt_dprintf(
|
|
|
c58607 |
fderr,
|
|
|
c58607 |
"%s%s%s%s: %s%s%s: openat(%s%s%s%s,%s%s%c%s%c%s,%s%s%s%s,%d) = %s%d%s%s%s%s%s.\n",
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,aclr_magenta,
|
|
|
c58607 |
dctx->program,
|
|
|
c58607 |
aclr_reset,
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,
|
|
|
c58607 |
"lconf",
|
|
|
c58607 |
aclr_reset,
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,aclr_blue,
|
|
|
c58607 |
scwd,
|
|
|
c58607 |
aclr_reset,
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,aclr_green,
|
|
|
c58607 |
'"',path,'"',
|
|
|
c58607 |
aclr_reset,
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,aclr_blue,
|
|
|
c58607 |
(oflag == O_DIRECTORY) ? "O_DIRECTORY" : "O_RDONLY",
|
|
|
c58607 |
aclr_reset,
|
|
|
c58607 |
|
|
|
c58607 |
mode,
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,
|
|
|
c58607 |
ret,
|
|
|
c58607 |
aclr_reset,
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,aclr_red,
|
|
|
c58607 |
serr,
|
|
|
c58607 |
aclr_reset);
|
|
|
c58607 |
|
|
|
c58607 |
return ret;
|
|
|
c58607 |
}
|
|
|
c58607 |
|
|
|
c58607 |
static int slbt_lconf_trace_fstat_silent(
|
|
|
c58607 |
struct slbt_driver_ctx * dctx,
|
|
|
c58607 |
int fd,
|
|
|
c58607 |
const char * path,
|
|
|
c58607 |
struct stat * st)
|
|
|
c58607 |
{
|
|
|
c58607 |
(void)dctx;
|
|
|
c58607 |
|
|
|
c58607 |
return path ? fstatat(fd,path,st,0) : fstat(fd,st);
|
|
|
c58607 |
}
|
|
|
c58607 |
|
|
|
c58607 |
static int slbt_lconf_trace_fstat_plain(
|
|
|
c58607 |
struct slbt_driver_ctx * dctx,
|
|
|
c58607 |
int fd,
|
|
|
c58607 |
const char * path,
|
|
|
c58607 |
struct stat * st)
|
|
|
c58607 |
{
|
|
|
c58607 |
char scwd[20];
|
|
|
c58607 |
char serr[512];
|
|
|
c58607 |
char quot[2] = {'"',0};
|
|
|
c58607 |
|
|
|
c58607 |
int ret = path ? fstatat(fd,path,st,0) : fstat(fd,st);
|
|
|
c58607 |
int fderr = slbt_driver_fderr(dctx);
|
|
|
c58607 |
|
|
|
c58607 |
if (fd == AT_FDCWD) {
|
|
|
c58607 |
strcpy(scwd,"AT_FDCWD");
|
|
|
c58607 |
} else {
|
|
|
c58607 |
sprintf(scwd,"%d",fd);
|
|
|
c58607 |
}
|
|
|
c58607 |
|
|
|
c58607 |
if ((ret < 0) && (errno == ENOENT)) {
|
|
|
c58607 |
strcpy(serr," [ENOENT]");
|
|
|
c58607 |
} else if (ret < 0) {
|
|
|
c58607 |
memset(serr,0,sizeof(serr));
|
|
|
c58607 |
strerror_r(errno,&serr[2],sizeof(serr)-4);
|
|
|
c58607 |
serr[0] = ' ';
|
|
|
c58607 |
serr[1] = '(';
|
|
|
c58607 |
serr[strlen(serr)] = ')';
|
|
|
c58607 |
} else {
|
|
|
c58607 |
serr[0] = 0;
|
|
|
c58607 |
}
|
|
|
c58607 |
|
|
|
c58607 |
slbt_dprintf(
|
|
|
c58607 |
fderr,
|
|
|
c58607 |
"%s: %s: %s(%s%s%s%s%s,...) = %d%s%s",
|
|
|
c58607 |
dctx->program,
|
|
|
c58607 |
"lconf",
|
|
|
c58607 |
path ? "fstatat" : "fstat",
|
|
|
c58607 |
scwd,
|
|
|
c58607 |
path ? "," : "",
|
|
|
c58607 |
path ? quot : "",
|
|
|
c58607 |
path ? path : "",
|
|
|
c58607 |
path ? quot : "",
|
|
|
c58607 |
ret,
|
|
|
c58607 |
serr,
|
|
|
c58607 |
ret ? ".\n" : "");
|
|
|
c58607 |
|
|
|
c58607 |
if (ret == 0)
|
|
|
c58607 |
slbt_dprintf(
|
|
|
c58607 |
fderr,
|
|
|
c58607 |
" {.st_dev = %ld, .st_ino = %ld}.\n",
|
|
|
c58607 |
st->st_dev,
|
|
|
c58607 |
st->st_ino);
|
|
|
c58607 |
|
|
|
c58607 |
return ret;
|
|
|
c58607 |
}
|
|
|
c58607 |
|
|
|
c58607 |
static int slbt_lconf_trace_fstat_annotated(
|
|
|
c58607 |
struct slbt_driver_ctx * dctx,
|
|
|
c58607 |
int fd,
|
|
|
c58607 |
const char * path,
|
|
|
c58607 |
struct stat * st)
|
|
|
c58607 |
{
|
|
|
c58607 |
char scwd[20];
|
|
|
c58607 |
char serr[512];
|
|
|
c58607 |
char quot[2] = {'"',0};
|
|
|
c58607 |
|
|
|
c58607 |
int ret = path ? fstatat(fd,path,st,0) : fstat(fd,st);
|
|
|
c58607 |
int fderr = slbt_driver_fderr(dctx);
|
|
|
c58607 |
|
|
|
c58607 |
if (fd == AT_FDCWD) {
|
|
|
c58607 |
strcpy(scwd,"AT_FDCWD");
|
|
|
c58607 |
} else {
|
|
|
c58607 |
sprintf(scwd,"%d",fd);
|
|
|
c58607 |
}
|
|
|
c58607 |
|
|
|
c58607 |
if ((ret < 0) && (errno == ENOENT)) {
|
|
|
c58607 |
strcpy(serr," [ENOENT]");
|
|
|
c58607 |
} else if (ret < 0) {
|
|
|
c58607 |
memset(serr,0,sizeof(serr));
|
|
|
c58607 |
strerror_r(errno,&serr[2],sizeof(serr)-4);
|
|
|
c58607 |
serr[0] = ' ';
|
|
|
c58607 |
serr[1] = '(';
|
|
|
c58607 |
serr[strlen(serr)] = ')';
|
|
|
c58607 |
} else {
|
|
|
c58607 |
serr[0] = 0;
|
|
|
c58607 |
}
|
|
|
c58607 |
|
|
|
c58607 |
slbt_dprintf(
|
|
|
c58607 |
fderr,
|
|
|
c58607 |
"%s%s%s%s: %s%s%s: %s(%s%s%s%s%s%s%s%s%s%s%s,...) = %s%d%s%s%s%s%s%s",
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,aclr_magenta,
|
|
|
c58607 |
dctx->program,
|
|
|
c58607 |
aclr_reset,
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,
|
|
|
c58607 |
"lconf",
|
|
|
c58607 |
aclr_reset,
|
|
|
c58607 |
|
|
|
c58607 |
path ? "fstatat" : "fstat",
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,aclr_blue,
|
|
|
c58607 |
scwd,
|
|
|
c58607 |
aclr_reset,
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,aclr_green,
|
|
|
c58607 |
path ? "," : "",
|
|
|
c58607 |
path ? quot : "",
|
|
|
c58607 |
path ? path : "",
|
|
|
c58607 |
path ? quot : "",
|
|
|
c58607 |
aclr_reset,
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,
|
|
|
c58607 |
ret,
|
|
|
c58607 |
aclr_reset,
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,aclr_red,
|
|
|
c58607 |
serr,
|
|
|
c58607 |
aclr_reset,
|
|
|
c58607 |
|
|
|
c58607 |
ret ? ".\n" : "");
|
|
|
c58607 |
|
|
|
c58607 |
if (ret == 0)
|
|
|
c58607 |
slbt_dprintf(
|
|
|
c58607 |
fderr,
|
|
|
c58607 |
" {%s%s.st_dev%s = %s%ld%s, %s%s.st_ino%s = %s%ld%s}.\n",
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,aclr_yellow,aclr_reset,
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,
|
|
|
c58607 |
st->st_dev,
|
|
|
c58607 |
aclr_reset,
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,aclr_yellow,aclr_reset,
|
|
|
c58607 |
|
|
|
c58607 |
aclr_bold,
|
|
|
c58607 |
st->st_ino,
|
|
|
c58607 |
aclr_reset);
|
|
|
c58607 |
|
|
|
c58607 |
return ret;
|
|
|
c58607 |
}
|
|
|
c58607 |
|
|
|
9c11fa |
static int slbt_lconf_trace_result_silent(
|
|
|
9c11fa |
struct slbt_driver_ctx * dctx,
|
|
|
9c11fa |
int fd,
|
|
|
9c11fa |
int fdat,
|
|
|
9c11fa |
const char * lconf,
|
|
|
a2aa78 |
int err,
|
|
|
a2aa78 |
char (*pathbuf)[PATH_MAX])
|
|
|
9c11fa |
{
|
|
|
9c11fa |
(void)dctx;
|
|
|
9c11fa |
(void)fd;
|
|
|
9c11fa |
(void)fdat;
|
|
|
9c11fa |
(void)lconf;
|
|
|
a2aa78 |
|
|
|
a2aa78 |
if (err)
|
|
|
a2aa78 |
return -1;
|
|
|
a2aa78 |
|
|
|
a2aa78 |
if (slbt_realpath(fdat,lconf,0,*pathbuf,sizeof(*pathbuf)) <0)
|
|
|
a2aa78 |
return -1;
|
|
|
a2aa78 |
|
|
|
a2aa78 |
return fd;
|
|
|
9c11fa |
}
|
|
|
9c11fa |
|
|
|
9c11fa |
static int slbt_lconf_trace_result_plain(
|
|
|
9c11fa |
struct slbt_driver_ctx * dctx,
|
|
|
9c11fa |
int fd,
|
|
|
9c11fa |
int fdat,
|
|
|
9c11fa |
const char * lconf,
|
|
|
a2aa78 |
int err,
|
|
|
a2aa78 |
char (*pathbuf)[PATH_MAX])
|
|
|
9c11fa |
{
|
|
|
9c11fa |
int fderr;
|
|
|
9c11fa |
const char * cpath;
|
|
|
9c11fa |
|
|
|
9c11fa |
fderr = slbt_driver_fderr(dctx);
|
|
|
9c11fa |
|
|
|
a2aa78 |
cpath = !(slbt_realpath(fdat,lconf,0,*pathbuf,sizeof(*pathbuf)))
|
|
|
a2aa78 |
? *pathbuf : lconf;
|
|
|
9c11fa |
|
|
|
9c11fa |
switch (err) {
|
|
|
9c11fa |
case 0:
|
|
|
9c11fa |
slbt_dprintf(
|
|
|
9c11fa |
fderr,
|
|
|
9c11fa |
"%s: %s: found %c%s%c.\n",
|
|
|
9c11fa |
dctx->program,
|
|
|
9c11fa |
"lconf",
|
|
|
9c11fa |
'"',cpath,'"');
|
|
|
9c11fa |
return fd;
|
|
|
9c11fa |
|
|
|
9c11fa |
case EXDEV:
|
|
|
9c11fa |
slbt_dprintf(
|
|
|
9c11fa |
fderr,
|
|
|
9c11fa |
"%s: %s: stopped in %c%s%c "
|
|
|
9c11fa |
"(config file not found on current device).\n",
|
|
|
9c11fa |
dctx->program,
|
|
|
9c11fa |
"lconf",
|
|
|
9c11fa |
'"',cpath,'"');
|
|
|
9c11fa |
return -1;
|
|
|
9c11fa |
|
|
|
9c11fa |
default:
|
|
|
9c11fa |
slbt_dprintf(
|
|
|
9c11fa |
fderr,
|
|
|
9c11fa |
"%s: %s: stopped in %c%s%c "
|
|
|
9c11fa |
"(top-level directory reached).\n",
|
|
|
9c11fa |
dctx->program,
|
|
|
9c11fa |
"lconf",
|
|
|
9c11fa |
'"',cpath,'"');
|
|
|
9c11fa |
return -1;
|
|
|
9c11fa |
}
|
|
|
9c11fa |
}
|
|
|
9c11fa |
|
|
|
9c11fa |
static int slbt_lconf_trace_result_annotated(
|
|
|
9c11fa |
struct slbt_driver_ctx * dctx,
|
|
|
9c11fa |
int fd,
|
|
|
9c11fa |
int fdat,
|
|
|
9c11fa |
const char * lconf,
|
|
|
a2aa78 |
int err,
|
|
|
a2aa78 |
char (*pathbuf)[PATH_MAX])
|
|
|
9c11fa |
{
|
|
|
9c11fa |
int fderr;
|
|
|
9c11fa |
const char * cpath;
|
|
|
9c11fa |
|
|
|
9c11fa |
fderr = slbt_driver_fderr(dctx);
|
|
|
9c11fa |
|
|
|
a2aa78 |
cpath = !(slbt_realpath(fdat,lconf,0,*pathbuf,sizeof(*pathbuf)))
|
|
|
a2aa78 |
? *pathbuf : lconf;
|
|
|
9c11fa |
|
|
|
9c11fa |
switch (err) {
|
|
|
9c11fa |
case 0:
|
|
|
9c11fa |
slbt_dprintf(
|
|
|
9c11fa |
fderr,
|
|
|
9c11fa |
"%s%s%s%s: %s%s%s: found %s%s%c%s%c%s.\n",
|
|
|
9c11fa |
|
|
|
9c11fa |
aclr_bold,aclr_magenta,
|
|
|
9c11fa |
dctx->program,
|
|
|
9c11fa |
aclr_reset,
|
|
|
9c11fa |
|
|
|
9c11fa |
aclr_bold,
|
|
|
9c11fa |
"lconf",
|
|
|
9c11fa |
aclr_reset,
|
|
|
9c11fa |
|
|
|
9c11fa |
aclr_bold,aclr_green,
|
|
|
9c11fa |
'"',cpath,'"',
|
|
|
9c11fa |
aclr_reset);
|
|
|
9c11fa |
return fd;
|
|
|
9c11fa |
|
|
|
9c11fa |
case EXDEV:
|
|
|
9c11fa |
slbt_dprintf(
|
|
|
9c11fa |
fderr,
|
|
|
9c11fa |
"%s%s%s%s: %s%s%s: stopped in %s%s%c%s%c%s "
|
|
|
9c11fa |
"%s%s(config file not found on current device)%s.\n",
|
|
|
9c11fa |
|
|
|
9c11fa |
aclr_bold,aclr_magenta,
|
|
|
9c11fa |
dctx->program,
|
|
|
9c11fa |
aclr_reset,
|
|
|
9c11fa |
|
|
|
9c11fa |
aclr_bold,
|
|
|
9c11fa |
"lconf",
|
|
|
9c11fa |
aclr_reset,
|
|
|
9c11fa |
|
|
|
9c11fa |
aclr_bold,aclr_green,
|
|
|
9c11fa |
'"',cpath,'"',
|
|
|
9c11fa |
aclr_reset,
|
|
|
9c11fa |
|
|
|
9c11fa |
aclr_bold,aclr_red,
|
|
|
9c11fa |
aclr_reset);
|
|
|
9c11fa |
return -1;
|
|
|
9c11fa |
|
|
|
9c11fa |
default:
|
|
|
9c11fa |
slbt_dprintf(
|
|
|
9c11fa |
fderr,
|
|
|
9c11fa |
"%s%s%s%s: %s%s%s: stopped in %s%s%c%s%c%s "
|
|
|
9c11fa |
"%s%s(top-level directory reached)%s.\n",
|
|
|
9c11fa |
|
|
|
9c11fa |
aclr_bold,aclr_magenta,
|
|
|
9c11fa |
dctx->program,
|
|
|
9c11fa |
aclr_reset,
|
|
|
9c11fa |
|
|
|
9c11fa |
aclr_bold,
|
|
|
9c11fa |
"lconf",
|
|
|
9c11fa |
aclr_reset,
|
|
|
9c11fa |
|
|
|
9c11fa |
aclr_bold,aclr_green,
|
|
|
9c11fa |
'"',cpath,'"',
|
|
|
9c11fa |
aclr_reset,
|
|
|
9c11fa |
|
|
|
9c11fa |
aclr_bold,aclr_red,
|
|
|
9c11fa |
aclr_reset);
|
|
|
9c11fa |
return -1;
|
|
|
9c11fa |
}
|
|
|
9c11fa |
}
|
|
|
9c11fa |
|
|
|
0801fe |
static int slbt_lconf_open(
|
|
|
0801fe |
struct slbt_driver_ctx * dctx,
|
|
|
a2aa78 |
const char * lconf,
|
|
|
a2aa78 |
char (*lconfpath)[PATH_MAX])
|
|
|
0801fe |
{
|
|
|
c58607 |
int fderr;
|
|
|
0801fe |
int fdcwd;
|
|
|
0801fe |
int fdlconf;
|
|
|
0801fe |
int fdlconfdir;
|
|
|
0801fe |
int fdparent;
|
|
|
0801fe |
struct stat stcwd;
|
|
|
0801fe |
struct stat stparent;
|
|
|
0b404d |
ino_t stinode;
|
|
|
0801fe |
|
|
|
c58607 |
int (*trace_lconf)(struct slbt_driver_ctx *,
|
|
|
c58607 |
const char *);
|
|
|
c58607 |
|
|
|
c58607 |
int (*trace_fstat)(struct slbt_driver_ctx *,
|
|
|
c58607 |
int,const char *, struct stat *);
|
|
|
c58607 |
|
|
|
c58607 |
int (*trace_openat)(struct slbt_driver_ctx *,
|
|
|
c58607 |
int,const char *,int,int);
|
|
|
c58607 |
|
|
|
9c11fa |
int (*trace_result)(struct slbt_driver_ctx *,
|
|
|
a2aa78 |
int,int,const char *,int,
|
|
|
a2aa78 |
char (*)[PATH_MAX]);
|
|
|
9c11fa |
|
|
|
c58607 |
lconf = lconf ? lconf : "libtool";
|
|
|
c58607 |
fderr = slbt_driver_fderr(dctx);
|
|
|
0801fe |
fdcwd = slbt_driver_fdcwd(dctx);
|
|
|
0801fe |
fdlconfdir = fdcwd;
|
|
|
0801fe |
|
|
|
c58607 |
if (dctx->cctx->drvflags & SLBT_DRIVER_SILENT) {
|
|
|
c58607 |
trace_lconf = 0;
|
|
|
c58607 |
trace_fstat = slbt_lconf_trace_fstat_silent;
|
|
|
c58607 |
trace_openat = slbt_lconf_trace_openat_silent;
|
|
|
9c11fa |
trace_result = slbt_lconf_trace_result_silent;
|
|
|
c58607 |
|
|
|
c58607 |
} else if (dctx->cctx->drvflags & SLBT_DRIVER_ANNOTATE_NEVER) {
|
|
|
c58607 |
trace_lconf = slbt_lconf_trace_lconf_plain;
|
|
|
c58607 |
trace_fstat = slbt_lconf_trace_fstat_plain;
|
|
|
c58607 |
trace_openat = slbt_lconf_trace_openat_plain;
|
|
|
9c11fa |
trace_result = slbt_lconf_trace_result_plain;
|
|
|
c58607 |
|
|
|
c58607 |
} else if (dctx->cctx->drvflags & SLBT_DRIVER_ANNOTATE_ALWAYS) {
|
|
|
c58607 |
trace_lconf = slbt_lconf_trace_lconf_annotated;
|
|
|
c58607 |
trace_fstat = slbt_lconf_trace_fstat_annotated;
|
|
|
c58607 |
trace_openat = slbt_lconf_trace_openat_annotated;
|
|
|
9c11fa |
trace_result = slbt_lconf_trace_result_annotated;
|
|
|
c58607 |
|
|
|
c58607 |
} else if (isatty(fderr)) {
|
|
|
c58607 |
trace_lconf = slbt_lconf_trace_lconf_annotated;
|
|
|
c58607 |
trace_fstat = slbt_lconf_trace_fstat_annotated;
|
|
|
c58607 |
trace_openat = slbt_lconf_trace_openat_annotated;
|
|
|
9c11fa |
trace_result = slbt_lconf_trace_result_annotated;
|
|
|
c58607 |
|
|
|
c58607 |
} else {
|
|
|
c58607 |
trace_lconf = slbt_lconf_trace_lconf_plain;
|
|
|
c58607 |
trace_fstat = slbt_lconf_trace_fstat_plain;
|
|
|
c58607 |
trace_openat = slbt_lconf_trace_openat_plain;
|
|
|
9c11fa |
trace_result = slbt_lconf_trace_result_plain;
|
|
|
c58607 |
}
|
|
|
c58607 |
|
|
|
503f2f |
if (!(dctx->cctx->drvflags & SLBT_DRIVER_DEBUG)) {
|
|
|
503f2f |
trace_fstat = slbt_lconf_trace_fstat_silent;
|
|
|
503f2f |
trace_openat = slbt_lconf_trace_openat_silent;
|
|
|
503f2f |
}
|
|
|
503f2f |
|
|
|
c58607 |
if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT)) {
|
|
|
c58607 |
trace_lconf(dctx,lconf);
|
|
|
c58607 |
slbt_output_fdcwd(dctx);
|
|
|
c58607 |
}
|
|
|
c58607 |
|
|
|
d70e37 |
if (lconf && strchr(lconf,'/'))
|
|
|
c58607 |
return ((fdlconf = trace_openat(dctx,fdcwd,lconf,O_RDONLY,0)) < 0)
|
|
|
0801fe |
? SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_LCONF_OPEN)
|
|
|
a2aa78 |
: trace_result(dctx,fdlconf,fdcwd,lconf,0,lconfpath);
|
|
|
0801fe |
|
|
|
c58607 |
if (trace_fstat(dctx,fdlconfdir,".",&stcwd) < 0)
|
|
|
6beda1 |
return SLBT_SYSTEM_ERROR(dctx,0);
|
|
|
0801fe |
|
|
|
0b404d |
stinode = stcwd.st_ino;
|
|
|
c58607 |
fdlconf = trace_openat(dctx,fdlconfdir,lconf,O_RDONLY,0);
|
|
|
0801fe |
|
|
|
0801fe |
while (fdlconf < 0) {
|
|
|
c58607 |
fdparent = trace_openat(dctx,fdlconfdir,"../",O_DIRECTORY,0);
|
|
|
0801fe |
slbt_lconf_close(fdcwd,fdlconfdir);
|
|
|
0801fe |
|
|
|
0801fe |
if (fdparent < 0)
|
|
|
6beda1 |
return SLBT_SYSTEM_ERROR(dctx,0);
|
|
|
0801fe |
|
|
|
c58607 |
if (trace_fstat(dctx,fdparent,0,&stparent) < 0) {
|
|
|
bab3d0 |
close(fdparent);
|
|
|
6beda1 |
return SLBT_SYSTEM_ERROR(dctx,0);
|
|
|
bab3d0 |
}
|
|
|
0801fe |
|
|
|
bab3d0 |
if (stparent.st_dev != stcwd.st_dev) {
|
|
|
a2aa78 |
trace_result(dctx,fdparent,fdparent,".",EXDEV,lconfpath);
|
|
|
bab3d0 |
close(fdparent);
|
|
|
0801fe |
return SLBT_CUSTOM_ERROR(
|
|
|
0801fe |
dctx,SLBT_ERR_LCONF_OPEN);
|
|
|
bab3d0 |
}
|
|
|
0801fe |
|
|
|
0b404d |
if (stparent.st_ino == stinode) {
|
|
|
a2aa78 |
trace_result(dctx,fdparent,fdparent,".",ELOOP,lconfpath);
|
|
|
0b404d |
close(fdparent);
|
|
|
0b404d |
return SLBT_CUSTOM_ERROR(
|
|
|
0b404d |
dctx,SLBT_ERR_LCONF_OPEN);
|
|
|
0b404d |
}
|
|
|
0b404d |
|
|
|
0801fe |
fdlconfdir = fdparent;
|
|
|
c58607 |
fdlconf = trace_openat(dctx,fdlconfdir,lconf,O_RDONLY,0);
|
|
|
0b404d |
stinode = stparent.st_ino;
|
|
|
0801fe |
}
|
|
|
0801fe |
|
|
|
a2aa78 |
trace_result(dctx,fdlconf,fdlconfdir,lconf,0,lconfpath);
|
|
|
9c11fa |
|
|
|
bab3d0 |
slbt_lconf_close(fdcwd,fdlconfdir);
|
|
|
bab3d0 |
|
|
|
0801fe |
return fdlconf;
|
|
|
0801fe |
}
|
|
|
0801fe |
|
|
|
20aece |
static int slbt_get_lconf_var(
|
|
|
489101 |
const struct slbt_txtfile_ctx * tctx,
|
|
|
489101 |
const char * var,
|
|
|
489101 |
const char space,
|
|
|
489101 |
char (*val)[PATH_MAX])
|
|
|
20aece |
{
|
|
|
489101 |
const char ** pline;
|
|
|
20aece |
const char * mark;
|
|
|
20aece |
const char * match;
|
|
|
489101 |
const char * cap;
|
|
|
20aece |
ssize_t len;
|
|
|
bb9df1 |
int cint;
|
|
|
20aece |
|
|
|
20aece |
/* init */
|
|
|
20aece |
match = 0;
|
|
|
489101 |
pline = tctx->txtlinev;
|
|
|
489101 |
len = strlen(var);
|
|
|
20aece |
|
|
|
20aece |
/* search for ^var= */
|
|
|
489101 |
for (; *pline && !match; ) {
|
|
|
489101 |
if (!strncmp(*pline,var,len)) {
|
|
|
489101 |
match = *pline;
|
|
|
20aece |
} else {
|
|
|
489101 |
pline++;
|
|
|
20aece |
}
|
|
|
20aece |
}
|
|
|
20aece |
|
|
|
20aece |
/* not found? */
|
|
|
00c58e |
if (!match) {
|
|
|
00c58e |
(*val)[0] = '\0';
|
|
|
20aece |
return 0;
|
|
|
00c58e |
}
|
|
|
20aece |
|
|
|
d29f9c |
/* support a single pair of double quotes */
|
|
|
20aece |
match = &match[len];
|
|
|
20aece |
mark = match;
|
|
|
20aece |
|
|
|
d29f9c |
if (match[0] == '"') {
|
|
|
d29f9c |
match++;
|
|
|
20aece |
mark++;
|
|
|
20aece |
|
|
|
489101 |
for (; *mark && (*mark != '"'); )
|
|
|
d29f9c |
mark++;
|
|
|
489101 |
|
|
|
489101 |
/* unpaired quote? */
|
|
|
489101 |
if (*mark != '"')
|
|
|
489101 |
return -1;
|
|
|
d29f9c |
} else {
|
|
|
489101 |
for (; *mark && !isspace((cint=*mark)); )
|
|
|
d29f9c |
mark++;
|
|
|
d29f9c |
}
|
|
|
d29f9c |
|
|
|
489101 |
cap = mark;
|
|
|
d29f9c |
|
|
|
20aece |
/* validate */
|
|
|
489101 |
for (mark=match; mark
|
|
|
20aece |
if ((*mark >= 'a') && (*mark <= 'z'))
|
|
|
20aece |
(void)0;
|
|
|
20aece |
|
|
|
20aece |
else if ((*mark >= 'A') && (*mark <= 'Z'))
|
|
|
20aece |
(void)0;
|
|
|
20aece |
|
|
|
20aece |
else if ((*mark >= '0') && (*mark <= '9'))
|
|
|
20aece |
(void)0;
|
|
|
20aece |
|
|
|
20aece |
else if ((*mark == '+') || (*mark == '-'))
|
|
|
20aece |
(void)0;
|
|
|
20aece |
|
|
|
20aece |
else if ((*mark == '/') || (*mark == '@'))
|
|
|
20aece |
(void)0;
|
|
|
20aece |
|
|
|
20aece |
else if ((*mark == '.') || (*mark == '_'))
|
|
|
20aece |
(void)0;
|
|
|
20aece |
|
|
|
145d2b |
else if ((*mark == ':') || (*mark == space))
|
|
|
d89592 |
(void)0;
|
|
|
d89592 |
|
|
|
20aece |
else
|
|
|
20aece |
return -1;
|
|
|
20aece |
}
|
|
|
20aece |
|
|
|
489101 |
/* all done */
|
|
|
489101 |
memcpy(*val,match,cap-match);
|
|
|
489101 |
(*val)[cap-match] = '\0';
|
|
|
489101 |
|
|
|
20aece |
return 0;
|
|
|
20aece |
}
|
|
|
20aece |
|
|
|
4b56de |
slbt_hidden int slbt_get_lconf_flags(
|
|
|
0801fe |
struct slbt_driver_ctx * dctx,
|
|
|
0801fe |
const char * lconf,
|
|
|
0801fe |
uint64_t * flags)
|
|
|
0801fe |
{
|
|
|
d29f9c |
struct slbt_driver_ctx_impl * ctx;
|
|
|
489101 |
struct slbt_txtfile_ctx * confctx;
|
|
|
0801fe |
int fdlconf;
|
|
|
0801fe |
struct stat st;
|
|
|
0801fe |
void * addr;
|
|
|
0801fe |
uint64_t optshared;
|
|
|
0801fe |
uint64_t optstatic;
|
|
|
20aece |
char val[PATH_MAX];
|
|
|
0801fe |
|
|
|
d29f9c |
/* driver context (ar, ranlib, cc) */
|
|
|
d29f9c |
ctx = slbt_get_driver_ictx(dctx);
|
|
|
d29f9c |
|
|
|
0801fe |
/* open relative libtool script */
|
|
|
a2aa78 |
if ((fdlconf = slbt_lconf_open(dctx,lconf,&val)) < 0)
|
|
|
a2aa78 |
return SLBT_NESTED_ERROR(dctx);
|
|
|
a2aa78 |
|
|
|
a2aa78 |
/* cache the configuration in library friendly form) */
|
|
|
a2aa78 |
if (slbt_lib_get_txtfile_ctx(dctx,val,&ctx->lconfctx) < 0)
|
|
|
0801fe |
return SLBT_NESTED_ERROR(dctx);
|
|
|
0801fe |
|
|
|
489101 |
confctx = ctx->lconfctx;
|
|
|
489101 |
|
|
|
0801fe |
/* map relative libtool script */
|
|
|
0801fe |
if (fstat(fdlconf,&st) < 0)
|
|
|
6beda1 |
return SLBT_SYSTEM_ERROR(dctx,0);
|
|
|
0801fe |
|
|
|
0801fe |
addr = mmap(
|
|
|
0801fe |
0,st.st_size,
|
|
|
0801fe |
PROT_READ,MAP_SHARED,
|
|
|
0801fe |
fdlconf,0);
|
|
|
0801fe |
|
|
|
0801fe |
close(fdlconf);
|
|
|
0801fe |
|
|
|
0801fe |
if (addr == MAP_FAILED)
|
|
|
0801fe |
return SLBT_CUSTOM_ERROR(
|
|
|
0801fe |
dctx,SLBT_ERR_LCONF_MAP);
|
|
|
0801fe |
|
|
|
0801fe |
/* scan */
|
|
|
0801fe |
optshared = 0;
|
|
|
0801fe |
optstatic = 0;
|
|
|
0801fe |
|
|
|
20aece |
/* shared libraries option */
|
|
|
489101 |
if (slbt_get_lconf_var(confctx,"build_libtool_libs=",0,&val) < 0)
|
|
|
20aece |
return SLBT_CUSTOM_ERROR(
|
|
|
20aece |
dctx,SLBT_ERR_LCONF_PARSE);
|
|
|
20aece |
|
|
|
20aece |
if (!strcmp(val,"yes")) {
|
|
|
20aece |
optshared = SLBT_DRIVER_SHARED;
|
|
|
20aece |
|
|
|
20aece |
} else if (!strcmp(val,"no")) {
|
|
|
20aece |
optshared = SLBT_DRIVER_DISABLE_SHARED;
|
|
|
20aece |
}
|
|
|
20aece |
|
|
|
20aece |
|
|
|
20aece |
/* static libraries option */
|
|
|
489101 |
if (slbt_get_lconf_var(confctx,"build_old_libs=",0,&val) < 0)
|
|
|
20aece |
return SLBT_CUSTOM_ERROR(
|
|
|
20aece |
dctx,SLBT_ERR_LCONF_PARSE);
|
|
|
20aece |
|
|
|
20aece |
if (!strcmp(val,"yes")) {
|
|
|
20aece |
optstatic = SLBT_DRIVER_STATIC;
|
|
|
32fd48 |
|
|
|
20aece |
} else if (!strcmp(val,"no")) {
|
|
|
20aece |
optstatic = SLBT_DRIVER_DISABLE_STATIC;
|
|
|
0801fe |
}
|
|
|
0801fe |
|
|
|
0801fe |
if (!optshared || !optstatic)
|
|
|
0801fe |
return SLBT_CUSTOM_ERROR(
|
|
|
0801fe |
dctx,SLBT_ERR_LCONF_PARSE);
|
|
|
0801fe |
|
|
|
0801fe |
*flags = optshared | optstatic;
|
|
|
0801fe |
|
|
|
d29f9c |
|
|
|
771899 |
/* host */
|
|
|
771899 |
if (!ctx->cctx.host.host) {
|
|
|
489101 |
if (slbt_get_lconf_var(confctx,"host=",0,&val) < 0)
|
|
|
771899 |
return SLBT_CUSTOM_ERROR(
|
|
|
771899 |
dctx,SLBT_ERR_LCONF_PARSE);
|
|
|
771899 |
|
|
|
771899 |
if (val[0] && !(ctx->host.host = strdup(val)))
|
|
|
771899 |
return SLBT_SYSTEM_ERROR(dctx,0);
|
|
|
771899 |
|
|
|
771899 |
ctx->cctx.host.host = ctx->host.host;
|
|
|
771899 |
}
|
|
|
771899 |
|
|
|
771899 |
|
|
|
d29f9c |
/* ar tool */
|
|
|
d29f9c |
if (!ctx->cctx.host.ar) {
|
|
|
489101 |
if (slbt_get_lconf_var(confctx,"AR=",0x20,&val) < 0)
|
|
|
d29f9c |
return SLBT_CUSTOM_ERROR(
|
|
|
d29f9c |
dctx,SLBT_ERR_LCONF_PARSE);
|
|
|
d29f9c |
|
|
|
4964fd |
if (val[0] && !(ctx->host.ar = strdup(val)))
|
|
|
d29f9c |
return SLBT_SYSTEM_ERROR(dctx,0);
|
|
|
4964fd |
|
|
|
4964fd |
ctx->cctx.host.ar = ctx->host.ar;
|
|
|
b5e104 |
}
|
|
|
b5e104 |
|
|
|
b5e104 |
|
|
|
b5e104 |
/* nm tool */
|
|
|
b5e104 |
if (!ctx->cctx.host.nm) {
|
|
|
b5e104 |
if (slbt_get_lconf_var(confctx,"NM=",0x20,&val) < 0)
|
|
|
b5e104 |
return SLBT_CUSTOM_ERROR(
|
|
|
b5e104 |
dctx,SLBT_ERR_LCONF_PARSE);
|
|
|
b5e104 |
|
|
|
b5e104 |
if (val[0] && !(ctx->host.nm = strdup(val)))
|
|
|
b5e104 |
return SLBT_SYSTEM_ERROR(dctx,0);
|
|
|
b5e104 |
|
|
|
b5e104 |
ctx->cctx.host.nm = ctx->host.nm;
|
|
|
d29f9c |
}
|
|
|
d29f9c |
|
|
|
d29f9c |
|
|
|
d29f9c |
/* ranlib tool */
|
|
|
d29f9c |
if (!ctx->cctx.host.ranlib) {
|
|
|
489101 |
if (slbt_get_lconf_var(confctx,"RANLIB=",0x20,&val) < 0)
|
|
|
d29f9c |
return SLBT_CUSTOM_ERROR(
|
|
|
d29f9c |
dctx,SLBT_ERR_LCONF_PARSE);
|
|
|
d29f9c |
|
|
|
4964fd |
if (val[0] && !(ctx->host.ranlib = strdup(val)))
|
|
|
d29f9c |
return SLBT_SYSTEM_ERROR(dctx,0);
|
|
|
4964fd |
|
|
|
4964fd |
ctx->cctx.host.ranlib = ctx->host.ranlib;
|
|
|
d29f9c |
}
|
|
|
d29f9c |
|
|
|
d29f9c |
|
|
|
b4058c |
/* as tool (optional) */
|
|
|
b4058c |
if (!ctx->cctx.host.as) {
|
|
|
489101 |
if (slbt_get_lconf_var(confctx,"AS=",0x20,&val) < 0)
|
|
|
ba9228 |
return SLBT_CUSTOM_ERROR(
|
|
|
ba9228 |
dctx,SLBT_ERR_LCONF_PARSE);
|
|
|
b4058c |
|
|
|
ba9228 |
if (val[0] && !(ctx->host.as = strdup(val)))
|
|
|
ba9228 |
return SLBT_SYSTEM_ERROR(dctx,0);
|
|
|
ba9228 |
|
|
|
ba9228 |
|
|
|
ba9228 |
ctx->cctx.host.as = ctx->host.as;
|
|
|
b4058c |
}
|
|
|
b4058c |
|
|
|
b4058c |
|
|
|
fc8ee9 |
/* dlltool tool (optional) */
|
|
|
fc8ee9 |
if (!ctx->cctx.host.dlltool) {
|
|
|
489101 |
if (slbt_get_lconf_var(confctx,"DLLTOOL=",0x20,&val) < 0)
|
|
|
94868f |
return SLBT_CUSTOM_ERROR(
|
|
|
94868f |
dctx,SLBT_ERR_LCONF_PARSE);
|
|
|
fc8ee9 |
|
|
|
94868f |
if (val[0] && !(ctx->host.dlltool = strdup(val)))
|
|
|
94868f |
return SLBT_SYSTEM_ERROR(dctx,0);
|
|
|
94868f |
|
|
|
94868f |
ctx->cctx.host.dlltool = ctx->host.dlltool;
|
|
|
fc8ee9 |
}
|
|
|
fc8ee9 |
|
|
|
fc8ee9 |
|
|
|
d29f9c |
/* all done */
|
|
|
6ab3f1 |
ctx->lconf.addr = addr;
|
|
|
6ab3f1 |
ctx->lconf.size = st.st_size;
|
|
|
d29f9c |
|
|
|
0801fe |
return 0;
|
|
|
0801fe |
}
|