shithub: ircd

Download patch

ref: e5bc78b0a0ede131eabfbe2aa15415a82319206d
author: sirjofri <sirjofri@sirjofri.de>
date: Sat Jul 19 10:24:38 EDT 2025

adds files with working parser

--- /dev/null
+++ b/cmd.c
@@ -1,0 +1,41 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+static void
+cversion(Request *r)
+{
+	fprint(2, "running version command\n");
+}
+
+static Command commands[] = {
+	{ "version", cversion },
+};
+int ncommands = sizeof(commands) / sizeof(Command);
+
+Command*
+findcommand(char *s)
+{
+	for (int i = 0; i < ncommands; i++) {
+		if (cistrcmp(commands[i].name, s) == 0)
+			return &commands[i];
+	}
+	return nil;
+}
+
+Command*
+findcommandn(int n)
+{
+	return nil;
+}
+
+void
+execrequest(Request r)
+{
+	if (!(r.cmd && r.cmd->func)) {
+		fprint(2, "cannot execute request: no command\n");
+		return;
+	}
+	r.cmd->func(&r);
+}
--- /dev/null
+++ b/dat.h
@@ -1,0 +1,25 @@
+typedef struct Prefix Prefix;
+typedef struct Request Request;
+typedef struct Command Command;
+
+#pragma varargck type "R" Request
+
+struct Prefix
+{
+	char *name;
+	char *user;
+	char *host;
+};
+
+struct Request
+{
+	Command *cmd;
+	Prefix prefix;
+	char *args[15];
+};
+
+struct Command
+{
+	char *name;
+	void (*func)(Request*);
+};
--- /dev/null
+++ b/fmt.c
@@ -1,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+int
+Rfmt(Fmt *f)
+{
+	Request r;
+	r = va_arg(f->args, Request);
+	return fmtprint(f, "\n"
+		"  prefix: '%s' ! '%s' @ '%s'\n"
+		"  cmd: '%s'\n"
+		"  args: '%s' '%s' '%s' '%s' '%s'"
+		       " '%s' '%s' '%s' '%s' '%s'"
+		       " '%s' '%s' '%s' '%s' '%s'\n",
+		r.prefix.name, r.prefix.user, r.prefix.host,
+		r.cmd ? r.cmd->name : nil,
+		r.args[0],  r.args[1],  r.args[2],  r.args[3],  r.args[4],
+		r.args[5],  r.args[6],  r.args[7],  r.args[8],  r.args[9],
+		r.args[10], r.args[11], r.args[12], r.args[13], r.args[14]
+	);
+}
--- /dev/null
+++ b/fns.h
@@ -1,0 +1,8 @@
+Request parseline(char*);
+
+Command* findcommand(char*);
+Command* findcommandn(int);
+void execrequest(Request);
+void clearrequest(Request);
+
+int Rfmt(Fmt *f);
--- /dev/null
+++ b/ircd.c
@@ -1,0 +1,44 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "dat.h"
+#include "fns.h"
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s\n", argv0);
+	exits("usage");
+}
+
+static Biobuf *bio;
+
+static void
+parselines(void)
+{
+	char *line;
+	Command *cmd;
+	Request parsedrequest;
+	
+	while (line = Brdstr(bio, '\n', 1)) {
+		parsedrequest = parseline(line);
+		fprint(2, "request: %R\n", parsedrequest);
+		execrequest(parsedrequest);
+		free(line);
+	}
+}
+
+void
+main(int argc, char **argv)
+{
+	ARGBEGIN{
+	case 'h':
+		usage();
+		break;
+	}ARGEND;
+	
+	fmtinstall('R', Rfmt);
+	
+	bio = Bfdopen(0, OREAD);
+	parselines();
+}
--- /dev/null
+++ b/parse.c
@@ -1,0 +1,119 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+static Prefix
+parseprefix(char *line, char **next)
+{
+	/* nick!user@host or servername */
+	Prefix r;
+	char *c;
+	
+	r.name = nil;
+	r.user = nil;
+	r.host = nil;
+	
+	/* first, find end of prefix */
+	c = strchr(line, ' ');
+	if (c) {
+		while (*c == ' ') {
+			*c = 0;
+			c++;
+		}
+		*next = c;
+	} else {
+		*next = line;
+		return r;
+	}
+	
+	/* nick/servername follows ':' */
+	*line = 0;
+	r.name = &line[1]; /* fixed character position */
+	c = r.name;
+	
+	/* next field: user after '!' */
+	c = strchr(c, '!');
+	if (c) {
+		*c = 0;
+		r.user = &c[1];
+		c = r.user;
+	} else {
+		c = r.name;
+	}
+	
+	/* next field: host after '@' */
+	c = strchr(c, '@');
+	if (c) {
+		*c = 0;
+		r.host = &c[1];
+	}
+	return r;
+}
+
+static void
+parseargs(Request *r, char *line)
+{
+	char *c;
+	char *trailing;
+	int i, maxargs;
+	
+	maxargs = 15;
+	c = strchr(line, ':');
+	trailing = c ? &c[1] : nil;
+	if (c) {
+		*c = 0;
+		maxargs--;
+	}
+	
+	i = getfields(line, r->args, maxargs, 1, " ");
+	r->args[i] = trailing;
+}
+
+Request
+parseline(char *line)
+{
+	Request r;
+	char *l;
+	char *c;
+	
+	r.cmd = nil;
+	r.args[0] = nil;
+	r.prefix.name = nil;
+	r.prefix.user = nil;
+	r.prefix.host = nil;
+	
+	for (int i = 0; i < 15; i++) {
+		r.args[i] = nil;
+	}
+	
+	if (!line || line[0] == 0)
+		return r;
+	
+	l = line;
+	
+	/* prefix */
+	if (l[0] == ':') {
+		r.prefix = parseprefix(l, &l);
+	}
+	
+	/* command */
+	c = strchr(l, ' ');
+	if (!c) {
+		r.cmd = findcommand(l);
+		return r;
+	}
+	*c = 0;
+	r.cmd = findcommand(l);
+	
+	l = &c[1];
+	while (*l == ' ') {
+		*l = 0;
+		l++;
+	}
+	
+	if (l[0])
+		parseargs(&r, l);
+	
+	return r;
+}
--- /dev/null
+++ b/test/mkfile
@@ -1,0 +1,8 @@
+</$objtype/mkfile
+
+TEST=\
+	parsetest\
+
+</sys/src/cmd/mktest
+
+$O.parsetest: ../parse.$O ../cmd.$O ../fmt.$O
--- /dev/null
+++ b/test/parsetest.c
@@ -1,0 +1,133 @@
+#include <u.h>
+#include <libc.h>
+#include "../dat.h"
+#include "../fns.h"
+
+typedef struct Req Req;
+struct Req
+{
+	char *cmd;
+	char *pname;
+	char *puser;
+	char *phost;
+	char *args[5];
+};
+
+static int
+strmatch(char *is, char *should)
+{
+	if (!!is == !!should) {
+		if (is && should)
+			return !strcmp(is, should);
+	} else
+		return 0;
+	return 1;
+}
+
+static int
+runtest(char *line, Req ex)
+{
+	char copy[513];
+	strncpy(copy, line, 513);
+	Request r = parseline(line);
+	
+	
+	if (!ex.cmd) {
+		/* if invalid command, but valid result */
+		if (r.cmd)
+			goto Fail;
+		if (r.cmd && r.cmd->name)
+			goto Fail;
+	} else {
+		/* if expected command */
+		if (!r.cmd || r.cmd->name) {
+			/* if command does not match */
+			if (strcmp(r.cmd->name, ex.cmd))
+				goto Fail;
+		}
+	}
+	
+	if (!strmatch(ex.pname, r.prefix.name))
+		goto Fail;
+	if (!strmatch(ex.puser, r.prefix.user))
+		goto Fail;
+	if (!strmatch(ex.phost, r.prefix.host))
+		goto Fail;
+	
+	for (int i = 0; i < 5; i++) {
+		if (!strmatch(ex.args[i], r.args[i]))
+			goto Fail;
+	}
+	
+	return 0;
+Fail:
+	fprint(2, "fail: %s\n", copy);
+	fprint(2, "got:%R\n", r);
+	return 1;
+}
+#define TEST(LINE, EXP) failed += runtest(LINE, EXP)
+
+static Req
+mkreq(char *cmd, char *pname, char *puser, char *phost, char *a1, char *a2, char *a3, char *a4, char *a5)
+{
+	Req r;
+	r.cmd = cmd;
+	r.pname = pname;
+	r.puser = puser;
+	r.phost = phost;
+	r.args[0] = a1;
+	r.args[1] = a2;
+	r.args[2] = a3;
+	r.args[3] = a4;
+	r.args[4] = a5;
+	return r;
+}
+
+void
+main(int, char**)
+{
+	int failed = 0;
+	fmtinstall('R', Rfmt);
+	
+	TEST("version",
+		mkreq("version", nil, nil, nil,
+		nil, nil, nil, nil, nil));
+	
+	TEST(":name version",
+		mkreq("version", "name", nil, nil,
+		nil, nil, nil, nil, nil));
+	
+	TEST(":name!user version",
+		mkreq("version", "name", "user", nil,
+		nil, nil, nil, nil, nil));
+	
+	TEST(":name!user@host version",
+		mkreq("version", "name", "user", "host",
+		nil, nil, nil, nil, nil));
+	
+	TEST(":name@host version",
+		mkreq("version", "name", nil, "host",
+		nil, nil, nil, nil, nil));
+	
+	TEST(":name!user@host version arg1",
+		mkreq("version", "name", "user", "host",
+		"arg1", nil, nil, nil, nil));
+	
+	TEST(":name version arg1 arg2",
+		mkreq("version", "name", nil, nil,
+		"arg1", "arg2", nil, nil, nil));
+	
+	TEST(":name version arg1 :arg2 with spaces",
+		mkreq("version", "name", nil, nil,
+		"arg1", "arg2 with spaces", nil, nil, nil));
+	
+	TEST("version arg1 arg2",
+		mkreq("version", nil, nil, nil,
+		"arg1", "arg2", nil, nil, nil));
+	
+	TEST("version arg1 :arg2 with spaces",
+		mkreq("version", nil, nil, nil,
+		"arg1", "arg2 with spaces", nil, nil, nil));
+	
+	exits(failed ? "failed" : nil);
+}
--