New tmux parser and test program.

This commit is contained in:
Nicholas Marriott
2026-06-30 07:27:06 +01:00
parent 543d104f85
commit efe2453eac
5 changed files with 1625 additions and 1230 deletions

59
parser-test/main.c Normal file
View File

@@ -0,0 +1,59 @@
/*
* Standalone driver for the cmd-parse.y AST parser. Reads a config/command
* file (argument, or stdin) and prints both forms: the debug AST dump via
* cmd_parse_log (to stderr) and the normalized form via cmd_parse_print (to
* stdout).
*
* Usage: parsetest [file]
*/
#include <locale.h>
#include "tmux.h"
#include "tmux-parser.h"
int
main(int argc, char **argv)
{
struct cmd_parse_input pi;
struct cmd_parse_tree *tree;
FILE *f = stdin;
char *cause = NULL, *out;
/* As tmux does at startup, so \u/\U escapes can be encoded. */
setlocale(LC_CTYPE, "");
memset(&pi, 0, sizeof pi);
pi.line = 1;
if (getenv("ONEGROUP") != NULL)
pi.flags |= CMD_PARSE_ONEGROUP;
if (argc > 1) {
pi.file = argv[1];
if ((f = fopen(argv[1], "r")) == NULL) {
perror(argv[1]);
return (1);
}
}
tree = cmd_parse_from_file(f, &pi, &cause);
if (f != stdin)
fclose(f);
if (tree == NULL) {
fprintf(stderr, "parse error: %s\n",
cause != NULL ? cause : "unknown");
free(cause);
return (1);
}
fprintf(stdout, "=== AST (stderr) ===\n");
fflush(stdout);
cmd_parse_log(tree);
out = cmd_parse_print(tree);
fprintf(stdout, "=== NORMALIZED ===\n%s\n", out);
free(out);
cmd_parse_free(tree);
return (0);
}

62
parser-test/tmux.h Normal file
View File

@@ -0,0 +1,62 @@
/* Stub tmux.h for the standalone cmd-parse.y test harness.
*
* This is NOT the real tmux.h. It provides just enough for cmd-parse.y to
* compile and link on its own: the queue macros, the allocation/logging
* helpers it calls, and the small part of struct cmd_parse_input it touches.
* It is found ahead of the real tmux.h via the harness include path.
*/
#ifndef TMUX_TEST_STUB_H
#define TMUX_TEST_STUB_H
#include <sys/types.h>
#include <limits.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "compat/queue.h"
#ifndef __dead
#define __dead __attribute__((__noreturn__))
#endif
#ifndef __unused
#define __unused __attribute__((__unused__))
#endif
#ifndef printflike
#define printflike(a, b) __attribute__((format(printf, a, b)))
#endif
/* Allocation helpers (implemented in xstubs.c). */
void *xmalloc(size_t);
void *xcalloc(size_t, size_t);
void *xrealloc(void *, size_t);
char *xstrdup(const char *);
char *xstrndup(const char *, size_t);
int xasprintf(char **, const char *, ...) printflike(2, 3);
int xvasprintf(char **, const char *, va_list);
/* Logging and fatal errors (implemented in xstubs.c). */
void log_debug(const char *, ...) printflike(1, 2);
__dead void fatal(const char *, ...) printflike(1, 2);
__dead void fatalx(const char *, ...) printflike(1, 2);
/*
* Parser input. The real struct has more fields, but the AST parser only
* touches flags, file and line.
*/
struct cmd_parse_input {
int flags;
#define CMD_PARSE_QUIET 0x1
#define CMD_PARSE_PARSEONLY 0x2
#define CMD_PARSE_NOALIAS 0x4
#define CMD_PARSE_VERBOSE 0x8
#define CMD_PARSE_ONEGROUP 0x10
const char *file;
u_int line;
};
#endif /* TMUX_TEST_STUB_H */

118
parser-test/xstubs.c Normal file
View File

@@ -0,0 +1,118 @@
/* Minimal implementations of the helpers cmd-parse.y links against. */
#include "tmux.h"
#include <errno.h>
void *
xmalloc(size_t size)
{
void *ptr;
if (size == 0)
size = 1;
if ((ptr = malloc(size)) == NULL)
fatalx("xmalloc");
return (ptr);
}
void *
xcalloc(size_t nmemb, size_t size)
{
void *ptr;
if (nmemb == 0 || size == 0)
nmemb = size = 1;
if ((ptr = calloc(nmemb, size)) == NULL)
fatalx("xcalloc");
return (ptr);
}
void *
xrealloc(void *oldptr, size_t newsize)
{
void *ptr;
if (newsize == 0)
newsize = 1;
if ((ptr = realloc(oldptr, newsize)) == NULL)
fatalx("xrealloc");
return (ptr);
}
char *
xstrdup(const char *s)
{
char *ptr;
if ((ptr = strdup(s)) == NULL)
fatalx("xstrdup");
return (ptr);
}
char *
xstrndup(const char *s, size_t maxlen)
{
char *ptr;
if ((ptr = strndup(s, maxlen)) == NULL)
fatalx("xstrndup");
return (ptr);
}
int
xvasprintf(char **ret, const char *fmt, va_list ap)
{
int i;
if ((i = vasprintf(ret, fmt, ap)) < 0)
fatalx("xvasprintf");
return (i);
}
int
xasprintf(char **ret, const char *fmt, ...)
{
va_list ap;
int i;
va_start(ap, fmt);
i = xvasprintf(ret, fmt, ap);
va_end(ap);
return (i);
}
void
log_debug(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fputc('\n', stderr);
}
__dead void
fatal(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, ": %s\n", strerror(errno));
exit(1);
}
__dead void
fatalx(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fputc('\n', stderr);
exit(1);
}