Control: retitle -1 ltrace: [p]{read,write}[v]() handling, common *64() 
functions in modern glibc, [fl]seek[o]()/ftell[o](); requisite [u]llong; 20x 
speed optimisation in default config; format %b+bin() lens and %w[f]32x; 
splice/c_f_r/sendfile[64] formatting; useful __errno_location; %config lines 
not limited to 1kB; pipe[2]() with fds; sysconf(); %m[un]map[64](); operator 
new/delete; <regex.h>; another ~10%

I'm attaching everything, since I fixed some documentation fucky-wuckies
from the earlier ones. 0018-0021 are new, and turn
  $ grep pipe ll
  pipe2(0x7ffcd6d7baf0, 0x80000, 0x7ffcd6d7bb70, 0x7ffcd6d7bb70)                
       = 0
  $ grep -e mmap -e sysconf ll
  sysconf(30, 0, 1, 0x393c)                                                     
       = 4096
  mmap64(0, 0x3d3c, 1, 2)                                                       
       = 0x7fe67b29c000
into
  $ grep pipe ll
  pipe2([3, 4], 0x80000)                                                        
       = 0
  $ grep -e mmap -e sysconf ll
  sysconf(_SC_PAGE_SIZE)                                                        
       = 4096
  mmap64(0, 15676, 0b1, 0x2, 0, 4096)                                           
       = 0x7fa1b27ff000

regex.h suite looks like:
  regcomp(0x7fff5ee83120, ";", 0b100)                                           
       = REG_OK
  
  regexec(0x7fff5ee83040, "\nint   SYS_access(string,octal);"..., 1, [{0, 31}], 
0b111) = REG_NOMATCH
  regexec(0x7fff5ee83040, ";\nint   SYS_access(string,octal)"..., 1, [{0, 32}], 
0b111) = REG_OK
  
  regcomp(0x7ffece59ee70, "\\(", 0b100)                                         
       = REG_EPAREN
  regerror(REG_EPAREN, 0x7ffece59ee70, nil, 0)                                  
       = 18
  malloc(18)                                                                    
       = 0x5604c35c2140
  regerror(REG_EPAREN, 0x7ffece59ee70, "Unmatched ( or \\(", 18)                
       = 18

And function lookups are sped up approximately ten-fold
(3μs -> 300ns on a hit (depending on the access pattern, of course) and
 12μs -> 600ns on a miss)
which translates to an approximately 10% (roughly, maybe, it's difficult
to measure) speed-up depending on the program:
  $ hyperfine '/bin/ltrace -o/dev/null -F /etc/ltrace.conf ls' \
                 './ltrace -o/dev/null -F /etc/ltrace.conf ls'
  Benchmark 1: /bin/ltrace -o/dev/null -F /etc/ltrace.conf ls
    Time (mean ± σ):     545.6 ms ±  75.8 ms    [User: 153.6 ms, System: 350.1 
ms]
    Range (min … max):   473.1 ms … 722.5 ms    10 runs
  
  Benchmark 2: ./ltrace -o/dev/null -F /etc/ltrace.conf ls
    Time (mean ± σ):     447.1 ms ±  36.7 ms    [User: 94.6 ms, System: 329.8 
ms]
    Range (min … max):   412.8 ms … 516.4 ms    10 runs
  
  Summary
    './ltrace -o/dev/null -F /etc/ltrace.conf ls' ran
      1.22 ± 0.20 times faster than '/bin/ltrace -o/dev/null -F 
/etc/ltrace.conf ls'
vs
  $ hyperfine '/bin/ltrace -F /etc/ltrace.conf -o/dev/null  
~/code/voreutils/out/cmd/tac -rs \; < etc/ltrace.conf' \
                 './ltrace -F /etc/ltrace.conf -o/dev/null  
~/code/voreutils/out/cmd/tac -rs \; < etc/ltrace.conf'
  Benchmark 1: /bin/ltrace -F /etc/ltrace.conf -o/dev/null  
~/code/voreutils/out/cmd/tac -rs \; < etc/ltrace.conf
    Time (mean ± σ):      3.631 s ±  0.117 s    [User: 1.087 s, System: 2.334 s]
    Range (min … max):    3.516 s …  3.886 s    10 runs
  
  Benchmark 2: ./ltrace -F /etc/ltrace.conf -o/dev/null  
~/code/voreutils/out/cmd/tac -rs \; < etc/ltrace.conf
    Time (mean ± σ):      3.113 s ±  0.169 s    [User: 0.659 s, System: 2.274 s]
    Range (min … max):    2.934 s …  3.496 s    10 runs
  
  Summary
    './ltrace -F /etc/ltrace.conf -o/dev/null  ~/code/voreutils/out/cmd/tac -rs 
\; < etc/ltrace.conf' ran
      1.17 ± 0.07 times faster than '/bin/ltrace -F /etc/ltrace.conf 
-o/dev/null  ~/code/voreutils/out/cmd/tac -rs \; < etc/ltrace.conf'
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz>
Date: Tue, 25 Jul 2023 21:20:32 +0200
Subject: Add llong/ullong. Tested on i686/amd64

---
 expr.c                         |  6 +++---
 expr.h                         |  4 ++--
 lens_default.c                 | 14 ++++++++++----
 printf.c                       | 10 +++++-----
 read_config_file.c             |  4 ++++
 sysdeps/linux-gnu/ia64/fetch.c |  4 ++++
 sysdeps/linux-gnu/m68k/fetch.c |  2 ++
 sysdeps/linux-gnu/ppc/fetch.c  |  2 ++
 sysdeps/linux-gnu/ppc/trace.c  |  6 ++++++
 sysdeps/linux-gnu/s390/fetch.c |  2 ++
 sysdeps/linux-gnu/s390/trace.c |  6 ++++++
 sysdeps/linux-gnu/x86/fetch.c  | 15 ++++++++++++---
 sysdeps/linux-gnu/x86/trace.c  |  6 ++++++
 type.c                         | 25 ++++++++++++++++++++++---
 type.h                         |  2 ++
 value.c                        | 16 ++++++++--------
 value.h                        |  4 ++--
 zero.c                         |  2 +-
 18 files changed, 99 insertions(+), 31 deletions(-)

diff --git a/expr.c b/expr.c
index 32860fd..01ce4c6 100644
--- a/expr.c
+++ b/expr.c
@@ -246,7 +246,7 @@ eval_index(struct expr_node *node, struct value *context,
 	if (expr_eval(node->lhs, context, arguments, &lhs) < 0)
 		return -1;
 
-	long l;
+	long long l;
 	if (expr_eval_word(node->u.node.n, context, arguments, &l) < 0) {
 	fail:
 		value_destroy(&lhs);
@@ -305,7 +305,7 @@ expr_eval(struct expr_node *node, struct value *context,
 
 int
 expr_eval_word(struct expr_node *node, struct value *context,
-	       struct value_dict *arguments, long *ret_value)
+	       struct value_dict *arguments, long long *ret_value)
 {
 	struct value val;
 	if (expr_eval(node, context, arguments, &val) < 0)
@@ -318,7 +318,7 @@ expr_eval_word(struct expr_node *node, struct value *context,
 }
 
 int
-expr_eval_constant(struct expr_node *node, long *valuep)
+expr_eval_constant(struct expr_node *node, long long *valuep)
 {
 	assert(expr_is_compile_constant(node));
 	return expr_eval_word(node, NULL, NULL, valuep);
diff --git a/expr.h b/expr.h
index 53b75b7..aa4b0b6 100644
--- a/expr.h
+++ b/expr.h
@@ -135,7 +135,7 @@ int expr_eval(struct expr_node *node, struct value *context,
 
 /* Evaluate compile-time expression.  Returns 0 on success or negative
  * value on failure.  Computed value is passed back in *VALUEP.  */
-int expr_eval_constant(struct expr_node *node, long *valuep);
+int expr_eval_constant(struct expr_node *node, long long *valuep);
 
 /* Evaluate expression, whose result should fit into a word.  In order
  * to easily support all the structure and array accesses, we simply
@@ -143,7 +143,7 @@ int expr_eval_constant(struct expr_node *node, long *valuep);
  * to be able to get out a word-size datum to use it as an index, a
  * length, etc.  */
 int expr_eval_word(struct expr_node *node, struct value *context,
-		   struct value_dict *arguments, long *ret_value);
+		   struct value_dict *arguments, long long *ret_value);
 
 /* Returns non-zero value if the expression is a compile-time
  * constant.  Currently this is only EXPR_OP_CONST, but eventually
diff --git a/lens_default.c b/lens_default.c
index 36531c2..2fd4915 100644
--- a/lens_default.c
+++ b/lens_default.c
@@ -58,7 +58,7 @@ READER(read_double, double)
 
 #define HANDLE_WIDTH(BITS)						\
 	do {								\
-		long l;							\
+		long long l;							\
 		if (value_extract_word(value, &l, arguments) < 0)	\
 			return -1;					\
 		int##BITS##_t i = l;					\
@@ -124,7 +124,7 @@ acc_fprintf(int *countp, FILE *stream, const char *format, ...)
 static int
 format_char(FILE *stream, struct value *value, struct value_dict *arguments)
 {
-	long lc;
+	long long lc;
 	if (value_extract_word(value, &lc, arguments) < 0)
 		return -1;
 	int c = (int)lc;
@@ -344,8 +344,8 @@ format_array(FILE *stream, struct value *value, struct value_dict *arguments,
 {
 	/* We need "long" to be long enough to cover the whole address
 	 * space.  */
-	(void)sizeof(char[1 - 2*(sizeof(long) < sizeof(void *))]);
-	long l;
+	(void)sizeof(char[1 - 2*(sizeof(long long) < sizeof(void *))]);
+	long long l;
 	if (expr_eval_word(length, value, arguments, &l) < 0)
 		return -1;
 	size_t len = (size_t)l;
@@ -394,11 +394,13 @@ toplevel_format_lens(struct lens *lens, FILE *stream,
 	case ARGTYPE_SHORT:
 	case ARGTYPE_INT:
 	case ARGTYPE_LONG:
+	case ARGTYPE_LLONG:
 		return format_integer(stream, value, int_fmt, arguments);
 
 	case ARGTYPE_USHORT:
 	case ARGTYPE_UINT:
 	case ARGTYPE_ULONG:
+	case ARGTYPE_ULLONG:
 		if (int_fmt == INT_FMT_i || int_fmt == INT_FMT_default)
 			int_fmt = INT_FMT_u;
 		return format_integer(stream, value, int_fmt, arguments);
@@ -520,9 +522,11 @@ bool_lens_format_cb(struct lens *lens, FILE *stream,
 	case ARGTYPE_SHORT:
 	case ARGTYPE_INT:
 	case ARGTYPE_LONG:
+	case ARGTYPE_LLONG:
 	case ARGTYPE_USHORT:
 	case ARGTYPE_UINT:
 	case ARGTYPE_ULONG:
+	case ARGTYPE_ULLONG:
 	case ARGTYPE_CHAR:
 		if ((zero = value_is_zero(value, arguments)) < 0)
 			return -1;
@@ -578,9 +582,11 @@ string_lens_format_cb(struct lens *lens, FILE *stream,
 	case ARGTYPE_SHORT:
 	case ARGTYPE_INT:
 	case ARGTYPE_LONG:
+	case ARGTYPE_LLONG:
 	case ARGTYPE_USHORT:
 	case ARGTYPE_UINT:
 	case ARGTYPE_ULONG:
+	case ARGTYPE_ULLONG:
 		return toplevel_format_lens(lens, stream, value,
 					    arguments, INT_FMT_default);
 
diff --git a/printf.c b/printf.c
index 9aa68a5..773d104 100644
--- a/printf.c
+++ b/printf.c
@@ -35,7 +35,7 @@
 struct param_enum {
 	struct value array;
 	int percent;
-	size_t *future_length;
+	unsigned long long *future_length;
 	char *format;
 	char const *ptr;
 	char const *end;
@@ -101,15 +101,15 @@ form_next_param(struct param_enum *self,
 		struct arg_type_info *infop)
 {
 	/* XXX note: Some types are wrong because we lack
-	   ARGTYPE_LONGLONG, ARGTYPE_UCHAR and ARGTYPE_SCHAR.  */
+	   ARGTYPE_UCHAR and ARGTYPE_SCHAR.  */
 	assert(lng <= 2);
 	assert(hlf <= 2);
 	static enum arg_type ints[] =
 		{ ARGTYPE_CHAR, ARGTYPE_SHORT, ARGTYPE_INT,
-		  ARGTYPE_LONG, ARGTYPE_ULONG };
+		  ARGTYPE_LONG, ARGTYPE_LLONG };
 	static enum arg_type uints[] =
 		{ ARGTYPE_CHAR, ARGTYPE_USHORT, ARGTYPE_UINT,
-		  ARGTYPE_ULONG, ARGTYPE_ULONG };
+		  ARGTYPE_ULONG, ARGTYPE_ULLONG };
 
 	struct arg_type_info *elt_info = NULL;
 	if (format_type == ARGTYPE_ARRAY || format_type == ARGTYPE_POINTER)
@@ -343,7 +343,7 @@ static enum param_status
 param_printf_stop(struct param_enum *self, struct value *value)
 {
 	if (self->future_length != NULL
-	    && value_extract_word(value, (long *)self->future_length, NULL) < 0)
+	    && value_extract_word(value, (long long *)self->future_length, NULL) < 0)
 		drop_future_length(self);
 
 	return PPCB_CONT;
diff --git a/read_config_file.c b/read_config_file.c
index e247436..98278ac 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -79,6 +79,8 @@ parse_arg_type(char **name, enum arg_type *ret)
 	KEYWORD("uint", ARGTYPE_UINT);
 	KEYWORD("long", ARGTYPE_LONG);
 	KEYWORD("ulong", ARGTYPE_ULONG);
+	KEYWORD("llong", ARGTYPE_LLONG);
+	KEYWORD("ullong", ARGTYPE_ULLONG);
 	KEYWORD("char", ARGTYPE_CHAR);
 	KEYWORD("short", ARGTYPE_SHORT);
 	KEYWORD("ushort", ARGTYPE_USHORT);
@@ -912,6 +914,8 @@ parse_nonpointer_type(char **str, struct param **extra_param, size_t param_num,
 	case ARGTYPE_UINT:
 	case ARGTYPE_LONG:
 	case ARGTYPE_ULONG:
+	case ARGTYPE_LLONG:
+	case ARGTYPE_ULLONG:
 	case ARGTYPE_CHAR:
 	case ARGTYPE_SHORT:
 	case ARGTYPE_USHORT:
diff --git a/sysdeps/linux-gnu/ia64/fetch.c b/sysdeps/linux-gnu/ia64/fetch.c
index 54dc5b8..3828dd1 100644
--- a/sysdeps/linux-gnu/ia64/fetch.c
+++ b/sysdeps/linux-gnu/ia64/fetch.c
@@ -433,6 +433,8 @@ arch_fetch_arg_next(struct fetch_context *ctx, enum tof type,
 	case ARGTYPE_UINT:
 	case ARGTYPE_LONG:
 	case ARGTYPE_ULONG:
+	case ARGTYPE_LLONG:
+	case ARGTYPE_ULLONG:
 	case ARGTYPE_POINTER:
 		return allocate_arg(ctx, proc, info, valuep);
 
@@ -467,6 +469,8 @@ arch_fetch_retval(struct fetch_context *ctx, enum tof type,
 	case ARGTYPE_UINT:
 	case ARGTYPE_LONG:
 	case ARGTYPE_ULONG:
+	case ARGTYPE_LLONG:
+	case ARGTYPE_ULLONG:
 	case ARGTYPE_POINTER:
 	case ARGTYPE_STRUCT:
 		return allocate_ret(ctx, proc, info, valuep);
diff --git a/sysdeps/linux-gnu/m68k/fetch.c b/sysdeps/linux-gnu/m68k/fetch.c
index f6d8a0b..0870c82 100644
--- a/sysdeps/linux-gnu/m68k/fetch.c
+++ b/sysdeps/linux-gnu/m68k/fetch.c
@@ -170,6 +170,8 @@ arch_fetch_retval(struct fetch_context *context, enum tof type,
 	case ARGTYPE_UINT:
 	case ARGTYPE_LONG:
 	case ARGTYPE_ULONG:
+	case ARGTYPE_LLONG:
+	case ARGTYPE_ULLONG:
 	case ARGTYPE_CHAR:
 	case ARGTYPE_SHORT:
 	case ARGTYPE_USHORT:
diff --git a/sysdeps/linux-gnu/ppc/fetch.c b/sysdeps/linux-gnu/ppc/fetch.c
index 9963a1e..6fa5b73 100644
--- a/sysdeps/linux-gnu/ppc/fetch.c
+++ b/sysdeps/linux-gnu/ppc/fetch.c
@@ -309,6 +309,8 @@ allocate_argument(struct fetch_context *ctx, struct Process *proc,
 	case ARGTYPE_UINT:
 	case ARGTYPE_LONG:
 	case ARGTYPE_ULONG:
+	case ARGTYPE_LLONG:
+	case ARGTYPE_ULLONG:
 	case ARGTYPE_POINTER:
 		break;
 
diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c
index 4357a1e..c1e9e2e 100644
--- a/sysdeps/linux-gnu/ppc/trace.c
+++ b/sysdeps/linux-gnu/ppc/trace.c
@@ -197,6 +197,10 @@ arch_type_sizeof(struct Process *proc, struct arg_type_info *info)
 	case ARGTYPE_POINTER:
 		return proc->e_machine == EM_PPC64 ? 8 : 4;
 
+	case ARGTYPE_LLONG:
+	case ARGTYPE_ULLONG:
+		return 8;
+
 	case ARGTYPE_FLOAT:
 		return 4;
 	case ARGTYPE_DOUBLE:
@@ -233,6 +237,8 @@ arch_type_alignof(struct Process *proc, struct arg_type_info *info)
 	case ARGTYPE_UINT:
 	case ARGTYPE_LONG:
 	case ARGTYPE_ULONG:
+	case ARGTYPE_LLONG:
+	case ARGTYPE_ULLONG:
 	case ARGTYPE_POINTER:
 	case ARGTYPE_FLOAT:
 	case ARGTYPE_DOUBLE:
diff --git a/sysdeps/linux-gnu/s390/fetch.c b/sysdeps/linux-gnu/s390/fetch.c
index fa8f42d..b01a71a 100644
--- a/sysdeps/linux-gnu/s390/fetch.c
+++ b/sysdeps/linux-gnu/s390/fetch.c
@@ -252,6 +252,8 @@ arch_fetch_arg_next(struct fetch_context *ctx, enum tof type,
 	case ARGTYPE_UINT:
 	case ARGTYPE_LONG:
 	case ARGTYPE_ULONG:
+	case ARGTYPE_LLONG:
+	case ARGTYPE_ULLONG:
 	case ARGTYPE_CHAR:
 	case ARGTYPE_SHORT:
 	case ARGTYPE_USHORT:
diff --git a/sysdeps/linux-gnu/s390/trace.c b/sysdeps/linux-gnu/s390/trace.c
index b9e05ff..6ded4fb 100644
--- a/sysdeps/linux-gnu/s390/trace.c
+++ b/sysdeps/linux-gnu/s390/trace.c
@@ -200,6 +200,10 @@ arch_type_sizeof(struct Process *proc, struct arg_type_info *info)
 	case ARGTYPE_POINTER:
 		return proc->e_class == ELFCLASS64 ? 8 : 4;
 
+	case ARGTYPE_LLONG:
+	case ARGTYPE_ULLONG:
+		return 8;
+
 	case ARGTYPE_FLOAT:
 		return 4;
 	case ARGTYPE_DOUBLE:
@@ -240,6 +244,8 @@ arch_type_alignof(struct Process *proc, struct arg_type_info *info)
 
 	case ARGTYPE_LONG:
 	case ARGTYPE_ULONG:
+	case ARGTYPE_LLONG:
+	case ARGTYPE_ULLONG:
 	case ARGTYPE_POINTER:
 		return proc->e_class == ELFCLASS64 ? 8 : 4;
 
diff --git a/sysdeps/linux-gnu/x86/fetch.c b/sysdeps/linux-gnu/x86/fetch.c
index 4dab4cc..b9ed8f1 100644
--- a/sysdeps/linux-gnu/x86/fetch.c
+++ b/sysdeps/linux-gnu/x86/fetch.c
@@ -259,7 +259,13 @@ allocate_integer(struct fetch_context *context, struct value *valuep,
 			HANDLE(0, rax);
 			HANDLE(1, rdx);
 #else
-			HANDLE(0, eax);
+		case 0:  // u64 return in EAX:EDX
+			copy_int_register(context, valuep,
+					  context->iregs.eax, offset);
+			if(sz == 8)
+				copy_int_register(context, valuep,
+						  context->iregs.edx, offset + 4);
+			return CLASS_INTEGER;
 #endif
 		default:
 			assert(!"Too many return value classes.");
@@ -448,9 +454,10 @@ classify(struct Process *proc, struct fetch_context *context,
 	case ARGTYPE_UINT:
 	case ARGTYPE_LONG:
 	case ARGTYPE_ULONG:
+	case ARGTYPE_LLONG:
+	case ARGTYPE_ULLONG:
 
 	case ARGTYPE_POINTER:
-		/* and LONGLONG */
 		/* CLASS_INTEGER */
 		classes[0] = CLASS_INTEGER;
 		return 1;
@@ -470,7 +477,7 @@ classify(struct Process *proc, struct fetch_context *context,
 		 * passed by value.  */
 		assert(expr_is_compile_constant(info->u.array_info.length));
 
-		long l;
+		long long l;
 		if (expr_eval_constant(info->u.array_info.length, &l) < 0)
 			return -1;
 
@@ -609,6 +616,8 @@ arch_fetch_retval_32(struct fetch_context *context, enum tof type,
 	case ARGTYPE_UINT:
 	case ARGTYPE_LONG:
 	case ARGTYPE_ULONG:
+	case ARGTYPE_LLONG:
+	case ARGTYPE_ULLONG:
 	case ARGTYPE_CHAR:
 	case ARGTYPE_SHORT:
 	case ARGTYPE_USHORT:
diff --git a/sysdeps/linux-gnu/x86/trace.c b/sysdeps/linux-gnu/x86/trace.c
index ed8bdb4..802bd0f 100644
--- a/sysdeps/linux-gnu/x86/trace.c
+++ b/sysdeps/linux-gnu/x86/trace.c
@@ -134,6 +134,10 @@ arch_type_sizeof(struct Process *proc, struct arg_type_info *info)
 	case ARGTYPE_POINTER:
 		return proc->e_machine == EM_X86_64 ? 8 : 4;
 
+	case ARGTYPE_LLONG:
+	case ARGTYPE_ULLONG:
+		return 8;
+
 	case ARGTYPE_FLOAT:
 		return 4;
 	case ARGTYPE_DOUBLE:
@@ -175,6 +179,8 @@ arch_type_alignof(struct Process *proc, struct arg_type_info *info)
 
 	case ARGTYPE_LONG:
 	case ARGTYPE_ULONG:
+	case ARGTYPE_LLONG:
+	case ARGTYPE_ULLONG:
 	case ARGTYPE_POINTER:
 		return proc->e_machine == EM_X86_64 ? 8 : 4;
 
diff --git a/type.c b/type.c
index 3ce8563..0880e5b 100644
--- a/type.c
+++ b/type.c
@@ -43,6 +43,8 @@ type_get_simple(enum arg_type type)
 	HANDLE(ARGTYPE_UINT)
 	HANDLE(ARGTYPE_LONG)
 	HANDLE(ARGTYPE_ULONG)
+	HANDLE(ARGTYPE_LLONG)
+	HANDLE(ARGTYPE_ULLONG)
 	HANDLE(ARGTYPE_CHAR)
 	HANDLE(ARGTYPE_SHORT)
 	HANDLE(ARGTYPE_USHORT)
@@ -239,6 +241,8 @@ type_destroy(struct arg_type_info *info)
 	case ARGTYPE_UINT:
 	case ARGTYPE_LONG:
 	case ARGTYPE_ULONG:
+	case ARGTYPE_LLONG:
+	case ARGTYPE_ULLONG:
 	case ARGTYPE_CHAR:
 	case ARGTYPE_SHORT:
 	case ARGTYPE_USHORT:
@@ -312,6 +316,10 @@ type_sizeof(struct Process *proc, struct arg_type_info *type)
 	case ARGTYPE_ULONG:
 		return sizeof(long);
 
+	case ARGTYPE_LLONG:
+	case ARGTYPE_ULLONG:
+		return sizeof(long long);
+
 	case ARGTYPE_FLOAT:
 		return sizeof(float);
 
@@ -328,7 +336,7 @@ type_sizeof(struct Process *proc, struct arg_type_info *type)
 
 	case ARGTYPE_ARRAY:
 		if (expr_is_compile_constant(type->u.array_info.length)) {
-			long l;
+			long long l;
 			if (expr_eval_constant(type->u.array_info.length,
 					       &l) < 0)
 				return -1;
@@ -369,6 +377,7 @@ type_alignof(struct Process *proc, struct arg_type_info *type)
 	struct { char c; short s; } cs;
 	struct { char c; int i; } ci;
 	struct { char c; long l; } cl;
+	struct { char c; long long ll; } cll;
 	struct { char c; void* p; } cp;
 	struct { char c; float f; } cf;
 	struct { char c; double d; } cd;
@@ -377,12 +386,16 @@ type_alignof(struct Process *proc, struct arg_type_info *type)
 	static size_t short_alignment = alignof(s, cs);
 	static size_t int_alignment = alignof(i, ci);
 	static size_t long_alignment = alignof(l, cl);
+	static size_t llong_alignment = alignof(ll, cll);
 	static size_t ptr_alignment = alignof(p, cp);
 	static size_t float_alignment = alignof(f, cf);
 	static size_t double_alignment = alignof(d, cd);
 
 	switch (type->type) {
 		size_t alignment;
+	case ARGTYPE_LLONG:
+	case ARGTYPE_ULLONG:
+		return llong_alignment;
 	case ARGTYPE_LONG:
 	case ARGTYPE_ULONG:
 		return long_alignment;
@@ -467,7 +480,7 @@ type_aggregate_size(struct arg_type_info *info)
 	       || info->type == ARGTYPE_ARRAY);
 
 	switch (info->type) {
-		long ret;
+		long long ret;
 	case ARGTYPE_ARRAY:
 		if (expr_eval_constant(info->u.array_info.length, &ret) < 0)
 			return (size_t)-1;
@@ -489,6 +502,8 @@ type_is_integral(enum arg_type type)
 	case ARGTYPE_UINT:
 	case ARGTYPE_LONG:
 	case ARGTYPE_ULONG:
+	case ARGTYPE_LLONG:
+	case ARGTYPE_ULLONG:
 	case ARGTYPE_CHAR:
 	case ARGTYPE_SHORT:
 	case ARGTYPE_USHORT:
@@ -517,11 +532,13 @@ type_is_signed(enum arg_type type)
 	case ARGTYPE_SHORT:
 	case ARGTYPE_INT:
 	case ARGTYPE_LONG:
+	case ARGTYPE_LLONG:
 		return 1;
 
+	case ARGTYPE_USHORT:
 	case ARGTYPE_UINT:
 	case ARGTYPE_ULONG:
-	case ARGTYPE_USHORT:
+	case ARGTYPE_ULLONG:
 		return 0;
 
 	case ARGTYPE_VOID:
@@ -551,8 +568,10 @@ type_get_fp_equivalent(struct arg_type_info *info)
 	case ARGTYPE_SHORT:
 	case ARGTYPE_INT:
 	case ARGTYPE_LONG:
+	case ARGTYPE_LLONG:
 	case ARGTYPE_UINT:
 	case ARGTYPE_ULONG:
+	case ARGTYPE_ULLONG:
 	case ARGTYPE_USHORT:
 	case ARGTYPE_VOID:
 	case ARGTYPE_ARRAY:
diff --git a/type.h b/type.h
index e8dec71..95838a0 100644
--- a/type.h
+++ b/type.h
@@ -32,6 +32,8 @@ enum arg_type {
 	ARGTYPE_UINT,
 	ARGTYPE_LONG,
 	ARGTYPE_ULONG,
+	ARGTYPE_LLONG,
+	ARGTYPE_ULLONG,
 	ARGTYPE_CHAR,
 	ARGTYPE_SHORT,
 	ARGTYPE_USHORT,
diff --git a/value.c b/value.c
index f89570f..3209b9f 100644
--- a/value.c
+++ b/value.c
@@ -219,8 +219,8 @@ value_size(struct value *val, struct value_dict *arguments)
 		      arguments, &length) < 0)
 		return (size_t)-1;
 
-	size_t l;
-	int o = value_extract_word(&length, (long *)&l, arguments);
+	uint64_t l;
+	int o = value_extract_word(&length, (long long *)&l, arguments);
 	value_destroy(&length);
 
 	if (o < 0)
@@ -278,7 +278,7 @@ value_init_deref(struct value *ret_val, struct value *valp)
 
 	/* Note: extracting a pointer value should not need value_dict
 	 * with function arguments.  */
-	long l;
+	long long l;
 	if (value_extract_word(valp, &l, NULL) < 0)
 		return -1;
 
@@ -368,7 +368,7 @@ value_extract_buf_sz(struct value *value, unsigned char *tgt, size_t sz,
 }
 
 int
-value_extract_word(struct value *value, long *retp,
+value_extract_word(struct value *value, long long *retp,
 		   struct value_dict *arguments)
 {
 	size_t sz = type_sizeof(value->inferior, value->type);
@@ -387,16 +387,16 @@ value_extract_word(struct value *value, long *retp,
 
 	switch (sz) {
 	case 1:
-		*retp = (long)u.u8;
+		*retp = (long long)u.u8;
 		return 0;
 	case 2:
-		*retp = (long)u.u16;
+		*retp = (long long)u.u16;
 		return 0;
 	case 4:
-		*retp = (long)u.u32;
+		*retp = (long long)u.u32;
 		return 0;
 	case 8:
-		*retp = (long)u.u64;
+		*retp = (long long)u.u64;
 		return 0;
 	default:
 		assert(sz != sz);
diff --git a/value.h b/value.h
index 795573c..a527667 100644
--- a/value.h
+++ b/value.h
@@ -52,7 +52,7 @@ struct value {
 	union {
 		void *address;  /* VAL_LOC_COPY, VAL_LOC_SHARED */
 		arch_addr_t inf_address;  /* VAL_LOC_INFERIOR */
-		long value;     /* VAL_LOC_WORD */
+		long long value;     /* VAL_LOC_WORD */
 		unsigned char buf[0];
 	} u;
 	enum value_location_t where;
@@ -137,7 +137,7 @@ size_t value_size(struct value *val, struct value_dict *arguments);
 
 /* Extract at most word-sized datum from the value.  Return 0 on
  * success or negative value on failure.  */
-int value_extract_word(struct value *val, long *retp,
+int value_extract_word(struct value *val, long long *retp,
 		       struct value_dict *arguments);
 
 /* Copy contents of VAL to DATA.  The buffer must be large enough to
diff --git a/zero.c b/zero.c
index bc119ee..ddb5e0d 100644
--- a/zero.c
+++ b/zero.c
@@ -58,7 +58,7 @@ static int
 zero_callback(struct value *ret_value, struct value *lhs,
 	      struct value *rhs, struct value_dict *arguments, void *data)
 {
-	long l;
+	long long l;
 	if (value_extract_word(rhs, &l, arguments) < 0)
 		return -1;
 	if (l < 0)
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz>
Date: Wed, 26 Jul 2023 01:42:23 +0200
Subject: ltrace.conf: openat*(), getopt_long*() consistency, fseek*(),
 ftell*(), reallocarray(), pread*(), pwrite*(), lseek*() with whence enum,
 *readv*(), *writev*(), __cxa_finalize()

Of course, this assumes that symbols ending in 64 are the only ones
taking a 64-bit off_t, and thus the ones that don't take longs.
This holds on x86, but probably not any new arches which just don't have
the weird broken *64() symbols.
---
 etc/ltrace.conf | 42 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 37 insertions(+), 5 deletions(-)

diff --git a/etc/ltrace.conf b/etc/ltrace.conf
index 8f83986..855d373 100644
--- a/etc/ltrace.conf
+++ b/etc/ltrace.conf
@@ -55,15 +55,18 @@ int dlclose(addr);
 addr __errno_location();
 
 ; fcntl.h
-int open(string,int,octal);		; WARNING: 3rd argument may not be there
-int open64(string,int,octal);		; WARNING: 3rd argument may not be there
+typedef openat_fd = enum(AT_FDCWD=-100);
+int open(string,hex(int),octal);		; WARNING: 3rd argument may not be there
+int open64(string,hex(int),octal);		; WARNING: 3rd argument may not be there
+int openat(openat_fd,string,hex(int),octal);	; WARNING: 4th argument may not be there
+int openat64(openat_fd,string,hex(int),octal);	; WARNING: 4th argument may not be there
 
 ; fnmatch.h
 int fnmatch(string, string, int);
 
 ; getopt.h
-int getopt_long(int,addr,string,addr,int*);
-int getopt_long_only(int,addr,string,addr,addr);
+int getopt_long(int,addr,string,addr,+int*);
+int getopt_long_only(int,addr,string,addr,+int*);
 
 ; grp.h
 void endgrent();
@@ -194,6 +197,13 @@ ulong fread(addr,ulong,ulong,file);
 ulong fread_unlocked(addr,ulong,ulong,file);
 ulong fwrite(string,ulong,ulong,file);
 ulong fwrite_unlocked(string,ulong,ulong,file);
+typedef lseek_whence = enum(SEEK_SET=0, SEEK_CUR=1, SEEK_END=2, SEEK_DATA=3, SEEK_HOLE=4);
+int fseek(file,long,lseek_whence);
+int fseeko(file,long,lseek_whence);
+int fseeko64(file,llong,lseek_whence);
+long ftell(file);
+long ftello(file);
+llong ftello64(file);
 int pclose(addr);
 void perror(string);
 addr popen(string, string);
@@ -227,6 +237,7 @@ addr malloc(ulong);
 void qsort(addr,ulong,ulong,addr);
 int random();
 addr realloc(addr,ulong);
+addr reallocarray(addr,ulong,ulong);
 void srandom(uint);
 int system(string);
 
@@ -335,6 +346,8 @@ int isatty(int);
 int link(string,string);
 int mkdir(string,octal);
 long read(int, +string[retval], ulong);
+long pread(int,+string[retval],ulong,long);
+long pread64(int,+string[retval],ulong,llong);
 int rmdir(string);
 int seteuid(uint);
 int setgid(int);
@@ -350,11 +363,27 @@ string ttyname(int);
 int unlink(string);
 void usleep(uint);
 long write(int, string3, ulong);
+long pwrite(int,string3,ulong,long);
+long pwrite64(int,string3,ulong,llong);
 addr sbrk(long);
 int getpagesize();
-long lseek(int,long,int);
+long lseek(int,long,lseek_whence);
+long lseek64(int,llong,lseek_whence);
 int pipe(addr);
 
+; sys/uio.h
+typedef iovec = struct(string[elt2],ulong);
+long readv(int,+array(iovec,arg3),int);
+long writev(int,array(iovec,arg3),int);
+long preadv(int,+array(iovec,arg3),int,long);
+long pwritev(int,array(iovec,arg3),int,long);
+long preadv64(int,+array(iovec,arg3),int,llong);
+long pwritev64(int,array(iovec,arg3),int,llong);
+long preadv2(int,+array(iovec,arg3),int,long,hex(int));
+long pwritev2(int,array(iovec,arg3),int,long,hex(int));
+long preadv64v2(int,+array(iovec,arg3),int,llong,hex(int));
+long pwritev64v2(int,array(iovec,arg3),int,llong,hex(int));
+
 ; utmp.h
 void endutent();
 addr getutent();
@@ -414,6 +443,9 @@ addr acl_from_mode(octal);
 int acl_get_perm(addr,uint);
 string acl_to_any_text(addr,string,char,int);
 
+; cxxabi.h
+void __cxa_finalize(addr);
+
 ; other symbols not included above
 long a64l(string);
 string l64a(long);
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz>
Date: Wed, 26 Jul 2023 02:55:43 +0200
Subject: Remove extraneous whitespace around [] and {}

Compare
pread64(3, "14044\n14045\n14046\n14047\n14048\n14"..., 65536, 73152)                                      = 65536
memmove(0x5769c1a4, "6\n", 2)                                                                             = 0x5769c1a4
memcpy(0x5769c1a0, "2496", 4)                                                                             = 0x5769c1a0
writev(1, [ { "24966\n", 6 }, { "24965\n", 6 }, { "24964\n", 6 }, { "24963\n", 6 }... ], 1024)            = 6144
writev(1, [ { "23942\n", 6 }, { "23941\n", 6 }, { "23940\n", 6 }, { "23939\n", 6 }... ], 1024)            = 6144
writev(1, [ { "22918\n", 6 }, { "22917\n", 6 }, { "22916\n", 6 }, { "22915\n", 6 }... ], 1024)            = 6144
writev(1, [ { "21894\n", 6 }, { "21893\n", 6 }, { "21892\n", 6 }, { "21891\n", 6 }... ], 1024)            = 6144
writev(1, [ { "20870\n", 6 }, { "20869\n", 6 }, { "20868\n", 6 }, { "20867\n", 6 }... ], 1024)            = 6144
writev(1, [ { "19846\n", 6 }, { "19845\n", 6 }, { "19844\n", 6 }, { "19843\n", 6 }... ], 1024)            = 6144
writev(1, [ { "18822\n", 6 }, { "18821\n", 6 }, { "18820\n", 6 }, { "18819\n", 6 }... ], 1024)            = 6144
writev(1, [ { "17798\n", 6 }, { "17797\n", 6 }, { "17796\n", 6 }, { "17795\n", 6 }... ], 1024)            = 6144
writev(1, [ { "16774\n", 6 }, { "16773\n", 6 }, { "16772\n", 6 }, { "16771\n", 6 }... ], 1024)            = 6144
writev(1, [ { "15750\n", 6 }, { "15749\n", 6 }, { "15748\n", 6 }, { "15747\n", 6 }... ], 1024)            = 6144
writev(1, [ { "14726\n", 6 }, { "14725\n", 6 }, { "14724\n", 6 }, { "14723\n", 6 }... ], 682)             = 4092
memcpy(0x5769c1a0, "14044\n", 6)                                                                          = 0x5769c1a0
pread64(3, "5\n1746\n1747\n1748\n1749\n1750\n1751\n"..., 65536, 7616)                                     = 65536
writev(1, [ { "14044\n", 6 }, { "14043\n", 6 }, { "14042\n", 6 }, { "14041\n", 6 }... ], 1024)            = 6144
writev(1, [ { "13020\n", 6 }, { "13019\n", 6 }, { "13018\n", 6 }, { "13017\n", 6 }... ], 1024)            = 6144
writev(1, [ { "11996\n", 6 }, { "11995\n", 6 }, { "11994\n", 6 }, { "11993\n", 6 }... ], 1024)            = 6144
writev(1, [ { "10972\n", 6 }, { "10971\n", 6 }, { "10970\n", 6 }, { "10969\n", 6 }... ], 1024)            = 6093
writev(1, [ { "9948\n", 5 }, { "9947\n", 5 }, { "9946\n", 5 }, { "9945\n", 5 }... ], 1024)                = 5120
writev(1, [ { "8924\n", 5 }, { "8923\n", 5 }, { "8922\n", 5 }, { "8921\n", 5 }... ], 1024)                = 5120
writev(1, [ { "7900\n", 5 }, { "7899\n", 5 }, { "7898\n", 5 }, { "7897\n", 5 }... ], 1024)                = 5120
writev(1, [ { "6876\n", 5 }, { "6875\n", 5 }, { "6874\n", 5 }, { "6873\n", 5 }... ], 1024)                = 5120
writev(1, [ { "5852\n", 5 }, { "5851\n", 5 }, { "5850\n", 5 }, { "5849\n", 5 }... ], 1024)                = 5120
writev(1, [ { "4828\n", 5 }, { "4827\n", 5 }, { "4826\n", 5 }, { "4825\n", 5 }... ], 1024)                = 5120
writev(1, [ { "3804\n", 5 }, { "3803\n", 5 }, { "3802\n", 5 }, { "3801\n", 5 }... ], 1024)                = 5120
writev(1, [ { "2780\n", 5 }, { "2779\n", 5 }, { "2778\n", 5 }, { "2777\n", 5 }... ], 1024)                = 5120
writev(1, [ { "1756\n", 5 }, { "1755\n", 5 }, { "1754\n", 5 }, { "1753\n", 5 }... ], 11)                  = 55
memcpy(0x5769c1a0, "5\n", 2)                                                                              = 0x5769c1a0
pread64(3, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14"..., 7616, 0)                                   = 7616
memmove(0x5769c1a3, "5\n", 2)                                                                             = 0x5769c1a3
memcpy(0x5769c1a0, "174", 3)                                                                              = 0x5769c1a0
writev(1, [ { "1745\n", 5 }, { "1744\n", 5 }, { "1743\n", 5 }, { "1742\n", 5 }... ], 1024)                = 4842
writev(1, [ { "721\n", 4 }, { "720\n", 4 }, { "719\n", 4 }, { "718\n", 4 }... ], 720)                     = 2774
memcpy(0x5769c1a0, "1\n", 2)                                                                              = 0x5769c1a0
writev(1, [ { "1\n", 2 } ], 1)                                                                            = 2
close(3)                                                                                                  = 0

To
pread64(3, "14044\n14045\n14046\n14047\n14048\n14"..., 65536, 73152)                                      = 65536
memmove(0x574c41a4, "6\n", 2)                                                                             = 0x574c41a4
memcpy(0x574c41a0, "2496", 4)                                                                             = 0x574c41a0
writev(1, [{"24966\n", 6}, {"24965\n", 6}, {"24964\n", 6}, {"24963\n", 6}...], 1024)                      = 6144
writev(1, [{"23942\n", 6}, {"23941\n", 6}, {"23940\n", 6}, {"23939\n", 6}...], 1024)                      = 6144
writev(1, [{"22918\n", 6}, {"22917\n", 6}, {"22916\n", 6}, {"22915\n", 6}...], 1024)                      = 6144
writev(1, [{"21894\n", 6}, {"21893\n", 6}, {"21892\n", 6}, {"21891\n", 6}...], 1024)                      = 6144
writev(1, [{"20870\n", 6}, {"20869\n", 6}, {"20868\n", 6}, {"20867\n", 6}...], 1024)                      = 6144
writev(1, [{"19846\n", 6}, {"19845\n", 6}, {"19844\n", 6}, {"19843\n", 6}...], 1024)                      = 6144
writev(1, [{"18822\n", 6}, {"18821\n", 6}, {"18820\n", 6}, {"18819\n", 6}...], 1024)                      = 6144
writev(1, [{"17798\n", 6}, {"17797\n", 6}, {"17796\n", 6}, {"17795\n", 6}...], 1024)                      = 6144
writev(1, [{"16774\n", 6}, {"16773\n", 6}, {"16772\n", 6}, {"16771\n", 6}...], 1024)                      = 6144
writev(1, [{"15750\n", 6}, {"15749\n", 6}, {"15748\n", 6}, {"15747\n", 6}...], 1024)                      = 6144
writev(1, [{"14726\n", 6}, {"14725\n", 6}, {"14724\n", 6}, {"14723\n", 6}...], 682)                       = 4092
memcpy(0x574c41a0, "14044\n", 6)                                                                          = 0x574c41a0
pread64(3, "5\n1746\n1747\n1748\n1749\n1750\n1751\n"..., 65536, 7616)                                     = 65536
writev(1, [{"14044\n", 6}, {"14043\n", 6}, {"14042\n", 6}, {"14041\n", 6}...], 1024)                      = 6144
writev(1, [{"13020\n", 6}, {"13019\n", 6}, {"13018\n", 6}, {"13017\n", 6}...], 1024)                      = 6144
writev(1, [{"11996\n", 6}, {"11995\n", 6}, {"11994\n", 6}, {"11993\n", 6}...], 1024)                      = 6144
writev(1, [{"10972\n", 6}, {"10971\n", 6}, {"10970\n", 6}, {"10969\n", 6}...], 1024)                      = 6093
writev(1, [{"9948\n", 5}, {"9947\n", 5}, {"9946\n", 5}, {"9945\n", 5}...], 1024)                          = 5120
writev(1, [{"8924\n", 5}, {"8923\n", 5}, {"8922\n", 5}, {"8921\n", 5}...], 1024)                          = 5120
writev(1, [{"7900\n", 5}, {"7899\n", 5}, {"7898\n", 5}, {"7897\n", 5}...], 1024)                          = 5120
writev(1, [{"6876\n", 5}, {"6875\n", 5}, {"6874\n", 5}, {"6873\n", 5}...], 1024)                          = 5120
writev(1, [{"5852\n", 5}, {"5851\n", 5}, {"5850\n", 5}, {"5849\n", 5}...], 1024)                          = 5120
writev(1, [{"4828\n", 5}, {"4827\n", 5}, {"4826\n", 5}, {"4825\n", 5}...], 1024)                          = 5120
writev(1, [{"3804\n", 5}, {"3803\n", 5}, {"3802\n", 5}, {"3801\n", 5}...], 1024)                          = 5120
writev(1, [{"2780\n", 5}, {"2779\n", 5}, {"2778\n", 5}, {"2777\n", 5}...], 1024)                          = 5120
writev(1, [{"1756\n", 5}, {"1755\n", 5}, {"1754\n", 5}, {"1753\n", 5}...], 11)                            = 55
memcpy(0x574c41a0, "5\n", 2)                                                                              = 0x574c41a0
pread64(3, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14"..., 7616, 0)                                   = 7616
memmove(0x574c41a3, "5\n", 2)                                                                             = 0x574c41a3
memcpy(0x574c41a0, "174", 3)                                                                              = 0x574c41a0
writev(1, [{"1745\n", 5}, {"1744\n", 5}, {"1743\n", 5}, {"1742\n", 5}...], 1024)                          = 4842
writev(1, [{"721\n", 4}, {"720\n", 4}, {"719\n", 4}, {"718\n", 4}...], 720)                               = 2774
memcpy(0x574c41a0, "1\n", 2)                                                                              = 0x574c41a0
writev(1, [{"1\n", 2}], 1)                                                                                = 2
close(3)                                                                                                  = 0
---
 lens_default.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lens_default.c b/lens_default.c
index 2fd4915..71d9584 100644
--- a/lens_default.c
+++ b/lens_default.c
@@ -231,7 +231,7 @@ static int
 format_struct(FILE *stream, struct value *value, struct value_dict *arguments)
 {
 	int written = 0;
-	if (acc_fprintf(&written, stream, "{ ") < 0)
+	if (acc_fprintf(&written, stream, "{") < 0)
 		return -1;
 
 	int need_delim = 0;
@@ -250,7 +250,7 @@ format_struct(FILE *stream, struct value *value, struct value_dict *arguments)
 
 		written += o;
 	}
-	if (acc_fprintf(&written, stream, " }") < 0)
+	if (acc_fprintf(&written, stream, "}") < 0)
 		return -1;
 	return written;
 }
@@ -425,7 +425,7 @@ toplevel_format_lens(struct lens *lens, FILE *stream,
 	case ARGTYPE_ARRAY:
 		return format_array(stream, value, arguments,
 				    value->type->u.array_info.length,
-				    options.arraylen, 1, "[ ", " ]", ", ");
+				    options.arraylen, 1, "[", "]", ", ");
 	}
 	abort();
 }
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz>
Date: Wed, 26 Jul 2023 19:12:16 +0200
Subject: Read larger-than-word buffers with process_vm_readv. Pre-buffer
 arrays of primitives

The first optimisation is obvious: at one point, without it,
ltrace does ptrace(PTRACE_PEEKDATA) /79959 consecutive times/
(thus reading 640kB; imagine how much slower it'd be on
 i686 with double the syscall count and slower syscalls).

The second is necessary because arrays are reified
by reifying each element separately.
Thus, a 64kB string will cause ltrace to PTRACE_PEEKDATA 65`536 times.)

Instead, cooperatively notify the reification module before formatting
an array, and read (up to a megabyte) into a static buffer.
This means that the no-flag and -s9999999999 runs complete without ever
PTRACE_PEEKDATAing a singular character.

The zero() lens would (needlessly) check more than the required
mapping size, which is limited by -A and -s: restrict it to never
try more than the maximum of those + 1, which allows us to speculatively
pre-map the entire prospective NUL-terminated string too.

Evaluation:
The program consists almost exclusively of pread64(64kB)
of seven-byte-lines and writev(1024)s (where each iov is one line)
of the whole read buffer. The input file is 6.6M, of seq 1000000.

$ time out/cmd/tail -r /tmp/1000000 > /dev/null

real    0m0.030s
user    0m0.017s
sys     0m0.013s

165kB of output:
$ time ltrace -o /dev/null -F /etc/ltrace.conf  out/cmd/tail -r /tmp/1000000 > /dev/null

real    0m8.508s
user    0m2.081s
sys     0m6.389s
$ time ~/backports/ltrace/ltrace -o /dev/null -F /etc/ltrace.conf  out/cmd/tail -r /tmp/1000000 > /dev/null

real    0m0.464s
user    0m0.173s
sys     0m0.282s

7.7MB of output:
$ time ltrace -o /dev/null -F /etc/ltrace.conf -s9999999999  out/cmd/tail -r /tmp/1000000 > /dev/null

real    0m17.820s
user    0m5.384s
sys     0m12.395s
$ time ~/backports/ltrace/ltrace -o /dev/null -F /etc/ltrace.conf -s9999999999  out/cmd/tail -r /tmp/1000000 > /dev/null

real    0m2.678s
user    0m2.377s
sys     0m0.265s

24M of output (especially torturous for the reasons mentioned above):
$ time ltrace -o /dev/null -F /etc/ltrace.conf -{A,s}9999999999  out/cmd/tail -r /tmp/1000000 > /dev/null

real    0m37.834s
user    0m12.685s
sys     0m25.027s
$ time ~/backports/ltrace/ltrace -o /dev/null -F /etc/ltrace.conf -{A,s}9999999999  out/cmd/tail -r /tmp/1000000 > /dev/null

real    0m19.512s
user    0m8.392s
sys     0m10.997s
---
 expr.c                    |  6 ++++++
 expr.h                    |  3 +++
 lens_default.c            | 10 +++++++++-
 sysdeps/linux-gnu/trace.c | 29 ++++++++++++++++++++++-------
 value.c                   | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 value.h                   |  8 ++++++++
 zero.c                    |  7 +++++++
 7 files changed, 102 insertions(+), 8 deletions(-)

diff --git a/expr.c b/expr.c
index 01ce4c6..e0215d5 100644
--- a/expr.c
+++ b/expr.c
@@ -178,6 +178,12 @@ expr_is_compile_constant(struct expr_node *node)
 	return node->kind == EXPR_OP_CONST;
 }
 
+int
+expr_is_trivial(struct expr_node *node)
+{
+	return !(node->kind == EXPR_OP_CALL1 || node->kind == EXPR_OP_CALL2);
+}
+
 static int
 eval_up(struct expr_node *node, struct value *context,
 	struct value_dict *arguments, struct value *ret_value)
diff --git a/expr.h b/expr.h
index aa4b0b6..dfd6789 100644
--- a/expr.h
+++ b/expr.h
@@ -150,6 +150,9 @@ int expr_eval_word(struct expr_node *node, struct value *context,
  * things like sizeof or simple expressions might be allowed.  */
 int expr_is_compile_constant(struct expr_node *node);
 
+/* Nonzero if evalutaion won't cause side effects (i.e. not EXPR_OP_CALL*). */
+int expr_is_trivial(struct expr_node *node);
+
 /* Returns a pre-computed expression "self".  */
 struct expr_node *expr_self(void);
 
diff --git a/lens_default.c b/lens_default.c
index 71d9584..767465f 100644
--- a/lens_default.c
+++ b/lens_default.c
@@ -342,14 +342,22 @@ format_array(FILE *stream, struct value *value, struct value_dict *arguments,
 	     struct expr_node *length, size_t maxlen, int before,
 	     const char *open, const char *close, const char *delim)
 {
-	/* We need "long" to be long enough to cover the whole address
+	/* We need "long long" to be long enough to cover the whole address
 	 * space.  */
 	(void)sizeof(char[1 - 2*(sizeof(long long) < sizeof(void *))]);
+
+	int preloaded __attribute__((__cleanup__(value_preload_for_array_flush))) = -1;
+	if(!expr_is_trivial(length))  // zero()
+		preloaded = value_preload_for_array(value, maxlen + 1);
+
 	long long l;
 	if (expr_eval_word(length, value, arguments, &l) < 0)
 		return -1;
 	size_t len = (size_t)l;
 
+	if(preloaded == -1)
+		preloaded = value_preload_for_array(value, len > maxlen ? maxlen : len);
+
 	int written = 0;
 	if (acc_fprintf(&written, stream, "%s", open) < 0)
 		return -1;
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
index e13b761..8fb0fcd 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -21,6 +21,7 @@
  * 02110-1301 USA
  */
 
+#define _GNU_SOURCE
 #include "config.h"
 
 #include <asm/unistd.h>
@@ -31,6 +32,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/uio.h>
 #include <unistd.h>
 
 #ifdef HAVE_LIBSELINUX
@@ -1183,8 +1185,21 @@ umovebytes(Process *proc, void *addr, void *laddr, size_t len) {
 	int started = 0;
 	size_t offset = 0, bytes_read = 0;
 
+
+	if(len > sizeof(long)) {
+		struct iovec local = {.iov_base = laddr, .iov_len = len};
+		struct iovec remote = {.iov_base = addr, .iov_len = len};
+		ssize_t rd = process_vm_readv(proc->pid, &local, 1, &remote, 1, 0);
+		if(rd != -1) {
+			started = 1;
+			offset = bytes_read = rd;
+		}
+	}
+
 	while (offset < len) {
-		a.a = ptrace(PTRACE_PEEKTEXT, proc->pid, addr + offset, 0);
+		int misalignment = (uintptr_t)(addr + offset) % sizeof(long);
+
+		a.a = ptrace(PTRACE_PEEKTEXT, proc->pid, addr + offset - misalignment, 0);
 		if (a.a == -1 && errno) {
 			if (started && errno == EIO)
 				return bytes_read;
@@ -1193,15 +1208,15 @@ umovebytes(Process *proc, void *addr, void *laddr, size_t len) {
 		}
 		started = 1;
 
-		if (len - offset >= sizeof(long)) {
-			memcpy(laddr + offset, &a.c[0], sizeof(long));
-			bytes_read += sizeof(long);
+		if (len - offset >= sizeof(long) - misalignment) {
+			memcpy(laddr + offset + misalignment, &a.c[0] + misalignment, sizeof(long) - misalignment);
+			bytes_read += sizeof(long) - misalignment;
 		}
 		else {
-			memcpy(laddr + offset, &a.c[0], len - offset);
-			bytes_read += (len - offset);
+			memcpy(laddr + offset + misalignment, &a.c[0] + misalignment, len - offset - misalignment);
+			bytes_read += (len - offset) - misalignment;
 		}
-		offset += sizeof(long);
+		offset += sizeof(long) - misalignment;
 	}
 
 	return bytes_read;
diff --git a/value.c b/value.c
index 3209b9f..1edb0e4 100644
--- a/value.c
+++ b/value.c
@@ -122,6 +122,47 @@ value_in_inferior(struct value *valp, arch_addr_t address)
 	valp->u.address = address;
 }
 
+static char preload_buf[1024 * 1024];
+static arch_addr_t preload_buf_start, preload_buf_end;
+int
+value_preload_for_array(struct value *val, size_t len)
+{
+	if (preload_buf_start || val->where != VAL_LOC_INFERIOR)
+		return 0;
+
+	struct arg_type_info *e_info = type_element(val->type, 0);
+	if (e_info == NULL)
+		return 0;
+	// We only have one preload: use it to map in arrays of primitives (strings. it's strings.)
+	switch(e_info->type) {
+		case ARGTYPE_VOID:
+		case ARGTYPE_ARRAY:
+		case ARGTYPE_STRUCT:
+			return 0;
+		default:
+			break;
+	}
+
+	size_t el_sz = type_sizeof(val->inferior, e_info);
+	if (el_sz == (size_t)-1)
+		return 0;
+
+	if (__builtin_mul_overflow(len, el_sz, &len) || len > sizeof(preload_buf))
+		len = sizeof(preload_buf);
+	size_t rd = umovebytes(val->inferior, val->u.inf_address, preload_buf, len);
+	if(rd == (size_t)-1)
+		return 0;
+	preload_buf_start = val->u.inf_address;
+	preload_buf_end = val->u.inf_address + rd;
+	return 1;
+}
+void
+value_preload_for_array_flush(int *preloaded)
+{
+	if (*preloaded && *preloaded != -1)
+		preload_buf_start = preload_buf_end = 0;
+}
+
 int
 value_reify(struct value *val, struct value_dict *arguments)
 {
@@ -145,12 +186,18 @@ value_reify(struct value *val, struct value_dict *arguments)
 		nloc = VAL_LOC_COPY;
 	}
 
+	if (val->u.inf_address >= preload_buf_start && val->u.inf_address + size <= preload_buf_end) {
+		memcpy(data, preload_buf + (val->u.inf_address - preload_buf_start), size);
+		goto ok;
+	}
+
 	if (umovebytes(val->inferior, val->u.inf_address, data, size) < size) {
 		if (nloc == VAL_LOC_COPY)
 			free(data);
 		return -1;
 	}
 
+ok:
 	val->where = nloc;
 	if (nloc == VAL_LOC_COPY)
 		val->u.address = data;
diff --git a/value.h b/value.h
index a527667..e6edabd 100644
--- a/value.h
+++ b/value.h
@@ -113,6 +113,14 @@ int value_init_element(struct value *ret_val, struct value *valp, size_t element
  * RET_VAL.  Returns 0 on success, or negative value on failure.  */
 int value_init_deref(struct value *ret_val, struct value *valp);
 
+/* Pre-read the contents of an array of max length len from the tracee.
+ * Optional optimisation. */
+int value_preload_for_array(struct value *val, size_t len);
+
+/* Must point to return value from value_preload_for_array() or -1.
+ * Suitable for use as __attribute__((__cleanup__(value_preload_for_array_flush))). */
+void value_preload_for_array_flush(int *preloaded);
+
 /* If value is in inferior, copy it over to ltrace.  Return 0 for
  * success or negative value for failure.  */
 int value_reify(struct value *val, struct value_dict *arguments);
diff --git a/zero.c b/zero.c
index ddb5e0d..a249086 100644
--- a/zero.c
+++ b/zero.c
@@ -32,6 +32,13 @@ zero_callback_max(struct value *ret_value, struct value *lhs,
 		  struct value_dict *arguments,
 		  size_t max, void *data)
 {
+	static size_t maxlen;
+	if(!maxlen) {
+		maxlen = options.strlen > options.arraylen ? options.strlen : options.arraylen;
+		if(maxlen != (size_t)-1)
+			++maxlen;
+	}
+	max = max < maxlen ? max : maxlen;
 	size_t i;
 	for (i = 0; i < max; ++i) {
 		struct value element;
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz>
Date: Thu, 27 Jul 2023 05:40:14 +0200
Subject: fix misalignment handling. idk why i wrote it like that but it
 breaks printf

---
 sysdeps/linux-gnu/trace.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
index 8fb0fcd..33ebdf2 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -1209,12 +1209,12 @@ umovebytes(Process *proc, void *addr, void *laddr, size_t len) {
 		started = 1;
 
 		if (len - offset >= sizeof(long) - misalignment) {
-			memcpy(laddr + offset + misalignment, &a.c[0] + misalignment, sizeof(long) - misalignment);
+			memcpy(laddr + offset, &a.c[0] + misalignment, sizeof(long) - misalignment);
 			bytes_read += sizeof(long) - misalignment;
 		}
 		else {
-			memcpy(laddr + offset + misalignment, &a.c[0] + misalignment, len - offset - misalignment);
-			bytes_read += (len - offset) - misalignment;
+			memcpy(laddr + offset, &a.c[0] + misalignment, len - offset);
+			bytes_read += len - offset;
 		}
 		offset += sizeof(long) - misalignment;
 	}
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz>
Date: Thu, 27 Jul 2023 05:59:19 +0200
Subject: Handle format %b. Add bin() lens

---
 lens_default.c     | 21 +++++++++++++++++++++
 lens_default.h     |  3 +++
 ltrace.conf.5      |  3 +++
 printf.c           |  4 ++++
 read_config_file.c |  1 +
 5 files changed, 32 insertions(+)

diff --git a/lens_default.c b/lens_default.c
index 767465f..e381b50 100644
--- a/lens_default.c
+++ b/lens_default.c
@@ -56,6 +56,12 @@ READER(read_double, double)
 
 #undef READER
 
+#ifndef PRIb64  // Underlying format supported by bookworm glibc, but macros not exposed until 2.38
+#ifndef __PRI64_PREFIX
+#define __PRI64_PREFIX "ll"
+#endif
+#define PRIb64 __PRI64_PREFIX "b"
+#endif
 #define HANDLE_WIDTH(BITS)						\
 	do {								\
 		long long l;							\
@@ -75,6 +81,8 @@ READER(read_double, double)
 			return fprintf(stream, "%"PRIu64, v);		\
 		case INT_FMT_o:						\
 			return fprintf(stream, "0%"PRIo64, v);		\
+		case INT_FMT_b:						\
+			return fprintf(stream, "%#"PRIb64, v);		\
 		}							\
 	} while (0)
 
@@ -84,6 +92,7 @@ enum int_fmt_t
 	INT_FMT_u,
 	INT_FMT_o,
 	INT_FMT_x,
+	INT_FMT_b,
 	INT_FMT_unknown,
 	INT_FMT_default,
 };
@@ -463,6 +472,18 @@ struct lens blind_lens = {
 };
 
 
+static int
+binary_lens_format_cb(struct lens *lens, FILE *stream,
+		     struct value *value, struct value_dict *arguments)
+{
+	return toplevel_format_lens(lens, stream, value, arguments, INT_FMT_b);
+}
+
+struct lens binary_lens = {
+	.format_cb = binary_lens_format_cb,
+};
+
+
 static int
 octal_lens_format_cb(struct lens *lens, FILE *stream,
 		     struct value *value, struct value_dict *arguments)
diff --git a/lens_default.h b/lens_default.h
index 1206ffc..66fee66 100644
--- a/lens_default.h
+++ b/lens_default.h
@@ -29,6 +29,9 @@ extern struct lens default_lens;
 /* A lens that doesn't output anything.  */
 extern struct lens blind_lens;
 
+/* A lens that formats integers in binary.  */
+extern struct lens binary_lens;
+
 /* A lens that formats integers in octal.  */
 extern struct lens octal_lens;
 
diff --git a/ltrace.conf.5 b/ltrace.conf.5
index 957fe8b..01a2132 100644
--- a/ltrace.conf.5
+++ b/ltrace.conf.5
@@ -122,6 +122,9 @@ .SH LENSES
 Ltrace understands the following lenses:
 
 .TP
+.B bin(\fITYPE\fB)
+The argument, which should be an integer type, is formatted in base-2.
+
 .B oct(\fITYPE\fB)
 The argument, which should be an integer type, is formatted in base-8.
 
diff --git a/printf.c b/printf.c
index 773d104..860e445 100644
--- a/printf.c
+++ b/printf.c
@@ -264,6 +264,10 @@ param_printf_next(struct param_enum *self, struct arg_type_info *infop,
 			self->percent = 0;
 			break;
 
+		case 'b':
+			lens = &binary_lens;
+			goto uint;
+
 		case 'o':
 			lens = &octal_lens;
 			goto uint;
diff --git a/read_config_file.c b/read_config_file.c
index 98278ac..6fa3c21 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -963,6 +963,7 @@ static struct named_lens {
 	struct lens *lens;
 } lenses[] = {
 	{ "hide", &blind_lens },
+	{ "bin", &binary_lens },
 	{ "octal", &octal_lens },
 	{ "oct", &octal_lens },
 	{ "bitvec", &bitvect_lens },
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz>
Date: Thu, 27 Jul 2023 15:34:25 +0200
Subject: Add %w[f]{WIDTH}x (glibc 2.38, C2x)

This even lets you trace programs that your libc doesn't have support
for! (By accident.)

  $ cat qwe.c
  int main() {
          printf("%#b\n", 69);
          printf("%w64x %s\n", "gameing", (char *)0);
          write(1, "zupa\n", 5);
  
  }
  $ ./ltrace -F ./ltrace.conf ./qwe  | tail
  printf("%#b\n", 0b1000101)                = 10
  printf("%w64x %s\n", 0x563ab86eb013, nil) = 14
  write(1, "zupa\n", 0b101)                 = 5
  +++ exited (status 0) +++
  zupa
  0b1000101
  %w64x gameing
---
 printf.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 56 insertions(+), 5 deletions(-)

diff --git a/printf.c b/printf.c
index 860e445..a9b90cd 100644
--- a/printf.c
+++ b/printf.c
@@ -23,6 +23,7 @@
 
 #include <assert.h>
 #include <stdlib.h>
+#include <stdint.h>
 
 #include "printf.h"
 #include "type.h"
@@ -188,6 +189,7 @@ param_printf_next(struct param_enum *self, struct arg_type_info *infop,
 	char len_buf[25] = {};
 	size_t len_buf_len = 0;
 	struct lens *lens = NULL;
+	_Bool digital_width = 0, digital_width_fast = 0;
 
 	for (; self->ptr < self->end; ++self->ptr) {
 		if (!self->percent) {
@@ -223,11 +225,49 @@ param_printf_next(struct param_enum *self, struct arg_type_info *infop,
 		case '1': case '2': case '3':
 		case '4': case '5': case '6':
 		case '7': case '8': case '9':
-			/* Field length likewise, but we need to parse
-			 * this to attach the appropriate string
-			 * length expression.  */
-			if (len_buf_len < sizeof(len_buf) - 1)
-				len_buf[len_buf_len++] = *self->ptr;
+			if(!digital_width) {
+				/* Field length likewise, but we need to parse
+				 * this to attach the appropriate string
+				 * length expression.  */
+				if (len_buf_len < sizeof(len_buf) - 1)
+					len_buf[len_buf_len++] = *self->ptr;
+			} else {
+				/* %w32d */
+				digital_width = 0;
+				int b;
+				switch(strtoul(self->ptr, (char **)&self->ptr, 10)) {
+#define W_OR_WF(bits)                                               \
+	case bits:                                                  \
+		--self->ptr;                                        \
+		b = digital_width_fast ? sizeof(int_fast##bits##_t) \
+				       : sizeof(int##bits##_t);     \
+		break
+				W_OR_WF(8);
+				W_OR_WF(16);
+				W_OR_WF(32);
+				W_OR_WF(64);
+				default:
+					goto err;
+				}
+				switch(b) {
+					case 1:
+						hlf = 2;
+						lng = 0;
+						break;
+					case 2:
+						hlf = 1;
+						lng = 0;
+						break;
+					case 4:
+						hlf = 0;
+						lng = 0;
+						break;
+					case 8:
+						hlf = 0;
+						lng = 2;
+						break;
+				}
+			}
 			continue;
 
 		case 'h':
@@ -258,6 +298,16 @@ param_printf_next(struct param_enum *self, struct arg_type_info *infop,
 			lng = 1; /* XXX ABI should tell */
 			continue;
 
+		case 'w':
+			++self->ptr;
+			digital_width = 1;
+			if(self->ptr >= self->end || *self->ptr != 'f') {
+				--self->ptr;
+				digital_width_fast = 0;
+			} else
+				digital_width_fast = 1;
+			continue;
+
 		case 'd':
 		case 'i':
 			format_type = ARGTYPE_INT;
@@ -339,6 +389,7 @@ param_printf_next(struct param_enum *self, struct arg_type_info *infop,
 		return 0;
 	}
 
+err:
 	*infop = *type_get_simple(ARGTYPE_VOID);
 	return 0;
 }
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz>
Date: Tue, 1 Aug 2023 17:09:11 +0200
Subject: Add splice()/copy_file_range()/sendfile[64]()

Before:
splice(3, 0x7ffd4b66f348, 1, 0)                = -1
copy_file_range(3, 0x7ffd772bbf28, 1, 0)       = -1
sendfile64(1, 3, 0x7ffd772bbf28, 0x400000)     = -1
pread64(3 <no return ...>
error: maximum array length seems negative
, "", 65536, -65536)                           = -1

After:
splice(3, -65536, 1, nil, 4194304, 0b101)      = -1
copy_file_range(3, -65536, 1, nil, 4194304, 0) = -1
sendfile64(1, 3, -65536, 4194304)              = -1
pread64(3 <no return ...>
error: maximum array length seems negative
, "", 65536, -65536)                           = -1
---
 etc/ltrace.conf | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/etc/ltrace.conf b/etc/ltrace.conf
index 855d373..db914cb 100644
--- a/etc/ltrace.conf
+++ b/etc/ltrace.conf
@@ -60,6 +60,12 @@ int open(string,hex(int),octal);		; WARNING: 3rd argument may not be there
 int open64(string,hex(int),octal);		; WARNING: 3rd argument may not be there
 int openat(openat_fd,string,hex(int),octal);	; WARNING: 4th argument may not be there
 int openat64(openat_fd,string,hex(int),octal);	; WARNING: 4th argument may not be there
+long splice(int,llong*,int,llong*,ulong,bin(uint))
+; unistd.h
+long copy_file_range(int,llong*,int,llong*,ulong,bin(uint))
+; sys/sendfile.h
+long sendfile(int,int,long*,ulong)
+long sendfile64(int,int,llong*,ulong)
 
 ; fnmatch.h
 int fnmatch(string, string, int);
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz>
Date: Tue, 1 Aug 2023 17:30:15 +0200
Subject: __errno_location() returns an int*, not addr

This means that before:
__errno_location() = 0x7f2707f256c0
__errno_location() = 0x7f2707f256c0
__errno_location() = 0x7f2707f256c0

After:
__errno_location() = 0
__errno_location() = 22
__errno_location() = 22

And this __errno_location() is made useful
---
 etc/ltrace.conf | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/etc/ltrace.conf b/etc/ltrace.conf
index db914cb..3d4f5cd 100644
--- a/etc/ltrace.conf
+++ b/etc/ltrace.conf
@@ -52,7 +52,7 @@ addr  dlsym(addr, string);
 int dlclose(addr);
 
 ; errno.h
-addr __errno_location();
+int* __errno_location();
 
 ; fcntl.h
 typedef openat_fd = enum(AT_FDCWD=-100);
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz>
Date: Thu, 3 Aug 2023 16:47:06 +0200
Subject: Fix config lines being truncated to 1kB

---
 read_config_file.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/read_config_file.c b/read_config_file.c
index 6fa3c21..561351d 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -1278,7 +1278,8 @@ init_global_config(void)
 void
 read_config_file(char *file) {
 	FILE *stream;
-	char buf[1024];
+	char *line = NULL;
+	size_t linecap = 0;
 
 	filename = file;
 	stream = fopen(filename, "r");
@@ -1289,10 +1290,10 @@ read_config_file(char *file) {
 	debug(1, "Reading config file `%s'...", filename);
 
 	line_no = 0;
-	while (fgets(buf, 1024, stream)) {
+	while (getline(&line, &linecap, stream) != -1) {
 		Function *tmp;
 
-		tmp = process_line(buf);
+		tmp = process_line(line);
 
 		if (tmp) {
 			debug(2, "New function: `%s'", tmp->name);
@@ -1300,5 +1301,6 @@ read_config_file(char *file) {
 			list_of_functions = tmp;
 		}
 	}
+	free(line);
 	fclose(stream);
 }
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz>
Date: Thu, 3 Aug 2023 17:08:40 +0200
Subject: pipe[2]() with fds; sysconf(); m[un]map[64](); operator new/delete;
 regex.h suite

$ grep pipe ll
pipe2(0x7ffcd6d7baf0, 0x80000, 0x7ffcd6d7bb70, 0x7ffcd6d7bb70)                       = 0
$ grep pipe ll
pipe2([3, 4], 0x80000)                                                               = 0

nabijaczleweli@tarta:~/backports/ltrace$ grep -e mmap -e sysconf ll
sysconf(30, 0, 1, 0x393c)                                                            = 4096
mmap64(0, 0x3d3c, 1, 2)                                                              = 0x7fe67b29c000
nabijaczleweli@tarta:~/backports/ltrace$ grep -e mmap -e sysconf ll
sysconf(_SC_PAGE_SIZE)                                                               = 4096
mmap64(0, 15676, 0b1, 0x2, 0, 4096)                                                  = 0x7fa1b27ff000

regcomp(0x7fff5ee83120, ";", 0b100)                                                  = REG_OK

regexec(0x7fff5ee83040, "\nint   SYS_access(string,octal);"..., 1, [{0, 31}], 0b111) = REG_NOMATCH
regexec(0x7fff5ee83040, ";\nint   SYS_access(string,octal)"..., 1, [{0, 32}], 0b111) = REG_OK

regcomp(0x7ffece59ee70, "\\(", 0b100)                                                = REG_EPAREN
regerror(REG_EPAREN, 0x7ffece59ee70, nil, 0)                                         = 18
malloc(18)                                                                           = 0x5604c35c2140
regerror(REG_EPAREN, 0x7ffece59ee70, "Unmatched ( or \\(", 18)                       = 18
---
 etc/ltrace.conf | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/etc/ltrace.conf b/etc/ltrace.conf
index 3d4f5cd..ad5a1a4 100644
--- a/etc/ltrace.conf
+++ b/etc/ltrace.conf
@@ -375,7 +375,9 @@ addr sbrk(long);
 int getpagesize();
 long lseek(int,long,lseek_whence);
 long lseek64(int,llong,lseek_whence);
-int pipe(addr);
+int pipe(+array(int,2));
+int pipe2(+array(int,2),hex(int));
+long sysconf(enum(_SC_ARG_MAX=0, _SC_CHILD_MAX=1, _SC_CLK_TCK=2, _SC_NGROUPS_MAX=3, _SC_OPEN_MAX=4, _SC_STREAM_MAX=5, _SC_TZNAME_MAX=6, _SC_JOB_CONTROL=7, _SC_SAVED_IDS=8, _SC_REALTIME_SIGNALS=9, _SC_PRIORITY_SCHEDULING=10, _SC_TIMERS=11, _SC_ASYNCHRONOUS_IO=12, _SC_PRIORITIZED_IO=13, _SC_SYNCHRONIZED_IO=14, _SC_FSYNC=15, _SC_MAPPED_FILES=16, _SC_MEMLOCK=17, _SC_MEMLOCK_RANGE=18, _SC_MEMORY_PROTECTION=19, _SC_MESSAGE_PASSING=20, _SC_SEMAPHORES=21, _SC_SHARED_MEMORY_OBJECTS=22, _SC_AIO_LISTIO_MAX=23, _SC_AIO_MAX=24, _SC_AIO_PRIO_DELTA_MAX=25, _SC_DELAYTIMER_MAX=26, _SC_MQ_OPEN_MAX=27, _SC_MQ_PRIO_MAX=28, _SC_VERSION=29, _SC_PAGE_SIZE=30, _SC_PAGESIZE=30, _SC_RTSIG_MAX=31, _SC_SEM_NSEMS_MAX=32, _SC_SEM_VALUE_MAX=33, _SC_SIGQUEUE_MAX=34, _SC_TIMER_MAX=35, _SC_BC_BASE_MAX=36, _SC_BC_DIM_MAX=37, _SC_BC_SCALE_MAX=38, _SC_BC_STRING_MAX=39, _SC_COLL_WEIGHTS_MAX=40, _SC_EXPR_NEST_MAX=42, _SC_LINE_MAX=43, _SC_RE_DUP_MAX=44, _SC_2_VERSION=46, _SC_2_C_BIND=47, _SC_2_C_DEV=48, _SC_2_FORT_DEV=49, _SC_2_FORT_RUN=50, _SC_2_SW_DEV=51, _SC_2_LOCALEDEF=52, _SC_UIO_MAXIOV=60, _SC_IOV_MAX=60, _SC_THREADS=67, _SC_THREAD_SAFE_FUNCTIONS=68, _SC_GETGR_R_SIZE_MAX=69, _SC_GETPW_R_SIZE_MAX=70, _SC_LOGIN_NAME_MAX=71, _SC_TTY_NAME_MAX=72, _SC_THREAD_DESTRUCTOR_ITERATIONS=73, _SC_THREAD_KEYS_MAX=74, _SC_THREAD_STACK_MIN=75, _SC_THREAD_THREADS_MAX=76, _SC_THREAD_ATTR_STACKADDR=77, _SC_THREAD_ATTR_STACKSIZE=78, _SC_THREAD_PRIORITY_SCHEDULING=79, _SC_THREAD_PRIO_INHERIT=80, _SC_THREAD_PRIO_PROTECT=81, _SC_THREAD_PROCESS_SHARED=82, _SC_NPROCESSORS_CONF=83, _SC_NPROCESSORS_ONLN=84, _SC_PHYS_PAGES=85, _SC_AVPHYS_PAGES=86, _SC_ATEXIT_MAX=87, _SC_PASS_MAX=88, _SC_XOPEN_VERSION=89, _SC_XOPEN_XCU_VERSION=90, _SC_XOPEN_UNIX=91, _SC_XOPEN_CRYPT=92, _SC_XOPEN_ENH_I18N=93, _SC_XOPEN_SHM=94, _SC_2_CHAR_TERM=95, _SC_2_UPE=97, _SC_XOPEN_XPG2=98, _SC_XOPEN_XPG3=99, _SC_XOPEN_XPG4=100, _SC_NZERO=109, _SC_XBS5_ILP32_OFF32=125, _SC_XBS5_ILP32_OFFBIG=126, _SC_XBS5_LP64_OFF64=127, _SC_XBS5_LPBIG_OFFBIG=128, _SC_XOPEN_LEGACY=129, _SC_XOPEN_REALTIME=130, _SC_XOPEN_REALTIME_THREADS=131, _SC_ADVISORY_INFO=132, _SC_BARRIERS=133, _SC_CLOCK_SELECTION=137, _SC_CPUTIME=138, _SC_THREAD_CPUTIME=139, _SC_MONOTONIC_CLOCK=149, _SC_READER_WRITER_LOCKS=153, _SC_SPIN_LOCKS=154, _SC_REGEXP=155, _SC_SHELL=157, _SC_SPAWN=159, _SC_SPORADIC_SERVER=160, _SC_THREAD_SPORADIC_SERVER=161, _SC_TIMEOUTS=164, _SC_TYPED_MEMORY_OBJECTS=165, _SC_2_PBS=168, _SC_2_PBS_ACCOUNTING=169, _SC_2_PBS_LOCATE=170, _SC_2_PBS_MESSAGE=171, _SC_2_PBS_TRACK=172, _SC_SYMLOOP_MAX=173, _SC_STREAMS=174, _SC_2_PBS_CHECKPOINT=175, _SC_V6_ILP32_OFF32=176, _SC_V6_ILP32_OFFBIG=177, _SC_V6_LP64_OFF64=178, _SC_V6_LPBIG_OFFBIG=179, _SC_HOST_NAME_MAX=180, _SC_TRACE=181, _SC_TRACE_EVENT_FILTER=182, _SC_TRACE_INHERIT=183, _SC_TRACE_LOG=184, _SC_IPV6=235, _SC_RAW_SOCKETS=236, _SC_V7_ILP32_OFF32=237, _SC_V7_ILP32_OFFBIG=238, _SC_V7_LP64_OFF64=239, _SC_V7_LPBIG_OFFBIG=240, _SC_SS_REPL_MAX=241, _SC_TRACE_EVENT_NAME_MAX=242, _SC_TRACE_NAME_MAX=243, _SC_TRACE_SYS_MAX=244, _SC_TRACE_USER_EVENT_MAX=245, _SC_XOPEN_STREAMS=246, _SC_THREAD_ROBUST_PRIO_INHERIT=247, _SC_THREAD_ROBUST_PRIO_PROTECT=248, _SC_MINSIGSTKSZ=249, _SC_SIGSTKSZ=250))
 
 ; sys/uio.h
 typedef iovec = struct(string[elt2],ulong);
@@ -449,9 +451,23 @@ addr acl_from_mode(octal);
 int acl_get_perm(addr,uint);
 string acl_to_any_text(addr,string,char,int);
 
+; sys/mman.h
+addr mmap(addr,ulong,bin(int),hex(int),int,long);
+addr mmap64(addr,ulong,bin(int),hex(int),int,llong);
+int munmap(addr, ulong);
+
 ; cxxabi.h
+addr _Znwm(ulong);  operator new
+void _ZdlPv(addr);  operator delete
 void __cxa_finalize(addr);
 
+; regex.h
+typedef regmatch_t = struct(int,int);  broken, needs to be long or llong in glibc!
+typedef regerror_t = enum(REG_OK=0,REG_NOMATCH,REG_BADPAT,REG_ECOLLATE,REG_ECTYPE,REG_EESCAPE,REG_ESUBREG,REG_EBRACK,REG_EPAREN,REG_EBRACE,REG_BADBR,REG_ERANGE,REG_ESPACE,REG_BADRPT,REG_EEND,REG_ESIZE,REG_ERPAREN);
+regerror_t regcomp(addr,string,bin(int));
+regerror_t regexec(addr,string,ulong,array(regmatch_t,arg3),bin(int));
+ulong regerror(regerror_t,addr,+string0,ulong);
+
 ; other symbols not included above
 long a64l(string);
 string l64a(long);
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz>
Date: Thu, 3 Aug 2023 17:51:32 +0200
Subject: Don't malloc file/addr typedefs. Turn opt_p into an array. Just read
 -Fs as they're encountered. Open -o "we" instead of "w" then setting
 O_CLOEXEC manually. Put default_prototype in .bss instead of allocating for
 it thrice

Instead of storing them in a list, linking them, then killing the list.
And strdup()ing the paths

This also means you can syntax/typecheck your config without actually
running anything
---
 common.h                  |   3 +-
 libltrace.c               |  67 +++++++++++---------------
 options.c                 |  89 ++++++++++-------------------------
 options.h                 |  22 ++-------
 output.c                  |  56 ++++++----------------
 read_config_file.c        | 117 +++++++++++++++++++++-------------------------
 read_config_file.h        |   3 +-
 sysdeps/linux-gnu/trace.c |  10 ++--
 8 files changed, 128 insertions(+), 239 deletions(-)

diff --git a/common.h b/common.h
index f333f3f..5464d61 100644
--- a/common.h
+++ b/common.h
@@ -28,6 +28,7 @@
 #include <sys/types.h>
 #include <sys/time.h>
 #include <stdio.h>
+#include <stdbool.h>
 
 #include "ltrace.h"
 #include "defs.h"
@@ -55,9 +56,9 @@ struct Function {
 	const char * name;
 	struct param *params;
 	struct arg_type_info *return_info;
-	int own_return_info;
 	size_t num_params;
 	Function * next;
+	bool own_return_info;
 };
 
 extern Function * list_of_functions;
diff --git a/libltrace.c b/libltrace.c
index 559edfa..873ce98 100644
--- a/libltrace.c
+++ b/libltrace.c
@@ -44,11 +44,10 @@ stop_non_p_processes(Process *proc, void *data)
 {
 	int stop = 1;
 
-	struct opt_p_t *it;
-	for (it = opt_p; it != NULL; it = it->next) {
-		Process * p_proc = pid2proc(it->pid);
+	for (size_t i = 0; i < opt_p_len; ++i) {
+		Process * p_proc = pid2proc(opt_p[i]);
 		if (p_proc == NULL) {
-			printf("stop_non_p_processes: %d terminated?\n", it->pid);
+			printf("stop_non_p_processes: %d terminated?\n", opt_p[i]);
 			continue;
 		}
 		if (p_proc == proc || p_proc->leader == proc->leader) {
@@ -97,42 +96,31 @@ normal_exit(void)
 	}
 }
 
+/* If filename begins with ~, expand it to the user's home */
+/* directory. This does not correctly handle ~yoda, but that */
+/* isn't as bad as it seems because the shell will normally */
+/* be doing the expansion for us; only the hardcoded */
+/* ~/.ltrace.conf should ever use this code. */
+static void
+opt_F_handler(const char *F)
+{
+	char path[PATH_MAX];
+	if (F[0] == '~') {
+		char *home_dir = getenv("HOME");
+		if (home_dir && snprintf(path, sizeof(path), "%s%s", home_dir, F + 1) < (int)sizeof(path))
+			read_config_file(path);
+	} else {
+		read_config_file(F);
+	}
+}
+
 void
 ltrace_init(int argc, char **argv) {
-	struct opt_p_t *opt_p_tmp;
-
 	atexit(normal_exit);
 	signal(SIGINT, signal_exit);	/* Detach processes when interrupted */
 	signal(SIGTERM, signal_exit);	/*  ... or killed */
 
-	argv = process_options(argc, argv);
-	init_global_config();
-	while (opt_F) {
-		/* If filename begins with ~, expand it to the user's home */
-		/* directory. This does not correctly handle ~yoda, but that */
-		/* isn't as bad as it seems because the shell will normally */
-		/* be doing the expansion for us; only the hardcoded */
-		/* ~/.ltrace.conf should ever use this code. */
-		if (opt_F->filename[0] == '~') {
-			char path[PATH_MAX];
-			char *home_dir = getenv("HOME");
-			if (home_dir) {
-				strncpy(path, home_dir, PATH_MAX - 1);
-				path[PATH_MAX - 1] = '\0';
-				strncat(path, opt_F->filename + 1,
-						PATH_MAX - strlen(path) - 1);
-				read_config_file(path);
-			}
-		} else {
-			read_config_file(opt_F->filename);
-		}
-
-		struct opt_F_t *next = opt_F->next;
-		if (opt_F->own_filename)
-			free(opt_F->filename);
-		free(opt_F);
-		opt_F = next;
-	}
+	argv = process_options(argc, argv, opt_F_handler);
 	if (command) {
 		/* Check that the binary ABI is supported before
 		 * calling execute_program.  */
@@ -151,11 +139,8 @@ ltrace_init(int argc, char **argv) {
 		trace_set_options(proc);
 		continue_process(pid);
 	}
-	opt_p_tmp = opt_p;
-	while (opt_p_tmp) {
-		open_pid(opt_p_tmp->pid);
-		opt_p_tmp = opt_p_tmp->next;
-	}
+	for (size_t i = 0; i < opt_p_len; ++i)
+		open_pid(opt_p[i]);
 }
 
 static int num_ltrace_callbacks[EVENT_MAX];
@@ -163,7 +148,7 @@ static callback_func * ltrace_callbacks[EVENT_MAX];
 
 void
 ltrace_add_callback(callback_func func, Event_type type) {
-	ltrace_callbacks[type] = realloc(ltrace_callbacks[type], (num_ltrace_callbacks[type]+1)*sizeof(callback_func));
+	ltrace_callbacks[type] = reallocarray(ltrace_callbacks[type], num_ltrace_callbacks[type] + 1, sizeof(callback_func));
 	ltrace_callbacks[type][num_ltrace_callbacks[type]++] = func;
 }
 
@@ -171,7 +156,7 @@ static void
 dispatch_callbacks(Event * ev) {
 	int i;
 	/* Ignoring case 1: signal into a dying tracer */
-	if (ev->type==EVENT_SIGNAL && 
+	if (ev->type==EVENT_SIGNAL &&
 			exiting && ev->e_un.signum == SIGSTOP) {
 		return;
 	}
diff --git a/options.c b/options.c
index 1e19dc7..f47aef9 100644
--- a/options.c
+++ b/options.c
@@ -63,17 +63,15 @@ struct options_t options = {
 	.follow = 0,                  /* trace child processes */
 };
 
-static char *progname;		/* Program name (`ltrace') */
-int opt_i = 0;			/* instruction pointer */
+static const char *progname;	/* Program name (`ltrace') */
+bool opt_i = false;		/* instruction pointer */
 int opt_r = 0;			/* print relative timestamp */
 int opt_t = 0;			/* print absolute timestamp */
 int opt_T = 0;			/* show the time spent inside each call */
 
 /* List of pids given to option -p: */
-struct opt_p_t *opt_p = NULL;	/* attach to process with a given pid */
-
-/* List of filenames give to option -F: */
-struct opt_F_t *opt_F = NULL;	/* alternate configuration file(s) */
+pid_t *opt_p;	/* attach to process with a given pid */
+size_t opt_p_len;
 
 static void
 err_usage(void) {
@@ -122,7 +120,7 @@ usage(void) {
 static void
 usage_debug(void) {
 	fprintf(stdout, "%s debugging option, --debug=<octal> or -D<octal>:\n", progname);
-	fprintf(stdout, 
+	fprintf(stdout,
 			"\n"
 			" number  ref. in source   description\n"
 			"      1   general           Generally helpful progress information\n"
@@ -443,7 +441,7 @@ parse_int(const char *optarg, char opt, int min, int max)
 }
 
 char **
-process_options(int argc, char **argv)
+process_options(int argc, char **argv, void(*opt_F)(const char *))
 {
 	progname = argv[0];
 	options.output = stderr;
@@ -454,13 +452,14 @@ process_options(int argc, char **argv)
 
 	guess_cols();
 
-	int libcalls = 1;
+	bool opt_F_any = false;
+	bool libcalls = true;
 
 	while (1) {
 		int c;
 		char *p;
 		int option_index = 0;
-		static struct option long_options[] = {
+		static const struct option long_options[] = {
 			{"align", 1, 0, 'a'},
 			{"config", 1, 0, 'F'},
 			{"debug", 1, 0, 'D'},
@@ -529,28 +528,14 @@ process_options(int argc, char **argv)
 			options.follow = 1;
 			break;
 		case 'F':
-			{
-				struct opt_F_t *tmp = malloc(sizeof(*tmp));
-				if (tmp == NULL) {
-				fail:
-					fprintf(stderr, "%s\n",
-						strerror(errno));
-					free(tmp);
-					exit(1);
-				}
-				tmp->filename = strdup(optarg);
-				if (tmp->filename == NULL)
-					goto fail;
-				tmp->own_filename = 1;
-				tmp->next = opt_F;
-				opt_F = tmp;
-				break;
-			}
+			opt_F(optarg);
+			opt_F_any = true;
+			break;
 		case 'h':
 			usage();
 			exit(0);
 		case 'i':
-			opt_i++;
+			opt_i = true;
 			break;
 
 		case 'l': {
@@ -563,34 +548,28 @@ process_options(int argc, char **argv)
 		}
 
 		case 'L':
-			libcalls = 0;
+			libcalls = false;
 			break;
 		case 'n':
 			options.indent = parse_int(optarg, 'n', 0, 20);
 			break;
 		case 'o':
-			options.output = fopen(optarg, "w");
+			options.output = fopen(optarg, "we");
 			if (!options.output) {
 				fprintf(stderr,
 					"can't open %s for writing: %s\n",
 					optarg, strerror(errno));
 				exit(1);
 			}
-			setvbuf(options.output, (char *)NULL, _IOLBF, 0);
-			fcntl(fileno(options.output), F_SETFD, FD_CLOEXEC);
+			setvbuf(options.output, NULL, _IOLBF, 0);
 			break;
 		case 'p':
-			{
-				struct opt_p_t *tmp = malloc(sizeof(struct opt_p_t));
-				if (!tmp) {
-					perror("ltrace: malloc");
-					exit(1);
-				}
-				tmp->pid = parse_int(optarg, 'p', 1, 0);
-				tmp->next = opt_p;
-				opt_p = tmp;
-				break;
+			if (!(opt_p = reallocarray(opt_p, ++opt_p_len, sizeof(*opt_p)))) {
+				perror("ltrace: malloc");
+				exit(1);
 			}
+			opt_p[opt_p_len - 1] = parse_int(optarg, 'p', 1, 0);
+			break;
 		case 'r':
 			opt_r++;
 			break;
@@ -633,29 +612,9 @@ process_options(int argc, char **argv)
 	argc -= optind;
 	argv += optind;
 
-	if (!opt_F) {
-		opt_F = malloc(sizeof(struct opt_F_t));
-		opt_F->next = malloc(sizeof(struct opt_F_t));
-		opt_F->next->next = NULL;
-		opt_F->filename = USER_CONFIG_FILE;
-		opt_F->own_filename = 0;
-		opt_F->next->filename = SYSTEM_CONFIG_FILE;
-		opt_F->next->own_filename = 0;
-	}
-	/* Reverse the config file list since it was built by
-	 * prepending, and it would make more sense to process the
-	 * files in the order they were given. Probably it would make
-	 * more sense to keep a tail pointer instead? */
-	{
-		struct opt_F_t *egg = NULL;
-		struct opt_F_t *chicken;
-		while (opt_F) {
-			chicken = opt_F->next;
-			opt_F->next = egg;
-			egg = opt_F;
-			opt_F = chicken;
-		}
-		opt_F = egg;
+	if (!opt_F_any) {
+		opt_F(SYSTEM_CONFIG_FILE);
+		opt_F(USER_CONFIG_FILE);
 	}
 
 	/* If neither -e, nor -l, nor -L are used, set default -e.
diff --git a/options.h b/options.h
index e1cc823..be862dd 100644
--- a/options.h
+++ b/options.h
@@ -22,6 +22,7 @@
  */
 
 #include <stdio.h>
+#include <stdbool.h>
 #include <sys/types.h>
 
 #include "forward.h"
@@ -53,25 +54,12 @@ struct options_t {
 };
 extern struct options_t options;
 
-extern int opt_i;		/* instruction pointer */
+extern bool opt_i;		/* instruction pointer */
 extern int opt_r;		/* print relative timestamp */
 extern int opt_t;		/* print absolute timestamp */
 extern int opt_T;		/* show the time spent inside each call */
 
-struct opt_p_t {
-	pid_t pid;
-	struct opt_p_t *next;
-};
-
-struct opt_F_t
-{
-	struct opt_F_t *next;
-	char *filename;
-	int own_filename : 1;
-};
-
-extern struct opt_p_t *opt_p;	/* attach to process with a given pid */
-
-extern struct opt_F_t *opt_F;	/* alternate configuration file(s) */
+extern pid_t *opt_p;		/* attach to process with a given pid */
+extern size_t opt_p_len;	/* attach to process with a given pid */
 
-extern char **process_options(int argc, char **argv);
+extern char **process_options(int argc, char **argv, void(*opt_F)(const char *));
diff --git a/output.c b/output.c
index c1eda23..dc0be31 100644
--- a/output.c
+++ b/output.c
@@ -139,55 +139,28 @@ begin_of_line(Process *proc, int is_func, int indent)
 static struct arg_type_info *
 get_unknown_type(void)
 {
-	static struct arg_type_info *info = NULL;
-	if (info == NULL) {
-		info = malloc(sizeof(*info));
-		if (info == NULL) {
-			report_global_error("malloc: %s", strerror(errno));
-			abort();
-		}
-		*info = *type_get_simple(ARGTYPE_LONG);
-		info->lens = &guess_lens;
-	}
-	return info;
+	static struct arg_type_info info;
+	info = *type_get_simple(ARGTYPE_LONG);
+	info.lens = &guess_lens;
+	return &info;
 }
 
 /* The default prototype is: long X(long, long, long, long).  */
-static Function *
-build_default_prototype(void)
+static void
+build_default_prototype(Function *ret)
 {
-	Function *ret = malloc(sizeof(*ret));
-	size_t i = 0;
-	if (ret == NULL)
-		goto err;
-	memset(ret, 0, sizeof(*ret));
+	static typeof(*ret->params) params[4];
 
 	struct arg_type_info *unknown_type = get_unknown_type();
 
 	ret->return_info = unknown_type;
 	ret->own_return_info = 0;
 
-	ret->num_params = 4;
-	ret->params = malloc(sizeof(*ret->params) * ret->num_params);
-	if (ret->params == NULL)
-		goto err;
+	ret->num_params = sizeof(params) / sizeof(*params);
+	ret->params = params;
 
-	for (i = 0; i < ret->num_params; ++i)
+	for (size_t i = 0; i < ret->num_params; ++i)
 		param_init_type(&ret->params[i], unknown_type, 0);
-
-	return ret;
-
-err:
-	report_global_error("malloc: %s", strerror(errno));
-	if (ret->params != NULL) {
-		while (i-- > 0)
-			param_destroy(&ret->params[i]);
-		free(ret->params);
-	}
-
-	free(ret);
-
-	return NULL;
 }
 
 static Function *
@@ -202,11 +175,10 @@ name2func(char const *name) {
 			return tmp;
 	}
 
-	static Function *def = NULL;
-	if (def == NULL)
-		def = build_default_prototype();
-
-	return def;
+	static Function def;
+	if (def.params == NULL)
+		build_default_prototype(&def);
+	return &def;
 }
 
 void
diff --git a/read_config_file.c b/read_config_file.c
index 561351d..bcdf2c8 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -42,20 +42,20 @@
 #include "lens_enum.h"
 
 static int line_no;
-static char *filename;
+static const char *filename;
 struct typedef_node_t;
 
 static struct arg_type_info *parse_nonpointer_type(char **str,
 						   struct param **extra_param,
-						   size_t param_num, int *ownp,
+						   size_t param_num, bool *ownp,
 						   struct typedef_node_t *td);
 static struct arg_type_info *parse_type(char **str, struct param **extra_param,
-					size_t param_num, int *ownp,
+					size_t param_num, bool *ownp,
 					struct typedef_node_t *in_typedef);
 static struct arg_type_info *parse_lens(char **str, struct param **extra_param,
-					size_t param_num, int *ownp,
+					size_t param_num, bool *ownp,
 					struct typedef_node_t *in_typedef);
-static int parse_enum(char **str, struct arg_type_info **retp, int *ownp);
+static int parse_enum(char **str, struct arg_type_info **retp, bool *ownp);
 
 Function *list_of_functions = NULL;
 
@@ -226,15 +226,15 @@ parse_char(char **str, char expected)
 	return 0;
 }
 
-static struct expr_node *parse_argnum(char **str, int *ownp, int zero);
+static struct expr_node *parse_argnum(char **str, bool *ownp, int zero);
 
 static struct expr_node *
-parse_zero(char **str, struct expr_node *ret, int *ownp)
+parse_zero(char **str, struct expr_node *ret, bool *ownp)
 {
 	eat_spaces(str);
 	if (**str == '(') {
 		++*str;
-		int own;
+		bool own;
 		struct expr_node *arg = parse_argnum(str, &own, 0);
 		if (arg == NULL)
 			return NULL;
@@ -248,12 +248,12 @@ parse_zero(char **str, struct expr_node *ret, int *ownp)
 		struct expr_node *ret = build_zero_w_arg(arg, own);
 		if (ret == NULL)
 			goto fail;
-		*ownp = 1;
+		*ownp = true;
 		return ret;
 
 	} else {
 		free(ret);
-		*ownp = 0;
+		*ownp = false;
 		return expr_node_zero();
 	}
 }
@@ -276,7 +276,7 @@ wrap_in_zero(struct expr_node **nodep)
  *  N      : The numeric value N
  */
 static struct expr_node *
-parse_argnum(char **str, int *ownp, int zero)
+parse_argnum(char **str, bool *ownp, int zero)
 {
 	struct expr_node *expr = malloc(sizeof(*expr));
 	if (expr == NULL)
@@ -294,7 +294,7 @@ parse_argnum(char **str, int *ownp, int zero)
 		if (zero && wrap_in_zero(&expr) < 0)
 			goto fail;
 
-		*ownp = 1;
+		*ownp = true;
 		return expr;
 
 	} else {
@@ -352,7 +352,7 @@ parse_argnum(char **str, int *ownp, int zero)
 			goto fail_ident;
 
 		free(name);
-		*ownp = 1;
+		*ownp = true;
 		return expr;
 	}
 
@@ -364,10 +364,16 @@ fail:
 struct typedef_node_t {
 	char *name;
 	struct arg_type_info *info;
-	int own_type;
-	int forward : 1;
 	struct typedef_node_t *next;
-} *typedefs = NULL;
+	bool own_type;
+	bool forward : 1;
+};
+
+static struct arg_type_info void_pointer[] = {{.type = ARGTYPE_POINTER, .u.ptr_info.info = &void_pointer[1]}, {.type = ARGTYPE_VOID}};
+static struct typedef_node_t file_typedef = {.name = (char *)"file", .info = void_pointer};
+static struct typedef_node_t addr_typedef = {.name = (char *)"addr", .info = void_pointer, .next = &file_typedef};
+static struct typedef_node_t *typedefs = &addr_typedef;
+
 
 static struct typedef_node_t *
 lookup_typedef(const char *name)
@@ -410,13 +416,13 @@ insert_typedef(struct typedef_node_t *td)
 }
 
 static struct typedef_node_t *
-new_typedef(char *name, struct arg_type_info *info, int own_type)
+new_typedef(char *name, struct arg_type_info *info, bool own_type)
 {
 	struct typedef_node_t *binding = malloc(sizeof(*binding));
 	binding->name = name;
 	binding->info = info;
 	binding->own_type = own_type;
-	binding->forward = 0;
+	binding->forward = false;
 	binding->next = NULL;
 	return binding;
 }
@@ -449,7 +455,7 @@ parse_typedef(char **str)
 	}
 	eat_spaces(str);
 
-	struct typedef_node_t *this_td = new_typedef(name, NULL, 0);
+	struct typedef_node_t *this_td = new_typedef(name, NULL, false);
 	this_td->info = parse_lens(str, NULL, 0, &this_td->own_type, this_td);
 
 	if (this_td->info == NULL) {
@@ -484,7 +490,7 @@ parse_typedef(char **str)
 	assert(this_td->own_type);
 	type_destroy(forward->info);
 	*forward->info = *this_td->info;
-	forward->forward = 0;
+	forward->forward = false;
 	free(this_td->info);
 	free(name);
 	free(this_td);
@@ -523,7 +529,7 @@ parse_struct(char **str, struct arg_type_info *info,
 		/* Forward declaration is currently handled as an
 		 * empty struct.  */
 		type_init_struct(info);
-		in_typedef->forward = 1;
+		in_typedef->forward = true;
 		return 0;
 	}
 
@@ -546,7 +552,7 @@ parse_struct(char **str, struct arg_type_info *info,
 			parse_char(str, ',');
 
 		eat_spaces(str);
-		int own;
+		bool own;
 		struct arg_type_info *field = parse_lens(str, NULL, 0, &own,
 							 NULL);
 		if (field == NULL || type_struct_add(info, field, own)) {
@@ -557,7 +563,7 @@ parse_struct(char **str, struct arg_type_info *info,
 }
 
 static int
-parse_string(char **str, struct arg_type_info **retp, int *ownp)
+parse_string(char **str, struct arg_type_info **retp, bool *ownp)
 {
 	struct arg_type_info *info = malloc(sizeof(*info) * 2);
 	if (info == NULL) {
@@ -567,7 +573,7 @@ parse_string(char **str, struct arg_type_info **retp, int *ownp)
 	}
 
 	struct expr_node *length;
-	int own_length;
+	bool own_length;
 	int with_arg = 0;
 
 	if (isdigit(**str)) {
@@ -593,7 +599,7 @@ parse_string(char **str, struct arg_type_info **retp, int *ownp)
 			free(length_arg);
 			goto fail;
 		}
-		own_length = 1;
+		own_length = true;
 
 	} else {
 		eat_spaces(str);
@@ -627,7 +633,7 @@ parse_string(char **str, struct arg_type_info **retp, int *ownp)
 		} else {
 			/* It was just a simple string after all.  */
 			length = expr_node_zero();
-			own_length = 0;
+			own_length = false;
 		}
 	}
 
@@ -637,7 +643,7 @@ parse_string(char **str, struct arg_type_info **retp, int *ownp)
 				length, own_length);
 
 		type_init_pointer(&info[0], &info[1], 0);
-		*ownp = 1;
+		*ownp = true;
 	}
 
 	info->lens = &string_lens;
@@ -695,7 +701,7 @@ try_parse_kwd(char **str, const char *kwd)
 /* Make a copy of INFO and set the *OWN bit if it's not already
  * owned.  */
 static int
-unshare_type_info(struct arg_type_info **infop, int *ownp)
+unshare_type_info(struct arg_type_info **infop, bool *ownp)
 {
 	if (*ownp)
 		return 0;
@@ -708,7 +714,7 @@ unshare_type_info(struct arg_type_info **infop, int *ownp)
 	}
 	*ninfo = **infop;
 	*infop = ninfo;
-	*ownp = 1;
+	*ownp = true;
 	return 0;
 }
 
@@ -717,7 +723,7 @@ unshare_type_info(struct arg_type_info **infop, int *ownp)
  * latter is only valid if the former is non-NULL, which is only in
  * top-level context.  */
 static int
-parse_alias(char **str, struct arg_type_info **retp, int *ownp,
+parse_alias(char **str, struct arg_type_info **retp, bool *ownp,
 	    struct param **extra_param, size_t param_num)
 {
 	/* For backward compatibility, we need to support things like
@@ -759,7 +765,7 @@ parse_array(char **str, struct arg_type_info *info)
 		return -1;
 
 	eat_spaces(str);
-	int own;
+	bool own;
 	struct arg_type_info *elt_info = parse_lens(str, NULL, 0, &own, NULL);
 	if (elt_info == NULL)
 		return -1;
@@ -768,7 +774,7 @@ parse_array(char **str, struct arg_type_info *info)
 	parse_char(str, ',');
 
 	eat_spaces(str);
-	int own_length;
+	bool own_length;
 	struct expr_node *length = parse_argnum(str, &own_length, 0);
 	if (length == NULL) {
 		if (own) {
@@ -790,7 +796,7 @@ parse_array(char **str, struct arg_type_info *info)
  *   enum<type> (keyname[=value],keyname[=value],... )
  */
 static int
-parse_enum(char **str, struct arg_type_info **retp, int *ownp)
+parse_enum(char **str, struct arg_type_info **retp, bool *ownp)
 {
 	/* Optional type argument.  */
 	eat_spaces(str);
@@ -820,7 +826,7 @@ parse_enum(char **str, struct arg_type_info **retp, int *ownp)
 
 	} else {
 		*retp = type_get_simple(ARGTYPE_INT);
-		*ownp = 0;
+		*ownp = false;
 	}
 
 	/* We'll need to set the lens, so unshare.  */
@@ -888,7 +894,7 @@ parse_enum(char **str, struct arg_type_info **retp, int *ownp)
 
 static struct arg_type_info *
 parse_nonpointer_type(char **str, struct param **extra_param, size_t param_num,
-		      int *ownp, struct typedef_node_t *in_typedef)
+		      bool *ownp, struct typedef_node_t *in_typedef)
 {
 	enum arg_type type;
 	if (parse_arg_type(str, &type) < 0) {
@@ -898,7 +904,7 @@ parse_nonpointer_type(char **str, struct param **extra_param, size_t param_num,
 		if (simple == NULL)
 			simple = parse_typedef_name(str);
 		if (simple != NULL) {
-			*ownp = 0;
+			*ownp = false;
 			return simple;
 		}
 
@@ -921,7 +927,7 @@ parse_nonpointer_type(char **str, struct param **extra_param, size_t param_num,
 	case ARGTYPE_USHORT:
 	case ARGTYPE_FLOAT:
 	case ARGTYPE_DOUBLE:
-		*ownp = 0;
+		*ownp = false;
 		return type_get_simple(type);
 
 	case ARGTYPE_ARRAY:
@@ -941,7 +947,7 @@ parse_nonpointer_type(char **str, struct param **extra_param, size_t param_num,
 			     "malloc: %s", strerror(errno));
 		return NULL;
 	}
-	*ownp = 1;
+	*ownp = true;
 
 	if (type == ARGTYPE_ARRAY) {
 		if (parse_array(str, info) < 0) {
@@ -986,7 +992,7 @@ name2lens(char **str, int *own_lensp)
 }
 
 static struct arg_type_info *
-parse_type(char **str, struct param **extra_param, size_t param_num, int *ownp,
+parse_type(char **str, struct param **extra_param, size_t param_num, bool *ownp,
 	   struct typedef_node_t *in_typedef)
 {
 	struct arg_type_info *info
@@ -1009,7 +1015,7 @@ parse_type(char **str, struct param **extra_param, size_t param_num, int *ownp,
 				return NULL;
 			}
 			type_init_pointer(outer, info, *ownp);
-			*ownp = 1;
+			*ownp = true;
 			(*str)++;
 			info = outer;
 		} else
@@ -1019,7 +1025,7 @@ parse_type(char **str, struct param **extra_param, size_t param_num, int *ownp,
 }
 
 static struct arg_type_info *
-parse_lens(char **str, struct param **extra_param, size_t param_num, int *ownp,
+parse_lens(char **str, struct param **extra_param, size_t param_num, bool *ownp,
 	   struct typedef_node_t *in_typedef)
 {
 	int own_lens;
@@ -1034,7 +1040,7 @@ parse_lens(char **str, struct param **extra_param, size_t param_num, int *ownp,
 		if (lens == &octal_lens && **str != '(') {
 			has_args = 0;
 			info = type_get_simple(ARGTYPE_INT);
-			*ownp = 0;
+			*ownp = false;
 		} else if (parse_char(str, '(') < 0) {
 			report_error(filename, line_no,
 				     "expected type argument after the lens");
@@ -1099,13 +1105,10 @@ param_is_void(struct param *param)
 static struct arg_type_info *
 get_hidden_int(void)
 {
-	char *str = strdup("hide(int)");
-	char *ptr = str;
-	assert(str != NULL);
-	int own;
+	char *ptr = (char *)"hide(int)";
+	bool own;
 	struct arg_type_info *info = parse_lens(&ptr, NULL, 0, &own, NULL);
 	assert(info != NULL);
-	free(str);
 	return info;
 }
 
@@ -1183,7 +1186,7 @@ process_line(char *buf) {
 			goto err;
 		}
 
-		int own;
+		bool own;
 		struct arg_type_info *type
 			= parse_lens(&str, &extra_param,
 				     fun->num_params - have_stop, &own, NULL);
@@ -1260,23 +1263,7 @@ process_line(char *buf) {
 }
 
 void
-init_global_config(void)
-{
-	struct arg_type_info *info = malloc(2 * sizeof(*info));
-	if (info == NULL)
-		error(1, errno, "malloc in init_global_config");
-
-	memset(info, 0, 2 * sizeof(*info));
-	info[0].type = ARGTYPE_POINTER;
-	info[0].u.ptr_info.info = &info[1];
-	info[1].type = ARGTYPE_VOID;
-
-	insert_typedef(new_typedef(strdup("addr"), info, 0));
-	insert_typedef(new_typedef(strdup("file"), info, 1));
-}
-
-void
-read_config_file(char *file) {
+read_config_file(const char *file) {
 	FILE *stream;
 	char *line = NULL;
 	size_t linecap = 0;
diff --git a/read_config_file.h b/read_config_file.h
index 7c60253..cf00bfb 100644
--- a/read_config_file.h
+++ b/read_config_file.h
@@ -19,5 +19,4 @@
  * 02110-1301 USA
  */
 
-extern void read_config_file(char *);
-extern void init_global_config(void);
+extern void read_config_file(const char *);
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
index 33ebdf2..09fc1a0 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -414,9 +414,8 @@ detach_process(struct Process *leader)
 	proc_each_breakpoint(leader, NULL, retract_breakpoint_cb, NULL);
 
 	/* Now untrace the process, if it was attached to by -p.  */
-	struct opt_p_t *it;
-	for (it = opt_p; it != NULL; it = it->next) {
-		struct Process *proc = pid2proc(it->pid);
+	for (size_t i = 0; i < opt_p_len; ++i) {
+		struct Process *proc = pid2proc(opt_p[i]);
 		if (proc == NULL)
 			continue;
 		if (proc->leader == leader) {
@@ -1152,9 +1151,8 @@ continue_after_syscall(struct Process *proc, int sysnum, int ret_p)
 void
 os_ltrace_exiting(void)
 {
-	struct opt_p_t *it;
-	for (it = opt_p; it != NULL; it = it->next) {
-		struct Process *proc = pid2proc(it->pid);
+	for (size_t i = 0; i < opt_p_len; ++i) {
+		struct Process *proc = pid2proc(opt_p[i]);
 		if (proc == NULL || proc->leader == NULL)
 			continue;
 		if (ltrace_exiting_install_handler(proc->leader) < 0)
From: =?utf-8?b?0L3QsNCx?= <nabijaczlew...@nabijaczleweli.xyz>
Date: Thu, 3 Aug 2023 19:13:17 +0200
Subject: Speed up function lookups appx. ten-fold

Wherein r_c_f is overall read_config_file() time in ns,
DUMP: y is hits, overall time in ns and lookup count,
and DUMP: y is misses, formatted likewise.

The lookups are now logarithmic with respect to function count
instead of linear. tsearch() was also evaluated but was both
significantly slower and not a good fit (list populated once).

This also fixes the debug message to say Replacing/New instead of always
new, and makes the precedence explicit.

$ ./ltrace -oll -F etc/ltrace.conf  tail -r /tmp/1000000a  > /dev/null
r_c_f: 1289130
DUMP: y: 12311844 w/2930
DUMP: n: 24297 w/2
$ ./ltrace -oll  -F etc/ltrace.conf  tail -r /tmp/1000000a  > /dev/null
r_c_f: 1961890
DUMP: y: 1038683 w/2930
DUMP: n: 1281 w/2

$ { head -c5k; ./ltrace -oll -F etc/ltrace.conf ~/code/voreutils/out/cmd/tac -rs \;; } < etc/ltrace.conf  > /dev/null
r_c_f: 1308504
DUMP: y: 84145117 w/32066
DUMP: n: 23977 w/2
$ { head -c5k; ./ltrace -oll -F etc/ltrace.conf ~/code/voreutils/out/cmd/tac -rs \;; } < etc/ltrace.conf  > /dev/null
r_c_f: 2605150
DUMP: y: 9162137 w/32066
DUMP: n: 1069 w/2

Instrumented thusly on amd64:
	#include <time.h>
	void
	read_config_file(char *file) {
		struct timespec s, e;
		clock_gettime(CLOCK_MONOTONIC, &s);
		FILE *stream;
		char *line = NULL;
		size_t linecap = 0;

		filename = file;
		stream = fopen(filename, "r");
		if (!stream) {
			return;
		}

		debug(1, "Reading config file `%s'...", filename);

		line_no = 0;
		while (getline(&line, &linecap, stream) != -1) {
			Function *tmp;

			tmp = process_line(line);

			if (tmp) {
				debug(2, "New function: `%s'", tmp->name);
				tmp->next = list_of_functions;
				list_of_functions = tmp;
			}
		}
		free(line);
		fclose(stream);
		clock_gettime(CLOCK_MONOTONIC, &e);
		assert(s.tv_sec == e.tv_sec);
		fprintf(stderr, "r_c_f: %lu\n", e.tv_nsec - s.tv_nsec);
	}

	#include <time.h>

	static uint64_t totalnsec_y;
	static unsigned totalcnt_y;
	static uint64_t totalnsec_n;
	static unsigned totalcnt_n;

	static Function *
	name2func(char const *name) {
		Function *tmp;
		struct timespec s, e;
		clock_gettime(CLOCK_MONOTONIC, &s);

		for (tmp = list_of_functions; tmp != NULL; tmp = tmp->next) {
			if (!strcmp(tmp->name, name)) {
				clock_gettime(CLOCK_MONOTONIC, &e);
				assert(s.tv_sec == e.tv_sec);
				++totalcnt_y;
				totalnsec_y += e.tv_nsec - s.tv_nsec;
				return tmp;
			}
		}

		static Function def;
		if (def.params == NULL)
			build_default_prototype(&def);
		clock_gettime(CLOCK_MONOTONIC, &e);
		assert(s.tv_sec == e.tv_sec);
		++totalcnt_n;
		totalnsec_n += e.tv_nsec - s.tv_nsec;
		return &def;
	}
	__attribute__((destructor)) void dumpy(void) {
		fprintf(stderr, "DUMP: y: %lu w/%u\n", totalnsec_y, totalcnt_y);
		fprintf(stderr, "DUMP: n: %lu w/%u\n", totalnsec_n, totalcnt_n);
	}
---
 common.h           |  9 ++++++++-
 output.c           | 39 ++++++++++++++++-----------------------
 read_config_file.c | 44 +++++++++++++++++++++++++-------------------
 3 files changed, 49 insertions(+), 43 deletions(-)

diff --git a/common.h b/common.h
index 5464d61..56ba479 100644
--- a/common.h
+++ b/common.h
@@ -27,6 +27,7 @@
 
 #include <sys/types.h>
 #include <sys/time.h>
+#include <string.h>
 #include <stdio.h>
 #include <stdbool.h>
 
@@ -57,13 +58,19 @@ struct Function {
 	struct param *params;
 	struct arg_type_info *return_info;
 	size_t num_params;
-	Function * next;
 	bool own_return_info;
 };
 
 extern Function * list_of_functions;
+extern size_t list_of_functions_size;
 extern char *PLTs_initialized_by_here;
 
+static inline int Function_cmp(const void *l, const void *r) {
+	const Function *lf = l;
+	const Function *rf = r;
+	return strcmp(lf->name, rf->name);
+}
+
 struct opt_c_struct {
 	int count;
 	struct timeval tv;
diff --git a/output.c b/output.c
index dc0be31..273461c 100644
--- a/output.c
+++ b/output.c
@@ -146,39 +146,32 @@ get_unknown_type(void)
 }
 
 /* The default prototype is: long X(long, long, long, long).  */
-static void
-build_default_prototype(Function *ret)
+static Function *
+build_default_prototype(void)
 {
-	static typeof(*ret->params) params[4];
+	static Function ret;
+	static typeof(*ret.params) params[4];
+	if (ret.params)
+		return &ret;
 
 	struct arg_type_info *unknown_type = get_unknown_type();
 
-	ret->return_info = unknown_type;
-	ret->own_return_info = 0;
+	ret.return_info = unknown_type;
+	ret.own_return_info = 0;
+
+	ret.num_params = sizeof(params) / sizeof(*params);
+	ret.params = params;
 
-	ret->num_params = sizeof(params) / sizeof(*params);
-	ret->params = params;
+	for (size_t i = 0; i < ret.num_params; ++i)
+		param_init_type(&ret.params[i], unknown_type, 0);
 
-	for (size_t i = 0; i < ret->num_params; ++i)
-		param_init_type(&ret->params[i], unknown_type, 0);
+	return &ret;
 }
 
 static Function *
 name2func(char const *name) {
-	Function *tmp;
-	const char *str1, *str2;
-
-	for (tmp = list_of_functions; tmp != NULL; tmp = tmp->next) {
-		str1 = tmp->name;
-		str2 = name;
-		if (!strcmp(str1, str2))
-			return tmp;
-	}
-
-	static Function def;
-	if (def.params == NULL)
-		build_default_prototype(&def);
-	return &def;
+	const Function key = {.name = name};
+	return bsearch(&key, list_of_functions, list_of_functions_size, sizeof(*list_of_functions), Function_cmp) ?: build_default_prototype();
 }
 
 void
diff --git a/read_config_file.c b/read_config_file.c
index bcdf2c8..8fe4646 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -28,6 +28,7 @@
 #include <ctype.h>
 #include <errno.h>
 #include <error.h>
+#include <search.h>
 #include <assert.h>
 
 #include "common.h"
@@ -58,6 +59,7 @@ static struct arg_type_info *parse_lens(char **str, struct param **extra_param,
 static int parse_enum(char **str, struct arg_type_info **retp, bool *ownp);
 
 Function *list_of_functions = NULL;
+size_t list_of_functions_size = 0;
 
 static int
 parse_arg_type(char **name, enum arg_type *ret)
@@ -500,8 +502,6 @@ static void
 destroy_fun(Function *fun)
 {
 	size_t i;
-	if (fun == NULL)
-		return;
 	if (fun->own_return_info) {
 		type_destroy(fun->return_info);
 		free(fun->return_info);
@@ -1112,12 +1112,10 @@ get_hidden_int(void)
 	return info;
 }
 
-static Function *
-process_line(char *buf) {
-	char *str = buf;
+static bool
+process_line(char *str, struct Function *fun) {
 	char *tmp;
 
-	line_no++;
 	debug(3, "Reading line %d of `%s'", line_no, filename);
 	eat_spaces(&str);
 
@@ -1130,12 +1128,7 @@ process_line(char *buf) {
 		return NULL;
 	}
 
-	Function *fun = calloc(1, sizeof(*fun));
-	if (fun == NULL) {
-		report_error(filename, line_no,
-			     "alloc function: %s", strerror(errno));
-		return NULL;
-	}
+	memset(fun, 0, sizeof(*fun));
 
 	fun->return_info = parse_lens(&str, NULL, 0,
 				      &fun->own_return_info, NULL);
@@ -1267,6 +1260,7 @@ read_config_file(const char *file) {
 	FILE *stream;
 	char *line = NULL;
 	size_t linecap = 0;
+	bool freeback = false;
 
 	filename = file;
 	stream = fopen(filename, "r");
@@ -1278,16 +1272,28 @@ read_config_file(const char *file) {
 
 	line_no = 0;
 	while (getline(&line, &linecap, stream) != -1) {
-		Function *tmp;
-
-		tmp = process_line(line);
+		++line_no;
+		if(!freeback) {
+			if(!(list_of_functions = reallocarray(list_of_functions, list_of_functions_size + 1, sizeof(*list_of_functions)))) {
+				report_error(filename, line_no,
+					     "alloc function: %s", strerror(errno));
+				continue;
+			}
+			freeback = true;
+		}
 
-		if (tmp) {
-			debug(2, "New function: `%s'", tmp->name);
-			tmp->next = list_of_functions;
-			list_of_functions = tmp;
+		if (process_line(line, &list_of_functions[list_of_functions_size])) {
+			Function *old = lfind(&list_of_functions[list_of_functions_size], list_of_functions, &list_of_functions_size, sizeof(*list_of_functions), Function_cmp);
+			debug(2, "%s function: `%s'", old ? "Replacing" : "New", list_of_functions[list_of_functions_size].name);
+			if (old)
+				memcpy(old, &list_of_functions[list_of_functions_size], sizeof(*old));
+			else {
+				++list_of_functions_size;
+				freeback = false;
+			}
 		}
 	}
 	free(line);
 	fclose(stream);
+	qsort(list_of_functions, list_of_functions_size, sizeof(*list_of_functions), Function_cmp);
 }

Attachment: signature.asc
Description: PGP signature

Reply via email to