mirror of
https://github.com/tmux/tmux.git
synced 2026-07-03 10:12:31 +00:00
New tmux parser and test program.
This commit is contained in:
2534
cmd-parse.y
2534
cmd-parse.y
File diff suppressed because it is too large
Load Diff
59
parser-test/main.c
Normal file
59
parser-test/main.c
Normal 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
62
parser-test/tmux.h
Normal 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
118
parser-test/xstubs.c
Normal 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);
|
||||
}
|
||||
82
tmux-parser.h
Normal file
82
tmux-parser.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/* $OpenBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 Nicholas Marriott <nicm@users.sourceforge.net>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef TMUX_PARSER_H
|
||||
#define TMUX_PARSER_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
* Parsed command tree.
|
||||
*
|
||||
* The parser builds syntax only. It does not expand formats, environment
|
||||
* variables, or ~, does not evaluate %if/%elif, does not expand aliases, and
|
||||
* does not build struct cmd or struct cmd_list. Those are execution-time
|
||||
* operations.
|
||||
*
|
||||
* Command failure scope is represented by CMD_PARSE_SEQUENCE: if an invoked
|
||||
* command or assignment fails, the invoker skips the remaining children of
|
||||
* that sequence. No explicit group ID is stored in the tree.
|
||||
*/
|
||||
|
||||
struct cmd_parse_tree;
|
||||
struct cmd_parse_node;
|
||||
struct cmd_parse_input;
|
||||
|
||||
enum cmd_parse_node_type {
|
||||
CMD_PARSE_ROOT,
|
||||
CMD_PARSE_SEQUENCE,
|
||||
|
||||
CMD_PARSE_COMMAND,
|
||||
CMD_PARSE_STRING,
|
||||
CMD_PARSE_COMMANDS,
|
||||
|
||||
CMD_PARSE_TEXT,
|
||||
CMD_PARSE_ENVIRONMENT,
|
||||
CMD_PARSE_TILDE,
|
||||
|
||||
CMD_PARSE_ASSIGN,
|
||||
CMD_PARSE_HIDDEN_ASSIGN,
|
||||
|
||||
CMD_PARSE_IF,
|
||||
CMD_PARSE_ELIF,
|
||||
CMD_PARSE_ELSE
|
||||
};
|
||||
|
||||
struct cmd_parse_tree *cmd_parse_from_file(FILE *, struct cmd_parse_input *,
|
||||
char **);
|
||||
struct cmd_parse_tree *cmd_parse_from_buffer(const void *, size_t,
|
||||
struct cmd_parse_input *, char **);
|
||||
struct cmd_parse_tree *cmd_parse_from_string(const char *,
|
||||
struct cmd_parse_input *, char **);
|
||||
struct cmd_parse_tree *cmd_parse_addref(struct cmd_parse_tree *);
|
||||
void cmd_parse_free(struct cmd_parse_tree *);
|
||||
struct cmd_parse_node *cmd_parse_root(struct cmd_parse_tree *);
|
||||
char *cmd_parse_print(const struct cmd_parse_tree *);
|
||||
void cmd_parse_log(const struct cmd_parse_tree *);
|
||||
enum cmd_parse_node_type cmd_parse_node_type(const struct cmd_parse_node *);
|
||||
const char *cmd_parse_node_type_string(enum cmd_parse_node_type);
|
||||
const char *cmd_parse_node_value(const struct cmd_parse_node *);
|
||||
u_int cmd_parse_node_line(const struct cmd_parse_node *);
|
||||
u_int cmd_parse_node_end_line(const struct cmd_parse_node *);
|
||||
struct cmd_parse_node *cmd_parse_node_first_child(struct cmd_parse_node *);
|
||||
struct cmd_parse_node *cmd_parse_node_next(struct cmd_parse_node *);
|
||||
|
||||
#endif /* TMUX_PARSER_H */
|
||||
Reference in New Issue
Block a user