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);
+}
--
⑨