summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/perk/perk.h12
-rw-r--r--include/perk/perk_output.h7
-rw-r--r--project/common.mk2
-rw-r--r--project/headers.mk1
-rw-r--r--project/tree.mk1
-rw-r--r--src/arbits/output/pe_ar_list_members.c388
-rw-r--r--src/arbits/output/pe_ar_print_members.c181
-rw-r--r--src/cmds/pe_cmd_ar.c120
-rw-r--r--src/driver/pe_amain.c22
-rw-r--r--src/driver/pe_driver_ctx.c112
-rw-r--r--src/internal/perk_ar_impl.h16
-rw-r--r--src/internal/perk_driver_impl.h3
-rw-r--r--src/internal/perk_synopsis_impl.h46
-rw-r--r--src/output/pe_output_error.c9
-rw-r--r--src/skin/pe_skin_ar.c20
15 files changed, 897 insertions, 43 deletions
diff --git a/include/perk/perk.h b/include/perk/perk.h
index a6d6f94..85f7ad0 100644
--- a/include/perk/perk.h
+++ b/include/perk/perk.h
@@ -102,9 +102,17 @@ enum pe_custom_error {
PERK_ERR_AR_DLUNIT_NOT_SPECIFIED,
PERK_ERR_AR_OUTPUT_NOT_SPECIFIED,
PERK_ERR_AR_OUTPUT_NOT_APPLICABLE,
+ PERK_ERR_AR_NON_ARCHIVE_IMAGE,
PERK_ERR_AR_NON_PE_MEMBERS,
PERK_ERR_AR_MIXED_PE_MEMBERS,
PERK_ERR_AR_NESTED_ARCHIVE,
+ PERK_ERR_AR_MISSING_ACTION,
+ PERK_ERR_AR_MULTIPLE_ACTIONS,
+ PERK_ERR_AR_MULTIPLE_ANCHORS,
+ PERK_ERR_AR_INVALID_ANCHORS,
+ PERK_ERR_AR_MISSING_ANCHOR,
+ PERK_ERR_AR_NULL_POSNAME,
+ PERK_ERR_AR_NULL_ARNAME,
PERK_ERR_CAP,
};
@@ -352,6 +360,10 @@ perk_api int pe_ar_get_archive_meta (const struct pe_driver_ctx *,
perk_api void pe_ar_free_archive_meta (struct pe_archive_meta *);
+/* archiver utility api */
+perk_api int pe_ar_list_members (const struct pe_archive_meta *, const char **);
+perk_api int pe_ar_print_members (const struct pe_archive_meta *, const char **);
+
/* package info */
perk_api const struct pe_source_version * pe_source_version(void);
diff --git a/include/perk/perk_output.h b/include/perk/perk_output.h
index d6e2529..313f609 100644
--- a/include/perk/perk_output.h
+++ b/include/perk/perk_output.h
@@ -35,7 +35,12 @@
#define PERK_PRETTY_TABLE PERK_PRETTY(0x00000002)
#define PERK_PRETTY_READOBJ PERK_PRETTY(0x00000004)
#define PERK_PRETTY_OBJDUMP PERK_PRETTY(0x00000008)
+
#define PERK_PRETTY_YAML PERK_PRETTY(0x00000010)
-#define PERK_PRETTY_DLLTOOL PERK_PRETTY(0x00000020)
+#define PERK_PRETTY_POSIX PERK_PRETTY(0x00000020)
+#define PERK_PRETTY_HEXDATA PERK_PRETTY(0x00000040)
+#define PERK_PRETTY_VERBOSE PERK_PRETTY(0x00000080)
+
+#define PERK_PRETTY_DLLTOOL PERK_PRETTY(0x10000000)
#endif
diff --git a/project/common.mk b/project/common.mk
index 4f95df4..3fb1971 100644
--- a/project/common.mk
+++ b/project/common.mk
@@ -4,6 +4,8 @@ API_SRCS = \
src/arbits/pe_armap_bsd_64.c \
src/arbits/pe_armap_sysv_32.c \
src/arbits/pe_armap_sysv_64.c \
+ src/arbits/output/pe_ar_list_members.c \
+ src/arbits/output/pe_ar_print_members.c \
src/cmds/pe_cmd_ar.c \
src/cmds/pe_cmd_perk.c \
src/driver/pe_amain.c \
diff --git a/project/headers.mk b/project/headers.mk
index fc1f132..e87271b 100644
--- a/project/headers.mk
+++ b/project/headers.mk
@@ -16,6 +16,7 @@ INTERNAL_HEADERS = \
$(SOURCE_DIR)/src/internal/$(PACKAGE)_errinfo_impl.h \
$(SOURCE_DIR)/src/internal/$(PACKAGE)_hexdump_impl.h \
$(SOURCE_DIR)/src/internal/$(PACKAGE)_reader_impl.h \
+ $(SOURCE_DIR)/src/internal/$(PACKAGE)_synopsis_impl.h \
$(SOURCE_DIR)/src/internal/$(PACKAGE)_visibility_impl.h \
ALL_HEADERS = $(API_HEADERS) $(INTERNAL_HEADERS)
diff --git a/project/tree.mk b/project/tree.mk
index 0fa72e3..63d4c97 100644
--- a/project/tree.mk
+++ b/project/tree.mk
@@ -1,6 +1,7 @@
tree.tag:
mkdir -p src
mkdir -p src/arbits
+ mkdir -p src/arbits/output
mkdir -p src/cmds
mkdir -p src/driver
mkdir -p src/hexdump
diff --git a/src/arbits/output/pe_ar_list_members.c b/src/arbits/output/pe_ar_list_members.c
new file mode 100644
index 0000000..d02129c
--- /dev/null
+++ b/src/arbits/output/pe_ar_list_members.c
@@ -0,0 +1,388 @@
+/***************************************************************/
+/* perk: PE Resource Kit */
+/* Copyright (C) 2015--2025 SysDeer Technologies, LLC */
+/* Released under GPLv2 and GPLv3; see COPYING.PERK. */
+/***************************************************************/
+
+#include <time.h>
+#include <locale.h>
+#include <inttypes.h>
+#include <perk/perk.h>
+#include <perk/perk_output.h>
+#include "perk_driver_impl.h"
+#include "perk_dprintf_impl.h"
+#include "perk_errinfo_impl.h"
+#include "perk_ar_impl.h"
+
+#define PERK_PRETTY_FLAGS (PERK_PRETTY_YAML \
+ | PERK_PRETTY_POSIX \
+ | PERK_PRETTY_HEXDATA)
+
+#define PPRIU64 "%"PRIu64
+
+const char pe_ar_perm_strs[8][4] = {
+ {'-','-','-','\0'},
+ {'-','-','x','\0'},
+ {'-','w','-','\0'},
+ {'-','w','x','\0'},
+ {'r','-','-','\0'},
+ {'r','-','x','\0'},
+ {'r','w','-','\0'},
+ {'r','w','x','\0'}
+};
+
+static unsigned pe_ar_output_decimal_len_from_val(size_t val, unsigned min)
+{
+ unsigned ret;
+
+ for (ret=0; val; ret++)
+ val /= 10;
+
+ return (ret > min) ? ret : min;
+}
+
+static void pe_ar_member_not_found(
+ const struct pe_archive_meta * meta,
+ const char * name)
+{
+ const struct pe_driver_ctx * dctx;
+ int fderr;
+
+ dctx = (pe_archive_meta_ictx(meta))->dctx;
+
+ if ((fderr = pe_driver_fderr(dctx)) >= 0)
+ pe_dprintf(
+ fderr,
+ "%s: error: member '%s' was not found in the current archive\n",
+ dctx->program,name);
+}
+
+static struct ar_meta_member_info * pe_ar_get_member_info(
+ const struct pe_archive_meta * meta,
+ const char * name)
+{
+ struct ar_meta_member_info ** memberp;
+ const char * slash;
+
+ if ((slash = strrchr(name,'/')))
+ name = ++slash;
+
+ for (memberp=meta->a_memberv; *memberp; memberp++)
+ if (!strcmp(memberp[0]->ar_file_header.ar_member_name,name))
+ return *memberp;
+
+ pe_ar_member_not_found(meta,name);
+
+ return 0;
+}
+
+static int pe_ar_member_in_members(
+ struct ar_meta_member_info * member,
+ const char ** members)
+{
+ const char ** pname;
+
+ for (pname=members; *pname; pname++)
+ if (!strcmp(*pname,member->ar_file_header.ar_member_name))
+ return 1;
+
+ return 0;
+}
+
+static int pe_ar_list_one_member_posix(
+ int fdout,
+ struct ar_meta_member_info * memberp)
+{
+ return pe_dprintf(
+ fdout,"%s\n",
+ memberp->ar_file_header.ar_member_name);
+}
+
+static int pe_ar_list_one_member_posix_verbose(
+ int fdout,
+ struct ar_meta_member_info * memberp,
+ const char * fmtstr,
+ locale_t arlocale)
+{
+ unsigned ownerbits;
+ unsigned groupbits;
+ unsigned worldbits;
+ time_t artimeval;
+ struct tm artimeloc;
+ char artimestr[64] = {0};
+
+ ownerbits = (memberp->ar_file_header.ar_file_mode & 0700) >> 6;
+ groupbits = (memberp->ar_file_header.ar_file_mode & 0070) >> 3;
+ worldbits = (memberp->ar_file_header.ar_file_mode & 0007);
+ artimeval = memberp->ar_file_header.ar_time_date_stamp;
+
+ if (localtime_r(&artimeval,&artimeloc))
+ strftime_l(
+ artimestr,sizeof(artimestr),
+ "%b %e %H:%M %Y",&artimeloc,
+ arlocale);
+
+ return pe_dprintf(
+ fdout,fmtstr,
+ pe_ar_perm_strs[ownerbits],
+ pe_ar_perm_strs[groupbits],
+ pe_ar_perm_strs[worldbits],
+ memberp->ar_file_header.ar_uid,
+ memberp->ar_file_header.ar_gid,
+ memberp->ar_object_size,
+ artimestr,
+ memberp->ar_file_header.ar_member_name);
+}
+
+static int pe_ar_list_members_posix(
+ const struct pe_driver_ctx * dctx,
+ const struct pe_archive_meta * meta,
+ const struct pe_fd_ctx * fdctx,
+ const char ** members)
+{
+ struct ar_meta_member_info ** memberp;
+ struct ar_meta_member_info * minfo;
+ const char ** pname;
+ int fdout;
+ size_t testval;
+ size_t sizelen;
+ size_t uidlen;
+ size_t gidlen;
+ locale_t arloc;
+ char fmtstr[64];
+
+ fdout = fdctx->fdout;
+ arloc = 0;
+
+ if (dctx->cctx->fmtflags & PERK_PRETTY_VERBOSE) {
+ for (sizelen=0,memberp=meta->a_memberv; *memberp; memberp++)
+ if ((testval = memberp[0]->ar_object_size) > sizelen)
+ if (!members || pe_ar_member_in_members(*memberp,members))
+ sizelen = testval;
+
+ for (uidlen=0,memberp=meta->a_memberv; *memberp; memberp++)
+ if ((testval = memberp[0]->ar_file_header.ar_uid) > uidlen)
+ if (!members || pe_ar_member_in_members(*memberp,members))
+ uidlen = testval;
+
+ for (gidlen=0,memberp=meta->a_memberv; *memberp; memberp++)
+ if ((testval = memberp[0]->ar_file_header.ar_gid) > gidlen)
+ if (!members || pe_ar_member_in_members(*memberp,members))
+ gidlen = testval;
+
+ sizelen = pe_ar_output_decimal_len_from_val(sizelen,6);
+ uidlen = pe_ar_output_decimal_len_from_val(uidlen,1);
+ gidlen = pe_ar_output_decimal_len_from_val(gidlen,1);
+ arloc = newlocale(LC_ALL,setlocale(LC_ALL,0),0);
+
+ sprintf(
+ fmtstr,
+ "%%s%%s%%s "
+ "%%" PPRIU64 "u"
+ "/%%-" PPRIU64 "u "
+ "%%" PPRIU64 "u "
+ "%%s "
+ "%%s\n",
+ uidlen,
+ gidlen,
+ sizelen);
+ }
+
+ for (pname=members; pname && *pname; pname++) {
+ if ((minfo = pe_ar_get_member_info(meta,*pname))) {
+ switch (minfo->ar_member_attr) {
+ case AR_MEMBER_ATTR_ARMAP:
+ case AR_MEMBER_ATTR_LINKINFO:
+ case AR_MEMBER_ATTR_NAMESTRS:
+ break;
+
+ default:
+ if (arloc) {
+ if (pe_ar_list_one_member_posix_verbose(
+ fdout,minfo,fmtstr,arloc) < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+ } else {
+ if (pe_ar_list_one_member_posix(
+ fdout,minfo) < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+ }
+ }
+ }
+ }
+
+ for (memberp=meta->a_memberv; !members && *memberp; memberp++) {
+ switch ((*memberp)->ar_member_attr) {
+ case AR_MEMBER_ATTR_ARMAP:
+ case AR_MEMBER_ATTR_LINKINFO:
+ case AR_MEMBER_ATTR_NAMESTRS:
+ break;
+
+ default:
+ if (arloc) {
+ if (pe_ar_list_one_member_posix_verbose(
+ fdout,*memberp,fmtstr,arloc) < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+ } else {
+ if (pe_ar_list_one_member_posix(
+ fdout,*memberp) < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+ }
+ }
+ }
+
+ if (arloc)
+ freelocale(arloc);
+
+ return 0;
+}
+
+static int pe_ar_list_one_member_yaml(
+ int fdout,
+ struct ar_meta_member_info * memberp)
+{
+ return pe_dprintf(
+ fdout,
+ " - [ member: %s ]\n",
+ memberp->ar_file_header.ar_member_name);
+}
+
+static int pe_ar_list_one_member_yaml_verbose(
+ int fdout,
+ struct ar_meta_member_info * memberp,
+ locale_t arlocale)
+{
+ time_t artimeval;
+ struct tm artimeloc;
+ char artimestr[64] = {0};
+
+ artimeval = memberp->ar_file_header.ar_time_date_stamp;
+
+ if (localtime_r(&artimeval,&artimeloc))
+ strftime_l(
+ artimestr,sizeof(artimestr),
+ "%Y/%m/%d @ %H:%M",&artimeloc,
+ arlocale);
+
+ return pe_dprintf(
+ fdout,
+ " - Member:\n"
+ " - [ name: " "%s" " ]\n"
+ " - [ timestamp: " "%s" " ]\n"
+ " - [ filesize: " PPRIU64 " ]\n"
+ " - [ uid: " "%d" " ]\n"
+ " - [ gid: " "%d" " ]\n"
+ " - [ mode: " "%d" " ]\n\n",
+ memberp->ar_file_header.ar_member_name,
+ artimestr,
+ memberp->ar_object_size,
+ memberp->ar_file_header.ar_uid,
+ memberp->ar_file_header.ar_gid,
+ memberp->ar_file_header.ar_file_mode);
+}
+
+static int pe_ar_list_members_yaml(
+ const struct pe_driver_ctx * dctx,
+ const struct pe_archive_meta * meta,
+ const struct pe_fd_ctx * fdctx,
+ const char ** members)
+{
+ struct ar_meta_member_info ** memberp;
+ struct ar_meta_member_info * minfo;
+ const char ** pname;
+ int fdout;
+ locale_t arloc;
+
+ fdout = fdctx->fdout;
+ arloc = 0;
+
+ if (dctx->cctx->fmtflags & PERK_PRETTY_VERBOSE) {
+ arloc = newlocale(LC_ALL,setlocale(LC_ALL,0),0);
+ }
+
+ if (pe_dprintf(fdctx->fdout," - Members:\n") < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+
+ for (pname=members; pname && *pname; pname++) {
+ if ((minfo = pe_ar_get_member_info(meta,*pname))) {
+ switch (minfo->ar_member_attr) {
+ case AR_MEMBER_ATTR_ARMAP:
+ case AR_MEMBER_ATTR_LINKINFO:
+ case AR_MEMBER_ATTR_NAMESTRS:
+ break;
+
+ default:
+ if (arloc) {
+ if (pe_ar_list_one_member_yaml_verbose(
+ fdout,minfo,arloc) < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+ } else {
+ if (pe_ar_list_one_member_yaml(
+ fdout,minfo) < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+ }
+ }
+ }
+ }
+
+ for (memberp=meta->a_memberv; !members && *memberp; memberp++) {
+ switch ((*memberp)->ar_member_attr) {
+ case AR_MEMBER_ATTR_ARMAP:
+ case AR_MEMBER_ATTR_LINKINFO:
+ case AR_MEMBER_ATTR_NAMESTRS:
+ break;
+
+ default:
+ if (arloc) {
+ if (pe_ar_list_one_member_yaml_verbose(
+ fdout,*memberp,arloc) < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+ } else {
+ if (pe_ar_list_one_member_yaml(
+ fdout,*memberp) < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+ }
+ }
+ }
+
+ if (arloc)
+ freelocale(arloc);
+
+ return 0;
+}
+
+int pe_ar_list_members(const struct pe_archive_meta * meta, const char ** members)
+{
+ const struct pe_driver_ctx * dctx;
+ struct pe_fd_ctx fdctx;
+
+ const char ** pmember;
+
+ dctx = (pe_archive_meta_ictx(meta))->dctx;
+
+ if (pe_lib_get_driver_fdctx(dctx,&fdctx) < 0)
+ return PERK_NESTED_ERROR(dctx);
+
+ if (members && !members[0])
+ members = 0;
+
+ if (!meta->a_memberv) {
+ for (pmember=members; pmember && *pmember; pmember++)
+ pe_ar_member_not_found(meta,*pmember);
+
+ return members ? 1 : 0;
+ }
+
+ switch (dctx->cctx->fmtflags & PERK_PRETTY_FLAGS) {
+ case PERK_PRETTY_YAML:
+ return pe_ar_list_members_yaml(
+ dctx,meta,&fdctx,members);
+
+ case PERK_PRETTY_POSIX:
+ return pe_ar_list_members_posix(
+ dctx,meta,&fdctx,members);
+
+ default:
+ return pe_ar_list_members_posix(
+ dctx,meta,&fdctx,members);
+ }
+}
diff --git a/src/arbits/output/pe_ar_print_members.c b/src/arbits/output/pe_ar_print_members.c
new file mode 100644
index 0000000..3bdf539
--- /dev/null
+++ b/src/arbits/output/pe_ar_print_members.c
@@ -0,0 +1,181 @@
+/***************************************************************/
+/* perk: PE Resource Kit */
+/* Copyright (C) 2015--2025 SysDeer Technologies, LLC */
+/* Released under GPLv2 and GPLv3; see COPYING.PERK. */
+/***************************************************************/
+
+#include <perk/perk.h>
+#include <perk/perk_output.h>
+#include "perk_driver_impl.h"
+#include "perk_dprintf_impl.h"
+#include "perk_errinfo_impl.h"
+#include "perk_ar_impl.h"
+
+static void pe_ar_member_not_found(
+ const struct pe_archive_meta * meta,
+ const char * name)
+{
+ const struct pe_driver_ctx * dctx;
+ int fderr;
+
+ dctx = (pe_archive_meta_ictx(meta))->dctx;
+
+ if ((fderr = pe_driver_fderr(dctx)) >= 0)
+ pe_dprintf(
+ fderr,
+ "%s: error: member '%s' was not found in the current archive\n",
+ dctx->program,name);
+}
+
+static struct ar_meta_member_info * pe_ar_get_member_info(
+ const struct pe_archive_meta * meta,
+ const char * name)
+{
+ struct ar_meta_member_info ** memberp;
+ const char * slash;
+
+ if ((slash = strrchr(name,'/')))
+ name = ++slash;
+
+ for (memberp=meta->a_memberv; *memberp; memberp++)
+ if (!strcmp(memberp[0]->ar_file_header.ar_member_name,name))
+ return *memberp;
+
+ pe_ar_member_not_found(meta,name);
+
+ return 0;
+}
+
+static int pe_ar_print_one_member_posix(
+ int fdout,
+ const struct pe_driver_ctx * dctx,
+ struct ar_meta_member_info * minfo)
+{
+ ssize_t ret;
+ size_t cnt;
+ const char * ch;
+
+ ret = 0;
+ cnt = minfo->ar_file_header.ar_file_size;
+ ch = minfo->ar_object_data;
+
+ for (; cnt && ret>=0; ) {
+ ch += ret;
+ cnt -= ret;
+
+ ret = write(fdout,ch,cnt);
+
+ while ((ret < 0) && (errno == EINTR))
+ ret = write(fdout,ch,cnt);
+ }
+
+ if (ret < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+
+ return 0;
+}
+
+static int pe_ar_print_one_member_posix_verbose(
+ int fdout,
+ const struct pe_driver_ctx * dctx,
+ struct ar_meta_member_info * minfo,
+ const char * aname)
+{
+ const char * name = aname ? aname : minfo->ar_file_header.ar_member_name;
+
+ if (pe_dprintf(fdout,"\n<%s>\n\n",name) < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+
+ if (pe_ar_print_one_member_posix(fdout,dctx,minfo) < 0)
+ return PERK_NESTED_ERROR(dctx);
+
+ return 0;
+}
+
+static int pe_ar_print_members_posix(
+ const struct pe_driver_ctx * dctx,
+ const struct pe_archive_meta * meta,
+ const struct pe_fd_ctx * fdctx,
+ const char ** members)
+{
+ struct ar_meta_member_info ** memberp;
+ struct ar_meta_member_info * minfo;
+ const char ** pname;
+ int fdout;
+ bool fname;
+
+ fdout = fdctx->fdout;
+ fname = dctx->cctx->fmtflags & PERK_PRETTY_VERBOSE;
+
+ for (pname=members; pname && *pname; pname++) {
+ if ((minfo = pe_ar_get_member_info(meta,*pname))) {
+ switch (minfo->ar_member_attr) {
+ case AR_MEMBER_ATTR_ARMAP:
+ case AR_MEMBER_ATTR_LINKINFO:
+ case AR_MEMBER_ATTR_NAMESTRS:
+ break;
+
+ default:
+ if (fname) {
+ if (pe_ar_print_one_member_posix_verbose(
+ fdout,dctx,minfo,*pname) < 0)
+ return PERK_NESTED_ERROR(dctx);
+ } else {
+ if (pe_ar_print_one_member_posix(
+ fdout,dctx,minfo) < 0)
+ return PERK_NESTED_ERROR(dctx);
+ }
+ }
+ }
+ }
+
+ for (memberp=meta->a_memberv; !members && *memberp; memberp++) {
+ switch ((*memberp)->ar_member_attr) {
+ case AR_MEMBER_ATTR_ARMAP:
+ case AR_MEMBER_ATTR_LINKINFO:
+ case AR_MEMBER_ATTR_NAMESTRS:
+ break;
+
+ default:
+ if (fname) {
+ if (pe_ar_print_one_member_posix_verbose(
+ fdout,dctx,*memberp,0) < 0)
+ return PERK_NESTED_ERROR(dctx);
+ } else {
+ if (pe_ar_print_one_member_posix(
+ fdout,dctx,*memberp) < 0)
+ return PERK_NESTED_ERROR(dctx);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int pe_ar_print_members(const struct pe_archive_meta * meta, const char ** members)
+{
+ const struct pe_driver_ctx * dctx;
+ struct pe_fd_ctx fdctx;
+
+ const char ** pmember;
+
+ dctx = (pe_archive_meta_ictx(meta))->dctx;
+
+ if (pe_lib_get_driver_fdctx(dctx,&fdctx) < 0)
+ return PERK_NESTED_ERROR(dctx);
+
+ if (members && !members[0])
+ members = 0;
+
+ if (!meta->a_memberv) {
+ for (pmember=members; pmember && *pmember; pmember++)
+ pe_ar_member_not_found(meta,*pmember);
+
+ return members ? 1 : 0;
+ }
+
+ if (pe_ar_print_members_posix(dctx,meta,&fdctx,members) < 0)
+ return PERK_NESTED_ERROR(dctx);
+
+ return 0;
+}
diff --git a/src/cmds/pe_cmd_ar.c b/src/cmds/pe_cmd_ar.c
index e578ca0..9d9423b 100644
--- a/src/cmds/pe_cmd_ar.c
+++ b/src/cmds/pe_cmd_ar.c
@@ -6,18 +6,126 @@
#include <perk/perk.h>
#include "perk_driver_impl.h"
+#include "perk_errinfo_impl.h"
+#include "perk_ar_impl.h"
+
+int pe_ar_list_members(const struct pe_archive_meta *, const char **);
+
+static int pe_cmd_ar_perform_unit_actions(
+ const struct pe_driver_ctx * dctx,
+ const char * arname,
+ const char ** members,
+ uint64_t action)
+{
+ struct pe_unit_ctx * arctx = 0;
+ int (*pe_ar_fn)(const struct pe_archive_meta *,const char **);
+
+ if (pe_lib_get_unit_ctx(dctx,arname,&arctx) < 0)
+ return PERK_NESTED_ERROR(dctx);
+
+ if (arctx->armeta == 0)
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_NON_ARCHIVE_IMAGE);
+
+ if (action == PERK_DRIVER_AR_LIST_MEMBERS) {
+ pe_ar_fn = pe_ar_list_members;
+
+ } else if (action == PERK_DRIVER_AR_PRINT_ARCHIVE) {
+ pe_ar_fn = pe_ar_print_members;
+ }
+
+ if (pe_ar_fn(arctx->armeta,members) < 0) {
+ pe_lib_free_unit_ctx(arctx);
+ return PERK_NESTED_ERROR(dctx);
+ }
+
+ pe_lib_free_unit_ctx(arctx);
+
+ return 0;
+}
+
+static int pe_cmd_ar_verify_cmdline(
+ const struct pe_driver_ctx * dctx,
+ uint64_t flags,
+ const char * posname,
+ const char * arname,
+ const char ** members)
+{
+ uint64_t action;
+ uint64_t poscmd;
+ uint64_t vercmd;
+
+ action = (flags & AR_ACTION_MASK);
+ poscmd = (flags & AR_POSNAME_MASK);
+ vercmd = (flags & PERK_DRIVER_VERSION);
+
+ if (vercmd && !posname && !arname && !members)
+ return 0;
+
+ switch (action) {
+ case 0:
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_MISSING_ACTION);
+
+ case PERK_DRIVER_AR_LIST_MEMBERS:
+ case PERK_DRIVER_AR_DELETE_MEMBERS:
+ case PERK_DRIVER_AR_APPEND_MEMBERS:
+ case PERK_DRIVER_AR_EXTRACT_MEMBERS:
+ case PERK_DRIVER_AR_PRINT_ARCHIVE:
+ if (poscmd || posname)
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_INVALID_ANCHORS);
+
+ break;
+
+ case AR_UPDATE_MASK:
+ case PERK_DRIVER_AR_REPLACE_MEMBERS:
+ case PERK_DRIVER_AR_MOVE_MEMBERS:
+ switch (poscmd) {
+ case 0:
+ if (posname)
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_MISSING_ANCHOR);
+ break;
+
+ case PERK_DRIVER_AR_POSITION_AFTER:
+ case PERK_DRIVER_AR_POSITION_BEFORE:
+ if (!posname)
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_NULL_POSNAME);
+ break;
+
+ default:
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_MULTIPLE_ANCHORS);
+ }
+
+ default:
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_MULTIPLE_ACTIONS);
+ }
+
+ if (!arname)
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_NULL_ARNAME);
+
+ return 0;
+}
int pe_cmd_ar(
const struct pe_driver_ctx * dctx,
uint64_t flags,
const char * posname,
const char * arname,
- const char ** units)
+ const char ** members)
{
- (void)dctx;
- (void)flags;
- (void)posname;
- (void)arname;
- (void)units;
+ uint64_t action = (flags & AR_ACTION_MASK);
+
+ if (pe_cmd_ar_verify_cmdline(dctx,flags,posname,arname,members) < 0)
+ return PERK_NESTED_ERROR(dctx);
+
+ if (pe_cmd_ar_perform_unit_actions(dctx,arname,members,action) < 0)
+ return PERK_NESTED_ERROR(dctx);
+
return 0;
}
diff --git a/src/driver/pe_amain.c b/src/driver/pe_amain.c
index fb68e30..a766dba 100644
--- a/src/driver/pe_amain.c
+++ b/src/driver/pe_amain.c
@@ -10,12 +10,11 @@
#include <perk/perk_output.h>
#include "perk_driver_impl.h"
#include "perk_dprintf_impl.h"
+#include "perk_ar_impl.h"
#ifndef PERK_DRIVER_FLAGS
#define PERK_DRIVER_FLAGS PERK_DRIVER_VERBOSITY_ERRORS \
| PERK_DRIVER_VERBOSITY_UTILITY \
- | PERK_DRIVER_AR_STRICT_PE \
- | PERK_DRIVER_AR_STRICT_PE_ARCH \
| PERK_DRIVER_AR_OBJECT_VECTOR
#endif
@@ -65,6 +64,10 @@ int pe_main(char ** argv, char ** envp, const struct pe_fd_ctx * fdctx)
uint64_t flags;
struct pe_driver_ctx * dctx;
const char ** unit;
+ const char * posname;
+ const char * arname;
+ const char ** arfiles;
+ uint64_t arflags;
flags = PERK_DRIVER_FLAGS;
fdout = fdctx ? fdctx->fdout : STDOUT_FILENO;
@@ -85,7 +88,20 @@ int pe_main(char ** argv, char ** envp, const struct pe_fd_ctx * fdctx)
break;
case PERK_CMD_AR:
- pe_cmd_ar(dctx,0,0,0,0);
+ arflags = dctx->cctx->drvflags;
+
+ if (arflags & AR_POSNAME_MASK) {
+ posname = dctx->units[0];
+ arname = posname ? dctx->units[1] : 0;
+ arfiles = arname ? &dctx->units[2] : 0;
+ } else {
+ posname = 0;
+ arname = dctx->units[0];
+ arfiles = arname ? &dctx->units[1] : 0;
+ }
+
+ pe_cmd_ar(dctx,arflags,posname,arname,arfiles);
+
break;
default:
diff --git a/src/driver/pe_driver_ctx.c b/src/driver/pe_driver_ctx.c
index a427252..fc00c06 100644
--- a/src/driver/pe_driver_ctx.c
+++ b/src/driver/pe_driver_ctx.c
@@ -14,6 +14,7 @@
#include <perk/perk_output.h>
#include "perk_version.h"
#include "perk_driver_impl.h"
+#include "perk_synopsis_impl.h"
#include "argv/argv.h"
/* package info */
@@ -76,30 +77,40 @@ static int pe_driver_usage(
const char * arg,
const struct argv_option ** optv,
struct argv_meta * meta,
+ const char * cmdarg,
enum pe_cmd cmd)
{
- char header[512];
- char * cmdarg[2];
- const char * cmdname;
-
- if (cmd == PERK_CMD_DEFAULT) {
- cmdarg[0] = "";
- cmdarg[1] = "";
- cmdname = "";
- } else {
- cmdarg[0] = " (--cmd=";
- cmdarg[1] = ")";
- cmdname = perk_cmd_name[cmd];
+ char header [2048];
+ char cmdname[128];
+
+ snprintf(cmdname,sizeof(cmdname),"%s%s%s",
+ program,
+ cmdarg ? " --cmd=" : "",
+ cmdarg ? perk_cmd_name[cmd] : "");
+
+ switch (cmd) {
+ case PERK_CMD_AR:
+ snprintf(header,sizeof(header),
+ PERK_AR_CMD_SYNOPSIS,
+ cmdname,cmdname,cmdname,cmdname,
+ cmdname,cmdname,cmdname,cmdname,
+ cmdname,cmdname,cmdname,cmdname,
+ cmdname,cmdname,cmdname);
+ break;
+
+ case PERK_CMD_PERK:
+ snprintf(header,sizeof(header),
+ PERK_PERK_CMD_SYNOPSIS,
+ cmdname,cmdname,cmdname);
+ break;
+
+ default:
+ snprintf(header,sizeof(header),
+ PERK_DEFAULT_CMD_SYNOPSIS,
+ program,program,program,program);
+ break;
}
- snprintf(header,sizeof(header),
- "Usage: %s [options] ...\n"
- "Usage: %s [options] [--cmd=<command>] <arg> <arg> ...\n\n"
- "Notes: --cmd must precede all non-option arguments, as well as\n"
- " all arguments that are specific to the selected command.\n\n"
- "Options%s%s%s:\n",
- program,program,cmdarg[0],cmdname,cmdarg[1]);
-
argv_usage(fdout,header,optv,arg);
argv_free(meta);
@@ -176,7 +187,8 @@ static int pe_cctx_update(
struct argv_meta * meta,
const struct pe_fd_ctx * fdctx,
struct pe_common_ctx * cctx,
- size_t * nunits)
+ size_t * nunits,
+ const char * cmdarg)
{
struct argv_entry * entry;
const char * pretty;
@@ -191,7 +203,7 @@ static int pe_cctx_update(
return pe_driver_usage(
fdctx->fdout,
program,entry->arg,
- optv,0,cctx->cmd);
+ optv,0,cmdarg,cctx->cmd);
break;
case TAG_CMD:
@@ -199,9 +211,10 @@ static int pe_cctx_update(
return pe_driver_usage(
fdctx->fderr,
program,0,
- optv,0,cctx->cmd);
+ optv,0,cmdarg,cctx->cmd);
cctx->cmd = pe_cmd_from_program(entry->arg);
+ cmdarg = entry->arg;
break;
case TAG_VERSION:
@@ -212,6 +225,10 @@ static int pe_cctx_update(
pretty = entry->arg;
break;
+ case TAG_VERBOSE:
+ cctx->fmtflags |= PERK_PRETTY_VERBOSE;
+ break;
+
case TAG_CATEGORY:
cctx->fmtflags |= PERK_OUTPUT_IMAGE_CATEGORY;
break;
@@ -284,6 +301,15 @@ static int pe_cctx_update(
cctx->hdrdump = PERK_HDRDUMP_IMPORT_TABLE;
}
break;
+
+ /*---ar---*/
+ case TAG_AR_LIST_MEMBERS:
+ cctx->drvflags |= PERK_DRIVER_AR_LIST_MEMBERS;
+ break;
+
+ case TAG_AR_PRINT_MEMBERS:
+ cctx->drvflags |= PERK_DRIVER_AR_PRINT_ARCHIVE;
+ break;
}
} else {
(*nunits)++;
@@ -293,6 +319,12 @@ static int pe_cctx_update(
if (pretty && !strcmp(pretty,"yaml")) {
cctx->fmtflags |= PERK_PRETTY_YAML;
+ } else if (pretty && !strcmp(pretty,"posix")) {
+ cctx->fmtflags |= PERK_PRETTY_POSIX;
+
+ } else if (pretty && !strcmp(pretty,"hexdata")) {
+ cctx->fmtflags |= PERK_PRETTY_HEXDATA;
+
} else if (pretty && !strcmp(pretty,"dlltool")) {
cctx->fmtflags |= PERK_PRETTY_DLLTOOL;
}
@@ -319,10 +351,12 @@ int pe_lib_get_driver_ctx(
struct argv_meta * meta;
size_t nunits;
const char * program;
+ const char * cmdarg;
char ** parg;
char ** pargcap;
char ** cmdargv;
char * cmdmark;
+ char stckarg[64];
struct argv_ctx actx = {ARGV_VERBOSITY_NONE,
ARGV_MODE_SCAN,
0,0,0,0,0,0,0,0};
@@ -341,6 +375,7 @@ int pe_lib_get_driver_ctx(
cctx.cmd = pe_cmd_from_program(program);
cctx.drvflags = flags;
nunits = 0;
+ cmdarg = 0;
/* missing arguments? */
argv_optv_init(perk_cmd_options[cctx.cmd],optv);
@@ -349,7 +384,18 @@ int pe_lib_get_driver_ctx(
return pe_driver_usage(
fdctx->fderr,
program,0,
- optv,0,cctx.cmd);
+ optv,0,0,cctx.cmd);
+
+ /* historic ar usage (vector will be cloned, so stack var is fine) */
+ if (cctx.cmd == PERK_CMD_AR) {
+ if (argv && argv[0] && argv[1] && (argv[1][0] != '-')) {
+ if (strlen(argv[1]) < (sizeof(stckarg) - 1)) {
+ stckarg[0] = '-';
+ strcpy(&stckarg[1],argv[1]);
+ argv[1] = stckarg;
+ }
+ }
+ }
/* initial argv scan: ... --cmd=xxx ... */
argv_scan(argv,optv,&actx,0);
@@ -370,9 +416,11 @@ int pe_lib_get_driver_ctx(
if (!strcmp(*parg,"--cmd") && parg[1]) {
cmdargv = &parg[2];
cmdmark = parg[2];
+ cmdarg = parg[1];
} else if (!strncmp(*parg,"--cmd=",6)) {
cmdargv = &parg[1];
cmdmark = parg[1];
+ cmdarg = &parg[0][6];
}
}
@@ -404,7 +452,8 @@ int pe_lib_get_driver_ctx(
if (pe_cctx_update(
program,optv,meta,
- fdctx,&cctx,&nunits)) {
+ fdctx,&cctx,&nunits,
+ cmdarg)) {
argv_free(meta);
return -1;
}
@@ -436,22 +485,19 @@ int pe_lib_get_driver_ctx(
if (pe_cctx_update(
program,optv,meta,
- fdctx,&cctx,&nunits)) {
+ fdctx,&cctx,&nunits,
+ cmdarg)) {
argv_free(meta);
return -1;
}
/* utility mode and no action to take? */
- if (cctx.cmd == PERK_CMD_AR) {
- (void)0;
-
- } else if (cctx.drvflags & PERK_DRIVER_VERBOSITY_UTILITY) {
+ if (cctx.drvflags & PERK_DRIVER_VERBOSITY_UTILITY)
if (!nunits && !(cctx.drvflags & PERK_DRIVER_VERSION))
return pe_driver_usage(
fdctx->fdout,
- program,0,
- optv,meta,cctx.cmd);
- }
+ program,0,optv,meta,
+ cmdarg,cctx.cmd);
/* context allocation */
if (!(ctx = pe_driver_ctx_alloc(meta,fdctx,&cctx,nunits)))
diff --git a/src/internal/perk_ar_impl.h b/src/internal/perk_ar_impl.h
index 543526d..b7c2fbc 100644
--- a/src/internal/perk_ar_impl.h
+++ b/src/internal/perk_ar_impl.h
@@ -20,6 +20,22 @@
/* initial number of elements in the transient, on-stack vector */
# define AR_STACK_VECTOR_ELEMENTS (0x200)
+/* bit mask to indicate that the first non-option argument is <posname> */
+#define AR_POSNAME_MASK (PERK_DRIVER_AR_POSITION_AFTER \
+ |PERK_DRIVER_AR_POSITION_BEFORE)
+
+#define AR_UPDATE_MASK (PERK_DRIVER_AR_UPDATE_MEMBERS \
+ |PERK_DRIVER_AR_REPLACE_MEMBERS)
+
+#define AR_ACTION_MASK (PERK_DRIVER_AR_LIST_MEMBERS \
+ |PERK_DRIVER_AR_MOVE_MEMBERS \
+ |PERK_DRIVER_AR_UPDATE_MEMBERS \
+ |PERK_DRIVER_AR_DELETE_MEMBERS \
+ |PERK_DRIVER_AR_APPEND_MEMBERS \
+ |PERK_DRIVER_AR_REPLACE_MEMBERS \
+ |PERK_DRIVER_AR_EXTRACT_MEMBERS \
+ |PERK_DRIVER_AR_PRINT_ARCHIVE)
+
extern const struct argv_option pe_ar_options[];
struct ar_armaps_impl {
diff --git a/src/internal/perk_driver_impl.h b/src/internal/perk_driver_impl.h
index 3948eae..22e7894 100644
--- a/src/internal/perk_driver_impl.h
+++ b/src/internal/perk_driver_impl.h
@@ -21,6 +21,7 @@ enum app_tags {
TAG_VERSION,
TAG_CMD,
TAG_PRETTY,
+ TAG_VERBOSE,
TAG_CATEGORY,
TAG_SECTIONS,
TAG_SYMBOLS,
@@ -31,6 +32,8 @@ enum app_tags {
TAG_DSOLIBS,
TAG_DSOSYMS,
TAG_HDRDUMP,
+ TAG_AR_LIST_MEMBERS,
+ TAG_AR_PRINT_MEMBERS,
};
struct pe_driver_ctx_impl {
diff --git a/src/internal/perk_synopsis_impl.h b/src/internal/perk_synopsis_impl.h
new file mode 100644
index 0000000..4f6959b
--- /dev/null
+++ b/src/internal/perk_synopsis_impl.h
@@ -0,0 +1,46 @@
+/***************************************************************/
+/* perk: PE Resource Kit */
+/* Copyright (C) 2015--2025 SysDeer Technologies, LLC */
+/* Released under GPLv2 and GPLv3; see COPYING.PERK. */
+/***************************************************************/
+
+#ifndef SLIBTOOL_SYNOPSIS_IMPL_H
+#define SLIBTOOL_SYNOPSIS_IMPL_H
+
+#define PERK_DEFAULT_CMD_SYNOPSIS \
+ "%s — PE/COFF Resource Kit\n\n" \
+ "Synopsis:\n" \
+ " %s [option] ...\n" \
+ " %s --cmd=<command> [option] ...\n" \
+ " %s --cmd=<command> [option] ... <file> ...\n\n" \
+ "Options:\n"
+
+#define PERK_PERK_CMD_SYNOPSIS \
+ "%s — PE/COFF Resource Kit\n\n" \
+ "Synopsis:\n" \
+ " %s [option] ...\n" \
+ " %s [option] ... <file> ...\n\n" \
+ "Options:\n"
+
+#define PERK_AR_CMD_SYNOPSIS \
+ "%s — the PE/COFF Resource Kit Archiver\n\n" \
+ "Synopsis:\n" \
+ " %s -d [-v] <archive> <file> ...\n" \
+ " %s -p [-v] [-s] <archive> <file> ...\n" \
+ " %s -q [-v] [-c] <archive> <file> ...\n" \
+ " %s -r [-v] [-c] [-u] <archive> <file> ...\n" \
+ " %s -t [-v] [-s] <archive> [<file> ...]\n" \
+ " %s -x [-v] [-s] [-C] [-T] <archive> [<file> ...]\n\n" \
+ \
+ " %s -m [-v] <archive> <file> ...\n" \
+ " %s -m -a [-v] <posname> <archive> <file> ...\n" \
+ " %s -m -b [-v] <posname> <archive> <file> ...\n" \
+ " %s -m -i [-v] <posname> <archive> <file> ...\n\n" \
+ \
+ " %s -r [-v] [-c] [-u] <archive> <file> ...\n" \
+ " %s -r -a [-v] [-c] [-u] <posname> <archive> <file> ...\n" \
+ " %s -r -b [-v] [-c] [-u] <posname> <archive> <file> ...\n" \
+ " %s -r -i [-v] [-c] [-u] <posname> <archive> <file> ...\n\n" \
+ "Options:\n"
+
+#endif
diff --git a/src/output/pe_output_error.c b/src/output/pe_output_error.c
index 1d818d1..50c7357 100644
--- a/src/output/pe_output_error.c
+++ b/src/output/pe_output_error.c
@@ -35,9 +35,18 @@ static const char * const pe_error_strings[PERK_ERR_CAP] = {
[PERK_ERR_BAD_IMAGE_TYPE] = "bad PE image type",
[PERK_ERR_UNSUPPORTED_ABI] = "unsupported image abi",
+ [PERK_ERR_AR_NON_ARCHIVE_IMAGE]= "the parsed PE/COFF object or image is not an archive",
[PERK_ERR_AR_NON_PE_MEMBERS] = "format of current archive member is not PE/COFF",
[PERK_ERR_AR_MIXED_PE_MEMBERS] = "archive mixes objects of different architectures",
[PERK_ERR_AR_NESTED_ARCHIVE] = "nested archives are currently not supported",
+
+ [PERK_ERR_AR_MISSING_ACTION] = "missing action, which should be exactly one of [dqmrxpt]",
+ [PERK_ERR_AR_MULTIPLE_ACTIONS] = "exactly one action permitted, multiple actions specified",
+ [PERK_ERR_AR_MULTIPLE_ANCHORS] = "multiple anchors: may specify _before_ or _after_, but not both",
+ [PERK_ERR_AR_INVALID_ANCHORS] = "mismatched arguments: anchors are incompatible with the selected action",
+ [PERK_ERR_AR_MISSING_ANCHOR] = "missing anchor: <posname> provided, but no anchor specified",
+ [PERK_ERR_AR_NULL_POSNAME] = "null <posname> argument with [-a], [-b], or [-i]",
+ [PERK_ERR_AR_NULL_ARNAME] = "null <arname> argument",
};
static const char * pe_output_error_header(const struct pe_error_info * erri)
diff --git a/src/skin/pe_skin_ar.c b/src/skin/pe_skin_ar.c
index e99ee37..af181ae 100644
--- a/src/skin/pe_skin_ar.c
+++ b/src/skin/pe_skin_ar.c
@@ -11,5 +11,25 @@ const perk_hidden struct argv_option pe_ar_options[] = {
ARGV_OPTION_HYBRID_ONLY,0,0,
"show usage information"},
+ {"Wverbose", 'v',TAG_VERBOSE,ARGV_OPTARG_NONE,
+ ARGV_OPTION_HYBRID_ONLY,0,0,
+ "provide verbose output"},
+
+ {"Wlist", 't',TAG_AR_LIST_MEMBERS,ARGV_OPTARG_NONE,
+ ARGV_OPTION_HYBRID_ONLY,0,0,
+ "list archive members; names only be default, "
+ "or otherwise a detailed table of contents in "
+ "verbose mode"},
+
+ {"Wprint", 'p',TAG_AR_PRINT_MEMBERS,ARGV_OPTARG_NONE,
+ ARGV_OPTION_HYBRID_ONLY,0,0,
+ "print archive members"},
+
+ {"Wpretty", 0,TAG_PRETTY,ARGV_OPTARG_REQUIRED,
+ ARGV_OPTION_HYBRID_ONLY|ARGV_OPTION_HYBRID_EQUAL,
+ "yaml|posix|hexdata",0,
+ "list archive members or print content "
+ "using the %s format specification"},
+
{0,0,0,0,0,0,0,0}
};