contrib: introduce simple highlighter library
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
		
							parent
							
								
									777fe674c4
								
							
						
					
					
						commit
						ee88038986
					
				
							
								
								
									
										25
									
								
								contrib/highlighter/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								contrib/highlighter/Makefile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
			
		||||
CFLAGS ?= -O3 -march=native
 | 
			
		||||
CFLAGS += -std=gnu99
 | 
			
		||||
CFLAGS += -Wall
 | 
			
		||||
CFLAGS += -MMD -MP
 | 
			
		||||
 | 
			
		||||
highlight: highlight.o highlighter.o
 | 
			
		||||
 | 
			
		||||
fuzz: CC := clang
 | 
			
		||||
fuzz: CFLAGS += -fsanitize=fuzzer
 | 
			
		||||
fuzz: fuzz.c highlighter.c
 | 
			
		||||
 | 
			
		||||
gui/Makefile: gui/highlight.pro
 | 
			
		||||
	cd gui && qmake
 | 
			
		||||
gui: gui/Makefile
 | 
			
		||||
	@$(MAKE) -C gui
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -f highlight fuzz *.o *.d
 | 
			
		||||
	@if [ -f gui/Makefile ]; then $(MAKE) -C gui distclean; fi
 | 
			
		||||
 | 
			
		||||
.PHONY: clean gui
 | 
			
		||||
.DEFAULT_GOAL: highlight
 | 
			
		||||
MAKEFLAGS += --no-print-directory
 | 
			
		||||
 | 
			
		||||
-include *.d
 | 
			
		||||
							
								
								
									
										22
									
								
								contrib/highlighter/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								contrib/highlighter/README
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
wg(8) and wg-quick(8) syntax highlighter library
 | 
			
		||||
================================================
 | 
			
		||||
 | 
			
		||||
highlighter.c contains a simple portable highlighter for the wg(8) and
 | 
			
		||||
wg-quick(8) configuration files. Simply copy `highlight.c` and
 | 
			
		||||
`highlight.h` into your project wholesale.
 | 
			
		||||
 | 
			
		||||
As a demo, a simple console highlighter program is included, alongside a
 | 
			
		||||
simple Qt5 GUI app to show its usage in realtime.
 | 
			
		||||
 | 
			
		||||
There is also a basic fuzzer, because why not?
 | 
			
		||||
 | 
			
		||||
Usage:
 | 
			
		||||
 | 
			
		||||
    $ make
 | 
			
		||||
    $ ./highlight < path/to/tunnel.conf
 | 
			
		||||
 | 
			
		||||
    $ make gui
 | 
			
		||||
    $ ./gui/highlight
 | 
			
		||||
 | 
			
		||||
    $ make fuzz
 | 
			
		||||
    $ ./fuzz -workers=$(nproc) -jobs=$(nproc) ./corpus
 | 
			
		||||
							
								
								
									
										22
									
								
								contrib/highlighter/fuzz.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								contrib/highlighter/fuzz.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include "highlighter.h"
 | 
			
		||||
 | 
			
		||||
int LLVMFuzzerTestOneInput(const char *data, size_t size)
 | 
			
		||||
{
 | 
			
		||||
	char *str = strndup(data, size);
 | 
			
		||||
	if (!str)
 | 
			
		||||
		return 0;
 | 
			
		||||
	struct highlight_span *spans = highlight_config(str);
 | 
			
		||||
	if (!spans)
 | 
			
		||||
		return 0;
 | 
			
		||||
	for (struct highlight_span *span = spans; span->type != HighlightEnd; ++span);
 | 
			
		||||
	free(spans);
 | 
			
		||||
	free(str);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										90
									
								
								contrib/highlighter/gui/highlight.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								contrib/highlighter/gui/highlight.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,90 @@
 | 
			
		||||
#include <QApplication>
 | 
			
		||||
#include <QPlainTextEdit>
 | 
			
		||||
#include <QPalette>
 | 
			
		||||
#include <QFontDatabase>
 | 
			
		||||
#include <QVBoxLayout>
 | 
			
		||||
#include <QPushButton>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include "../highlighter.h"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static QColor colormap[] = {
 | 
			
		||||
	[HighlightSection] = QColor("#ababab"),
 | 
			
		||||
	[HighlightField] = QColor("#70c0b1"),
 | 
			
		||||
	[HighlightPrivateKey] = QColor("#7aa6da"),
 | 
			
		||||
	[HighlightPublicKey] = QColor("#7aa6da"),
 | 
			
		||||
	[HighlightPresharedKey] = QColor("#7aa6da"),
 | 
			
		||||
	[HighlightIP] = QColor("#b9ca4a"),
 | 
			
		||||
	[HighlightCidr] = QColor("#e78c45"),
 | 
			
		||||
	[HighlightHost] = QColor("#b9ca4a"),
 | 
			
		||||
	[HighlightPort] = QColor("#e78c45"),
 | 
			
		||||
	[HighlightMTU] = QColor("#c397d8"),
 | 
			
		||||
	[HighlightKeepalive] = QColor("#c397d8"),
 | 
			
		||||
	[HighlightComment] = QColor("#969896"),
 | 
			
		||||
	[HighlightDelimiter] = QColor("#7aa6da"),
 | 
			
		||||
#ifndef MOBILE_WGQUICK_SUBSET
 | 
			
		||||
	[HighlightTable] = QColor("#c397d8"),
 | 
			
		||||
	[HighlightFwMark] = QColor("#c397d8"),
 | 
			
		||||
	[HighlightSaveConfig] = QColor("#c397d8"),
 | 
			
		||||
	[HighlightCmd] = QColor("#969896"),
 | 
			
		||||
#endif
 | 
			
		||||
	[HighlightError] = QColor("#d54e53")
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	QApplication a(argc, argv);
 | 
			
		||||
	QWidget w;
 | 
			
		||||
	w.setWindowTitle(QObject::tr("WireGuard Configuration Highlighter"));
 | 
			
		||||
	QVBoxLayout v;
 | 
			
		||||
	w.setLayout(&v);
 | 
			
		||||
	QPlainTextEdit e;
 | 
			
		||||
	v.addWidget(&e);
 | 
			
		||||
	QPalette p(e.palette());
 | 
			
		||||
	p.setColor(QPalette::Base, QColor("#010101"));
 | 
			
		||||
	p.setColor(QPalette::Text, QColor("#eaeaea"));
 | 
			
		||||
	e.setPalette(p);
 | 
			
		||||
	QFont f(QFontDatabase::systemFont(QFontDatabase::FixedFont));
 | 
			
		||||
	f.setPointSize(16);
 | 
			
		||||
	e.setFont(f);
 | 
			
		||||
	e.setMinimumSize(400, 500);
 | 
			
		||||
	bool guard = false;
 | 
			
		||||
	QObject::connect(&e, &QPlainTextEdit::textChanged, [&]() {
 | 
			
		||||
		if (guard)
 | 
			
		||||
			return;
 | 
			
		||||
		struct highlight_span *spans = highlight_config(e.toPlainText().toLatin1().data());
 | 
			
		||||
		if (!spans)
 | 
			
		||||
			return;
 | 
			
		||||
		QTextCursor cursor(e.document());
 | 
			
		||||
		QTextCharFormat format;
 | 
			
		||||
		cursor.beginEditBlock();
 | 
			
		||||
		cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
 | 
			
		||||
		cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
 | 
			
		||||
		format.setForeground(p.color(QPalette::Text));
 | 
			
		||||
		format.setUnderlineStyle(QTextCharFormat::NoUnderline);
 | 
			
		||||
		cursor.mergeCharFormat(format);
 | 
			
		||||
		for (struct highlight_span *span = spans; span->type != HighlightEnd; ++span) {
 | 
			
		||||
			cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
 | 
			
		||||
			cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, span->start);
 | 
			
		||||
			cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, span->len);
 | 
			
		||||
			format.setForeground(colormap[span->type]);
 | 
			
		||||
			format.setUnderlineStyle(span->type == HighlightError ? QTextCharFormat::SpellCheckUnderline : QTextCharFormat::NoUnderline);
 | 
			
		||||
			cursor.mergeCharFormat(format);
 | 
			
		||||
		}
 | 
			
		||||
		free(spans);
 | 
			
		||||
		guard = true;
 | 
			
		||||
		cursor.endEditBlock();
 | 
			
		||||
		guard = false;
 | 
			
		||||
	});
 | 
			
		||||
	QPushButton b;
 | 
			
		||||
	v.addWidget(&b);
 | 
			
		||||
	b.setText(QObject::tr("&Randomize colors"));
 | 
			
		||||
	QObject::connect(&b, &QPushButton::clicked, [&]() {
 | 
			
		||||
		for (size_t i = 0; i < sizeof(colormap) / sizeof(colormap[0]); ++i)
 | 
			
		||||
			colormap[i] = QColor::fromHsl(qrand() % 360, qrand() % 192 + 64, qrand() % 128 + 128);
 | 
			
		||||
		e.setPlainText(e.toPlainText());
 | 
			
		||||
	});
 | 
			
		||||
	w.show();
 | 
			
		||||
	return a.exec();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								contrib/highlighter/gui/highlight.pro
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								contrib/highlighter/gui/highlight.pro
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
QT += core gui widgets
 | 
			
		||||
TEMPLATE = app
 | 
			
		||||
TARGET = highlight
 | 
			
		||||
SOURCES += highlight.cpp ../highlighter.c
 | 
			
		||||
HEADERS += ../highlighter.h
 | 
			
		||||
							
								
								
									
										83
									
								
								contrib/highlighter/highlight.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								contrib/highlighter/highlight.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include "highlighter.h"
 | 
			
		||||
 | 
			
		||||
#define TERMINAL_FG_BLACK	"\x1b[30m"
 | 
			
		||||
#define TERMINAL_FG_RED		"\x1b[31m"
 | 
			
		||||
#define TERMINAL_FG_GREEN	"\x1b[32m"
 | 
			
		||||
#define TERMINAL_FG_YELLOW	"\x1b[33m"
 | 
			
		||||
#define TERMINAL_FG_BLUE	"\x1b[34m"
 | 
			
		||||
#define TERMINAL_FG_MAGENTA	"\x1b[35m"
 | 
			
		||||
#define TERMINAL_FG_CYAN	"\x1b[36m"
 | 
			
		||||
#define TERMINAL_FG_WHITE	"\x1b[37m"
 | 
			
		||||
#define TERMINAL_FG_DEFAULT	"\x1b[39m"
 | 
			
		||||
 | 
			
		||||
#define TERMINAL_BG_BLACK	"\x1b[40m"
 | 
			
		||||
#define TERMINAL_BG_RED		"\x1b[41m"
 | 
			
		||||
#define TERMINAL_BG_GREEN	"\x1b[42m"
 | 
			
		||||
#define TERMINAL_BG_YELLOW	"\x1b[43m"
 | 
			
		||||
#define TERMINAL_BG_BLUE	"\x1b[44m"
 | 
			
		||||
#define TERMINAL_BG_MAGENTA	"\x1b[45m"
 | 
			
		||||
#define TERMINAL_BG_CYAN	"\x1b[46m"
 | 
			
		||||
#define TERMINAL_BG_WHITE	"\x1b[47m"
 | 
			
		||||
#define TERMINAL_BG_DEFAULT	"\x1b[49m"
 | 
			
		||||
 | 
			
		||||
#define TERMINAL_BOLD		"\x1b[1m"
 | 
			
		||||
#define TERMINAL_NO_BOLD	"\x1b[22m"
 | 
			
		||||
#define TERMINAL_UNDERLINE	"\x1b[4m"
 | 
			
		||||
#define TERMINAL_NO_UNDERLINE	"\x1b[24m"
 | 
			
		||||
 | 
			
		||||
#define TERMINAL_RESET		"\x1b[0m"
 | 
			
		||||
 | 
			
		||||
static const char *colormap[] = {
 | 
			
		||||
	[HighlightSection] = TERMINAL_FG_BLACK TERMINAL_BOLD,
 | 
			
		||||
	[HighlightField] = TERMINAL_FG_BLUE TERMINAL_BOLD,
 | 
			
		||||
	[HighlightPrivateKey] = TERMINAL_FG_YELLOW TERMINAL_BOLD,
 | 
			
		||||
	[HighlightPublicKey] = TERMINAL_FG_YELLOW TERMINAL_BOLD,
 | 
			
		||||
	[HighlightPresharedKey] = TERMINAL_FG_YELLOW TERMINAL_BOLD,
 | 
			
		||||
	[HighlightIP] = TERMINAL_FG_GREEN,
 | 
			
		||||
	[HighlightCidr] = TERMINAL_FG_YELLOW,
 | 
			
		||||
	[HighlightHost] = TERMINAL_FG_GREEN TERMINAL_BOLD,
 | 
			
		||||
	[HighlightPort] = TERMINAL_FG_MAGENTA,
 | 
			
		||||
	[HighlightMTU] = TERMINAL_FG_BLUE,
 | 
			
		||||
	[HighlightKeepalive] = TERMINAL_FG_BLUE,
 | 
			
		||||
	[HighlightComment] = TERMINAL_FG_CYAN,
 | 
			
		||||
	[HighlightDelimiter] = TERMINAL_FG_CYAN,
 | 
			
		||||
#ifndef MOBILE_WGQUICK_SUBSET
 | 
			
		||||
	[HighlightTable] = TERMINAL_FG_BLUE,
 | 
			
		||||
	[HighlightFwMark] = TERMINAL_FG_BLUE,
 | 
			
		||||
	[HighlightSaveConfig] = TERMINAL_FG_BLUE,
 | 
			
		||||
	[HighlightCmd] = TERMINAL_FG_WHITE,
 | 
			
		||||
#endif
 | 
			
		||||
	[HighlightError] = TERMINAL_FG_RED TERMINAL_UNDERLINE
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	char input[1024 * 1024];
 | 
			
		||||
	struct highlight_span *spans;
 | 
			
		||||
	size_t last = 0, total_len;
 | 
			
		||||
 | 
			
		||||
	total_len = fread(input, 1, sizeof(input) - 1, stdin);
 | 
			
		||||
	input[total_len] = '\0';
 | 
			
		||||
	spans = highlight_config(input);
 | 
			
		||||
 | 
			
		||||
	fputs(TERMINAL_RESET, stdout);
 | 
			
		||||
	for (struct highlight_span *span = spans; span->type != HighlightEnd; ++span) {
 | 
			
		||||
		fwrite(input + last, 1, span->start - last, stdout);
 | 
			
		||||
		fputs(colormap[span->type], stdout);
 | 
			
		||||
		fwrite(input + span->start, 1, span->len, stdout);
 | 
			
		||||
		fputs(TERMINAL_RESET, stdout);
 | 
			
		||||
		last = span->start + span->len;
 | 
			
		||||
	}
 | 
			
		||||
	fwrite(input + last, 1, total_len - last, stdout);
 | 
			
		||||
 | 
			
		||||
	free(spans);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										620
									
								
								contrib/highlighter/highlighter.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										620
									
								
								contrib/highlighter/highlighter.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,620 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include "highlighter.h"
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
	const char *s;
 | 
			
		||||
	size_t len;
 | 
			
		||||
} string_span_t;
 | 
			
		||||
 | 
			
		||||
static bool is_decimal(char c)
 | 
			
		||||
{
 | 
			
		||||
	return c >= '0' && c <= '9';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_hexadecimal(char c)
 | 
			
		||||
{
 | 
			
		||||
	return is_decimal(c) || ((c | 32) >= 'a' && (c | 32) <= 'f');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_alphabet(char c)
 | 
			
		||||
{
 | 
			
		||||
	return (c | 32) >= 'a' && (c | 32) <= 'z';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_same(string_span_t s, const char *c)
 | 
			
		||||
{
 | 
			
		||||
	size_t len = strlen(c);
 | 
			
		||||
 | 
			
		||||
	if (len != s.len)
 | 
			
		||||
		return false;
 | 
			
		||||
	return !memcmp(s.s, c, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_caseless_same(string_span_t s, const char *c)
 | 
			
		||||
{
 | 
			
		||||
	size_t len = strlen(c);
 | 
			
		||||
 | 
			
		||||
	if (len != s.len)
 | 
			
		||||
		return false;
 | 
			
		||||
	for (size_t i = 0; i < len; ++i) {
 | 
			
		||||
		char a = c[i], b = s.s[i];
 | 
			
		||||
		if ((unsigned)a - 'a' < 26)
 | 
			
		||||
			a &= 95;
 | 
			
		||||
		if ((unsigned)b - 'a' < 26)
 | 
			
		||||
			b &= 95;
 | 
			
		||||
		if (a != b)
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_valid_key(string_span_t s)
 | 
			
		||||
{
 | 
			
		||||
	if (s.len != 44 || s.s[43] != '=')
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	for (size_t i = 0; i < 43; ++i) {
 | 
			
		||||
		if (!is_decimal(s.s[i]) && !is_alphabet(s.s[i]) &&
 | 
			
		||||
		    s.s[i] != '/' && s.s[i] != '+')
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_valid_hostname(string_span_t s)
 | 
			
		||||
{
 | 
			
		||||
	size_t num_digit = 0, num_entity = s.len;
 | 
			
		||||
 | 
			
		||||
	if (s.len > 63 || !s.len)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (s.s[0] == '-' || s.s[s.len - 1] == '-')
 | 
			
		||||
		return false;
 | 
			
		||||
	if (s.s[0] == '.' || s.s[s.len - 1] == '.')
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	for (size_t i = 0; i < s.len; ++i) {
 | 
			
		||||
		if (is_decimal(s.s[i])) {
 | 
			
		||||
			++num_digit;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if (s.s[i] == '.') {
 | 
			
		||||
			--num_entity;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!is_alphabet(s.s[i]) && s.s[i] != '-')
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		if (i && s.s[i] == '.' && s.s[i - 1] == '.')
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
	return num_digit != num_entity;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_valid_ipv4(string_span_t s)
 | 
			
		||||
{
 | 
			
		||||
	for (size_t j, i = 0, pos = 0; i < 4 && pos < s.len; ++i) {
 | 
			
		||||
		uint32_t val = 0;
 | 
			
		||||
 | 
			
		||||
		for (j = 0; j < 3 && pos + j < s.len && is_decimal(s.s[pos + j]); ++j)
 | 
			
		||||
			val = 10 * val + s.s[pos + j] - '0';
 | 
			
		||||
		if (j == 0 || (j > 1 && s.s[pos] == '0') || val > 255)
 | 
			
		||||
			return false;
 | 
			
		||||
		if (pos + j == s.len && i == 3)
 | 
			
		||||
			return true;
 | 
			
		||||
		if (s.s[pos + j] != '.')
 | 
			
		||||
			return false;
 | 
			
		||||
		pos += j + 1;
 | 
			
		||||
	}
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_valid_ipv6(string_span_t s)
 | 
			
		||||
{
 | 
			
		||||
	size_t pos = 0;
 | 
			
		||||
	bool seen_colon = false;
 | 
			
		||||
 | 
			
		||||
	if (s.len < 2)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (s.s[pos] == ':' && s.s[++pos] != ':')
 | 
			
		||||
		return false;
 | 
			
		||||
	if (s.s[s.len - 1] == ':' && s.s[s.len - 2] != ':')
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	for (size_t j, i = 0; pos < s.len; ++i) {
 | 
			
		||||
		if (s.s[pos] == ':' && !seen_colon) {
 | 
			
		||||
			seen_colon = true;
 | 
			
		||||
			if (++pos == s.len)
 | 
			
		||||
				break;
 | 
			
		||||
			if (i == 7)
 | 
			
		||||
				return false;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		for (j = 0; j < 4 && pos + j < s.len && is_hexadecimal(s.s[pos + j]); ++j);
 | 
			
		||||
		if (j == 0)
 | 
			
		||||
			return false;
 | 
			
		||||
		if (pos + j == s.len && (seen_colon || i == 7))
 | 
			
		||||
			break;
 | 
			
		||||
		if (i == 7)
 | 
			
		||||
			return false;
 | 
			
		||||
		if (s.s[pos + j] != ':') {
 | 
			
		||||
			if (s.s[pos + j] != '.' || (i < 6 && !seen_colon))
 | 
			
		||||
				return false;
 | 
			
		||||
			return is_valid_ipv4((string_span_t){ s.s + pos, s.len - pos });
 | 
			
		||||
		}
 | 
			
		||||
		pos += j + 1;
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_valid_uint(string_span_t s, bool support_hex, uint64_t min, uint64_t max)
 | 
			
		||||
{
 | 
			
		||||
	uint64_t val = 0;
 | 
			
		||||
 | 
			
		||||
	/* Bound this around 32 bits, so that we don't have to write overflow logic. */
 | 
			
		||||
	if (s.len > 10 || !s.len)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (support_hex && s.len > 2 && s.s[0] == '0' && s.s[1] == 'x') {
 | 
			
		||||
		for (size_t i = 2; i < s.len; ++i) {
 | 
			
		||||
			if (s.s[i] - '0' < 10)
 | 
			
		||||
				val = 16 * val + (s.s[i] - '0');
 | 
			
		||||
			else if ((s.s[i] | 32) - 'a' < 6)
 | 
			
		||||
				val = 16 * val + (s.s[i] | 32) - 'a' + 10;
 | 
			
		||||
			else
 | 
			
		||||
				return false;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		for (size_t i = 0; i < s.len; ++i) {
 | 
			
		||||
			if (!is_decimal(s.s[i]))
 | 
			
		||||
				return false;
 | 
			
		||||
			val = 10 * val + s.s[i] - '0';
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return val <= max && val >= min;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_valid_port(string_span_t s)
 | 
			
		||||
{
 | 
			
		||||
	return is_valid_uint(s, false, 0, 65535);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_valid_mtu(string_span_t s)
 | 
			
		||||
{
 | 
			
		||||
	return is_valid_uint(s, false, 576, 65535);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_valid_persistentkeepalive(string_span_t s)
 | 
			
		||||
{
 | 
			
		||||
	if (is_same(s, "off"))
 | 
			
		||||
		return true;
 | 
			
		||||
	return is_valid_uint(s, false, 0, 65535);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef MOBILE_WGQUICK_SUBSET
 | 
			
		||||
 | 
			
		||||
static bool is_valid_fwmark(string_span_t s)
 | 
			
		||||
{
 | 
			
		||||
	if (is_same(s, "off"))
 | 
			
		||||
		return true;
 | 
			
		||||
	return is_valid_uint(s, true, 0, 4294967295);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_valid_table(string_span_t s)
 | 
			
		||||
{
 | 
			
		||||
	if (is_same(s, "auto"))
 | 
			
		||||
		return true;
 | 
			
		||||
	if (is_same(s, "off"))
 | 
			
		||||
		return true;
 | 
			
		||||
	/* This pretty much invalidates the other checks, but rt_names.c's
 | 
			
		||||
	 * fread_id_name does no validation aside from this. */
 | 
			
		||||
	if (s.len < 512)
 | 
			
		||||
		return true;
 | 
			
		||||
	return is_valid_uint(s, false, 0, 4294967295);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_valid_saveconfig(string_span_t s)
 | 
			
		||||
{
 | 
			
		||||
	return is_same(s, "true") || is_same(s, "false");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_valid_prepostupdown(string_span_t s)
 | 
			
		||||
{
 | 
			
		||||
	/* It's probably not worthwhile to try to validate a bash expression.
 | 
			
		||||
	 * So instead we just demand non-zero length. */
 | 
			
		||||
	return s.len;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static bool is_valid_scope(string_span_t s)
 | 
			
		||||
{
 | 
			
		||||
	if (s.len > 64 || !s.len)
 | 
			
		||||
		return false;
 | 
			
		||||
	for (size_t i = 0; i < s.len; ++i) {
 | 
			
		||||
		if (!is_alphabet(s.s[i]) && !is_decimal(s.s[i]) &&
 | 
			
		||||
		    s.s[i] != '_' && s.s[i] != '=' && s.s[i] != '+' &&
 | 
			
		||||
		    s.s[i] != '.' && s.s[i] != '-')
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_valid_endpoint(string_span_t s)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	if (!s.len)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (s.s[0] == '[') {
 | 
			
		||||
		bool seen_scope = false;
 | 
			
		||||
		string_span_t hostspan = { s.s + 1, 0 };
 | 
			
		||||
 | 
			
		||||
		for (size_t i = 1; i < s.len; ++i) {
 | 
			
		||||
			if (s.s[i] == '%') {
 | 
			
		||||
				if (seen_scope)
 | 
			
		||||
					return false;
 | 
			
		||||
				seen_scope = true;
 | 
			
		||||
				if (!is_valid_ipv6(hostspan))
 | 
			
		||||
					return false;
 | 
			
		||||
				hostspan = (string_span_t){ s.s + i + 1, 0 };
 | 
			
		||||
			} else if (s.s[i] == ']') {
 | 
			
		||||
				if (seen_scope) {
 | 
			
		||||
					if (!is_valid_scope(hostspan))
 | 
			
		||||
						return false;
 | 
			
		||||
				} else if (!is_valid_ipv6(hostspan)) {
 | 
			
		||||
					return false;
 | 
			
		||||
				}
 | 
			
		||||
				if (i == s.len - 1 || s.s[i + 1] != ':')
 | 
			
		||||
					return false;
 | 
			
		||||
				return is_valid_port((string_span_t){ s.s + i + 2, s.len - i - 2 });
 | 
			
		||||
			} else {
 | 
			
		||||
				++hostspan.len;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	for (size_t i = 0; i < s.len; ++i) {
 | 
			
		||||
		if (s.s[i] == ':') {
 | 
			
		||||
			string_span_t host = { s.s, i }, port = { s.s + i + 1, s.len - i - 1};
 | 
			
		||||
			return is_valid_port(port) && (is_valid_ipv4(host) || is_valid_hostname(host));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_valid_network(string_span_t s)
 | 
			
		||||
{
 | 
			
		||||
	for (size_t i = 0; i < s.len; ++i) {
 | 
			
		||||
		if (s.s[i] == '/') {
 | 
			
		||||
			string_span_t ip = { s.s, i }, cidr = { s.s + i + 1, s.len - i - 1};
 | 
			
		||||
			uint16_t cidrval = 0;
 | 
			
		||||
 | 
			
		||||
			if (cidr.len > 3 || !cidr.len)
 | 
			
		||||
				return false;
 | 
			
		||||
 | 
			
		||||
			for (size_t j = 0; j < cidr.len; ++j) {
 | 
			
		||||
				if (!is_decimal(cidr.s[j]))
 | 
			
		||||
					return false;
 | 
			
		||||
				cidrval = 10 * cidrval + cidr.s[j] - '0';
 | 
			
		||||
			}
 | 
			
		||||
			if (is_valid_ipv4(ip))
 | 
			
		||||
				return cidrval <= 32;
 | 
			
		||||
			else if (is_valid_ipv6(ip))
 | 
			
		||||
				return cidrval <= 128;
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return is_valid_ipv4(s) || is_valid_ipv6(s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_valid_dns(string_span_t s)
 | 
			
		||||
{
 | 
			
		||||
	return is_valid_ipv4(s) || is_valid_ipv6(s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum field {
 | 
			
		||||
	InterfaceSection,
 | 
			
		||||
	PrivateKey,
 | 
			
		||||
	ListenPort,
 | 
			
		||||
	Address,
 | 
			
		||||
	DNS,
 | 
			
		||||
	MTU,
 | 
			
		||||
#ifndef MOBILE_WGQUICK_SUBSET
 | 
			
		||||
	FwMark,
 | 
			
		||||
	Table,
 | 
			
		||||
	PreUp, PostUp, PreDown, PostDown,
 | 
			
		||||
	SaveConfig,
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	PeerSection,
 | 
			
		||||
	PublicKey,
 | 
			
		||||
	PresharedKey,
 | 
			
		||||
	AllowedIPs,
 | 
			
		||||
	Endpoint,
 | 
			
		||||
	PersistentKeepalive,
 | 
			
		||||
 | 
			
		||||
	Invalid
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static enum field section_for_field(enum field t)
 | 
			
		||||
{
 | 
			
		||||
	if (t > InterfaceSection && t < PeerSection)
 | 
			
		||||
		return InterfaceSection;
 | 
			
		||||
	if (t > PeerSection && t < Invalid)
 | 
			
		||||
		return PeerSection;
 | 
			
		||||
	return Invalid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static enum field get_field(string_span_t s)
 | 
			
		||||
{
 | 
			
		||||
#define check_enum(t) do { if (is_caseless_same(s, #t)) return t; } while (0)
 | 
			
		||||
	check_enum(PrivateKey);
 | 
			
		||||
	check_enum(ListenPort);
 | 
			
		||||
	check_enum(Address);
 | 
			
		||||
	check_enum(DNS);
 | 
			
		||||
	check_enum(MTU);
 | 
			
		||||
	check_enum(PublicKey);
 | 
			
		||||
	check_enum(PresharedKey);
 | 
			
		||||
	check_enum(AllowedIPs);
 | 
			
		||||
	check_enum(Endpoint);
 | 
			
		||||
	check_enum(PersistentKeepalive);
 | 
			
		||||
#ifndef MOBILE_WGQUICK_SUBSET
 | 
			
		||||
	check_enum(FwMark);
 | 
			
		||||
	check_enum(Table);
 | 
			
		||||
	check_enum(PreUp);
 | 
			
		||||
	check_enum(PostUp);
 | 
			
		||||
	check_enum(PreDown);
 | 
			
		||||
	check_enum(PostDown);
 | 
			
		||||
	check_enum(SaveConfig);
 | 
			
		||||
#endif
 | 
			
		||||
	return Invalid;
 | 
			
		||||
#undef check_enum
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static enum field get_sectiontype(string_span_t s)
 | 
			
		||||
{
 | 
			
		||||
	if (is_caseless_same(s, "[Peer]"))
 | 
			
		||||
		return PeerSection;
 | 
			
		||||
	if (is_caseless_same(s, "[Interface]"))
 | 
			
		||||
		return InterfaceSection;
 | 
			
		||||
	return Invalid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct highlight_span_array {
 | 
			
		||||
	size_t len, capacity;
 | 
			
		||||
	struct highlight_span *spans;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* A useful OpenBSD-ism. */
 | 
			
		||||
static void *realloc_array(void *optr, size_t nmemb, size_t size)
 | 
			
		||||
{
 | 
			
		||||
	if ((nmemb >= (size_t)1 << (sizeof(size_t) * 4) ||
 | 
			
		||||
	     size >= (size_t)1 << (sizeof(size_t) * 4)) &&
 | 
			
		||||
	    nmemb > 0 && SIZE_MAX / nmemb < size) {
 | 
			
		||||
		errno = ENOMEM;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	return realloc(optr, size * nmemb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool append_highlight_span(struct highlight_span_array *a, const char *o, string_span_t s, enum highlight_type t)
 | 
			
		||||
{
 | 
			
		||||
	if (!s.len)
 | 
			
		||||
		return true;
 | 
			
		||||
	if (a->len >= a->capacity) {
 | 
			
		||||
		struct highlight_span *resized;
 | 
			
		||||
 | 
			
		||||
		a->capacity = a->capacity ? a->capacity * 2 : 64;
 | 
			
		||||
		resized = realloc_array(a->spans, a->capacity, sizeof(*resized));
 | 
			
		||||
		if (!resized) {
 | 
			
		||||
			free(a->spans);
 | 
			
		||||
			memset(a, 0, sizeof(*a));
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		a->spans = resized;
 | 
			
		||||
	}
 | 
			
		||||
	a->spans[a->len++] = (struct highlight_span){ t, s.s - o, s.len };
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void highlight_multivalue_value(struct highlight_span_array *ret, const string_span_t parent, const string_span_t s, enum field section)
 | 
			
		||||
{
 | 
			
		||||
	switch (section) {
 | 
			
		||||
	case DNS:
 | 
			
		||||
		append_highlight_span(ret, parent.s, s, is_valid_dns(s) ? HighlightIP : HighlightError);
 | 
			
		||||
		break;
 | 
			
		||||
	case Address:
 | 
			
		||||
	case AllowedIPs: {
 | 
			
		||||
		size_t slash;
 | 
			
		||||
 | 
			
		||||
		if (!is_valid_network(s)) {
 | 
			
		||||
			append_highlight_span(ret, parent.s, s, HighlightError);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		for (slash = 0; slash < s.len; ++slash) {
 | 
			
		||||
			if (s.s[slash] == '/')
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
		if (slash == s.len) {
 | 
			
		||||
			append_highlight_span(ret, parent.s, s, HighlightIP);
 | 
			
		||||
		} else {
 | 
			
		||||
			append_highlight_span(ret, parent.s, (string_span_t){ s.s, slash }, HighlightIP);
 | 
			
		||||
			append_highlight_span(ret, parent.s, (string_span_t){ s.s + slash, 1 }, HighlightDelimiter);
 | 
			
		||||
			append_highlight_span(ret, parent.s, (string_span_t){ s.s + slash + 1, s.len - slash - 1 }, HighlightCidr);
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	default:
 | 
			
		||||
		append_highlight_span(ret, parent.s, s, HighlightError);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void highlight_multivalue(struct highlight_span_array *ret, const string_span_t parent, const string_span_t s, enum field section)
 | 
			
		||||
{
 | 
			
		||||
	string_span_t current_span = { s.s, 0 };
 | 
			
		||||
	size_t len_at_last_space = 0;
 | 
			
		||||
 | 
			
		||||
	for (size_t i = 0; i < s.len; ++i) {
 | 
			
		||||
		if (s.s[i] == ',') {
 | 
			
		||||
			current_span.len = len_at_last_space;
 | 
			
		||||
			highlight_multivalue_value(ret, parent, current_span, section);
 | 
			
		||||
			append_highlight_span(ret, parent.s, (string_span_t){ s.s + i, 1 }, HighlightDelimiter);
 | 
			
		||||
			len_at_last_space = 0;
 | 
			
		||||
			current_span = (string_span_t){ s.s + i + 1, 0 };
 | 
			
		||||
		} else if (s.s[i] == ' ' || s.s[i] == '\t') {
 | 
			
		||||
			if (&s.s[i] == current_span.s && !current_span.len)
 | 
			
		||||
				++current_span.s;
 | 
			
		||||
			else
 | 
			
		||||
				++current_span.len;
 | 
			
		||||
		} else {
 | 
			
		||||
			len_at_last_space = ++current_span.len;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	current_span.len = len_at_last_space;
 | 
			
		||||
	if (current_span.len)
 | 
			
		||||
		highlight_multivalue_value(ret, parent, current_span, section);
 | 
			
		||||
	else if (ret->spans[ret->len - 1].type == HighlightDelimiter)
 | 
			
		||||
		ret->spans[ret->len - 1].type = HighlightError;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void highlight_value(struct highlight_span_array *ret, const string_span_t parent, const string_span_t s, enum field section)
 | 
			
		||||
{
 | 
			
		||||
	switch (section) {
 | 
			
		||||
	case PrivateKey:
 | 
			
		||||
		append_highlight_span(ret, parent.s, s, is_valid_key(s) ? HighlightPrivateKey : HighlightError);
 | 
			
		||||
		break;
 | 
			
		||||
	case PublicKey:
 | 
			
		||||
		append_highlight_span(ret, parent.s, s, is_valid_key(s) ? HighlightPublicKey : HighlightError);
 | 
			
		||||
		break;
 | 
			
		||||
	case PresharedKey:
 | 
			
		||||
		append_highlight_span(ret, parent.s, s, is_valid_key(s) ? HighlightPresharedKey : HighlightError);
 | 
			
		||||
		break;
 | 
			
		||||
	case MTU:
 | 
			
		||||
		append_highlight_span(ret, parent.s, s, is_valid_mtu(s) ? HighlightMTU : HighlightError);
 | 
			
		||||
		break;
 | 
			
		||||
#ifndef MOBILE_WGQUICK_SUBSET
 | 
			
		||||
	case SaveConfig:
 | 
			
		||||
		append_highlight_span(ret, parent.s, s, is_valid_saveconfig(s) ? HighlightSaveConfig : HighlightError);
 | 
			
		||||
		break;
 | 
			
		||||
	case FwMark:
 | 
			
		||||
		append_highlight_span(ret, parent.s, s, is_valid_fwmark(s) ? HighlightFwMark : HighlightError);
 | 
			
		||||
		break;
 | 
			
		||||
	case Table:
 | 
			
		||||
		append_highlight_span(ret, parent.s, s, is_valid_table(s) ? HighlightTable : HighlightError);
 | 
			
		||||
		break;
 | 
			
		||||
	case PreUp:
 | 
			
		||||
	case PostUp:
 | 
			
		||||
	case PreDown:
 | 
			
		||||
	case PostDown:
 | 
			
		||||
		append_highlight_span(ret, parent.s, s, is_valid_prepostupdown(s) ? HighlightCmd : HighlightError);
 | 
			
		||||
		break;
 | 
			
		||||
#endif
 | 
			
		||||
	case ListenPort:
 | 
			
		||||
		append_highlight_span(ret, parent.s, s, is_valid_port(s) ? HighlightPort : HighlightError);
 | 
			
		||||
		break;
 | 
			
		||||
	case PersistentKeepalive:
 | 
			
		||||
		append_highlight_span(ret, parent.s, s, is_valid_persistentkeepalive(s) ? HighlightKeepalive : HighlightError);
 | 
			
		||||
		break;
 | 
			
		||||
	case Endpoint: {
 | 
			
		||||
		size_t colon;
 | 
			
		||||
 | 
			
		||||
		if (!is_valid_endpoint(s)) {
 | 
			
		||||
			append_highlight_span(ret, parent.s, s, HighlightError);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		for (colon = s.len; colon --> 0;) {
 | 
			
		||||
			if (s.s[colon] == ':')
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
		append_highlight_span(ret, parent.s, (string_span_t){ s.s, colon }, HighlightHost);
 | 
			
		||||
		append_highlight_span(ret, parent.s, (string_span_t){ s.s + colon, 1 }, HighlightDelimiter);
 | 
			
		||||
		append_highlight_span(ret, parent.s, (string_span_t){ s.s + colon + 1, s.len - colon - 1 }, HighlightPort);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	case Address:
 | 
			
		||||
	case DNS:
 | 
			
		||||
	case AllowedIPs:
 | 
			
		||||
		highlight_multivalue(ret, parent, s, section);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		append_highlight_span(ret, parent.s, s, HighlightError);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct highlight_span *highlight_config(const char *config)
 | 
			
		||||
{
 | 
			
		||||
	struct highlight_span_array ret = { 0 };
 | 
			
		||||
	const string_span_t s = { config, strlen(config) };
 | 
			
		||||
	string_span_t current_span = { s.s, 0 };
 | 
			
		||||
	enum field current_section = Invalid, current_field = Invalid;
 | 
			
		||||
	enum { OnNone, OnKey, OnValue, OnComment, OnSection } state = OnNone;
 | 
			
		||||
	size_t len_at_last_space = 0, equals_location = 0;
 | 
			
		||||
 | 
			
		||||
	for (size_t i = 0; i <= s.len; ++i) {
 | 
			
		||||
		if (i == s.len || s.s[i] == '\n' || (state != OnComment && s.s[i] == '#')) {
 | 
			
		||||
			if (state == OnKey) {
 | 
			
		||||
				current_span.len = len_at_last_space;
 | 
			
		||||
				append_highlight_span(&ret, s.s, current_span, HighlightError);
 | 
			
		||||
			} else if (state == OnValue) {
 | 
			
		||||
				if (current_span.len) {
 | 
			
		||||
					append_highlight_span(&ret, s.s, (string_span_t){ s.s + equals_location, 1 }, HighlightDelimiter);
 | 
			
		||||
					current_span.len = len_at_last_space;
 | 
			
		||||
					highlight_value(&ret, s, current_span, current_field);
 | 
			
		||||
				} else {
 | 
			
		||||
					append_highlight_span(&ret, s.s, (string_span_t){ s.s + equals_location, 1 }, HighlightError);
 | 
			
		||||
				}
 | 
			
		||||
			} else if (state == OnSection) {
 | 
			
		||||
				current_span.len = len_at_last_space;
 | 
			
		||||
				current_section = get_sectiontype(current_span);
 | 
			
		||||
				append_highlight_span(&ret, s.s, current_span, current_section == Invalid ? HighlightError : HighlightSection);
 | 
			
		||||
			} else if (state == OnComment) {
 | 
			
		||||
				append_highlight_span(&ret, s.s, current_span, HighlightComment);
 | 
			
		||||
			}
 | 
			
		||||
			if (i == s.len)
 | 
			
		||||
				break;
 | 
			
		||||
			len_at_last_space = 0;
 | 
			
		||||
			current_field = Invalid;
 | 
			
		||||
			if (s.s[i] == '#') {
 | 
			
		||||
				current_span = (string_span_t){ s.s + i, 1 };
 | 
			
		||||
				state = OnComment;
 | 
			
		||||
			} else {
 | 
			
		||||
				current_span = (string_span_t){ s.s + i + 1, 0 };
 | 
			
		||||
				state = OnNone;
 | 
			
		||||
			}
 | 
			
		||||
		} else if (state == OnComment) {
 | 
			
		||||
			++current_span.len;
 | 
			
		||||
		} else if (s.s[i] == ' ' || s.s[i] == '\t') {
 | 
			
		||||
			if (&s.s[i] == current_span.s && !current_span.len)
 | 
			
		||||
				++current_span.s;
 | 
			
		||||
			else
 | 
			
		||||
				++current_span.len;
 | 
			
		||||
		} else if (s.s[i] == '=' && state == OnKey) {
 | 
			
		||||
			current_span.len = len_at_last_space;
 | 
			
		||||
			current_field = get_field(current_span);
 | 
			
		||||
			enum field section = section_for_field(current_field);
 | 
			
		||||
			if (section == Invalid || current_field == Invalid || section != current_section)
 | 
			
		||||
				append_highlight_span(&ret, s.s, current_span, HighlightError);
 | 
			
		||||
			else
 | 
			
		||||
				append_highlight_span(&ret, s.s, current_span, HighlightField);
 | 
			
		||||
			equals_location = i;
 | 
			
		||||
			current_span = (string_span_t){ s.s + i + 1, 0 };
 | 
			
		||||
			state = OnValue;
 | 
			
		||||
		} else {
 | 
			
		||||
			if (state == OnNone)
 | 
			
		||||
				state = s.s[i] == '[' ? OnSection : OnKey;
 | 
			
		||||
			len_at_last_space = ++current_span.len;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	append_highlight_span(&ret, s.s, (string_span_t){ s.s, -1 }, HighlightEnd);
 | 
			
		||||
	return ret.spans;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								contrib/highlighter/highlighter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								contrib/highlighter/highlighter.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
			
		||||
/* SPDX-License-Identifier: GPL-2.0 */
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
 | 
			
		||||
enum highlight_type {
 | 
			
		||||
	HighlightSection,
 | 
			
		||||
	HighlightField,
 | 
			
		||||
	HighlightPrivateKey,
 | 
			
		||||
	HighlightPublicKey,
 | 
			
		||||
	HighlightPresharedKey,
 | 
			
		||||
	HighlightIP,
 | 
			
		||||
	HighlightCidr,
 | 
			
		||||
	HighlightHost,
 | 
			
		||||
	HighlightPort,
 | 
			
		||||
	HighlightMTU,
 | 
			
		||||
	HighlightKeepalive,
 | 
			
		||||
	HighlightComment,
 | 
			
		||||
	HighlightDelimiter,
 | 
			
		||||
#ifndef MOBILE_WGQUICK_SUBSET
 | 
			
		||||
	HighlightTable,
 | 
			
		||||
	HighlightFwMark,
 | 
			
		||||
	HighlightSaveConfig,
 | 
			
		||||
	HighlightCmd,
 | 
			
		||||
#endif
 | 
			
		||||
	HighlightError,
 | 
			
		||||
	HighlightEnd
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct highlight_span {
 | 
			
		||||
	enum highlight_type type;
 | 
			
		||||
	size_t start, len;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct highlight_span *highlight_config(const char *config);
 | 
			
		||||
		Reference in New Issue
	
	Block a user