diff --git a/NOTES.md b/NOTES.md
new file mode 100644
index 0000000000000000000000000000000000000000..9ce89dc3f071adfc4d3def1017b0a46515d48f69
--- /dev/null
+++ b/NOTES.md
@@ -0,0 +1,11 @@
+# Development notes
+
+These are the development notes for the Go version.
+
+The idea is to use the help files to implement BULLETIN.
+
+## Module links
+
+ * cli ([docs](https://cli.urfave.org/v3/getting-started/), [github](https://pkg.go.dev/github.com/urfave/cli/v3))
+ * readline ([docs](https://pkg.go.dev/github.com/chzyer/readline), [github](https://github.com/chzyer/readline))
+ * xdg ([docs](https://pkg.go.dev/github.com/adrg/xdg), [github](https://github.com/adrg/xdg))
diff --git a/accounts/accounts.go b/accounts/accounts.go
index 32412dd489ffa0b5714fbae70a4f63c2f17d24ac..beb1c48f95d204ccc73930ceb77c96dfdd57525a 100644
--- a/accounts/accounts.go
+++ b/accounts/accounts.go
@@ -10,3 +10,8 @@ func Verify(acc string) error {
}
return nil
}
+
+// IsAdmin returns true if the user is an admin
+func IsAdmin(acc string) bool {
+ return false
+}
diff --git a/go.mod b/go.mod
index a82526a6e502dfec887a738223b22fb03a5595ff..a7c9849b48a2fcb5ed8f42bdcc3004683d46f4c7 100644
--- a/go.mod
+++ b/go.mod
@@ -2,4 +2,8 @@ module git.lyda.ie/kevin/bulletin
go 1.24.2
-require github.com/urfave/cli/v3 v3.3.2 // indirect
+require (
+ github.com/chzyer/readline v1.5.1 // indirect
+ github.com/urfave/cli/v3 v3.3.2 // indirect
+ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect
+)
diff --git a/go.sum b/go.sum
index 80dc36d5fb7c9d6272c40569bcad852a0bb49e34..e3a5fccaaa4e63c632e27b59b7223a1c62204460 100644
--- a/go.sum
+++ b/go.sum
@@ -1,2 +1,8 @@
+github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
+github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
+github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
+github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/urfave/cli/v3 v3.3.2 h1:BYFVnhhZ8RqT38DxEYVFPPmGFTEf7tJwySTXsVRrS/o=
github.com/urfave/cli/v3 v3.3.2/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
+golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 h1:y/woIyUBFbpQGKS0u1aHF/40WUDnek3fPOyD08H5Vng=
+golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/main.go b/main.go
index 6df8ee6c8e51616777344a7807dc47ed67e98369..be02f5d8c5866eed92d45de4dc64730154836c59 100644
--- a/main.go
+++ b/main.go
@@ -8,6 +8,7 @@ import (
"os"
"git.lyda.ie/kevin/bulletin/accounts"
+ "git.lyda.ie/kevin/bulletin/repl"
"github.com/urfave/cli/v3"
)
@@ -32,7 +33,10 @@ func main() {
if err != nil {
return err
}
- fmt.Println("Running as", user)
+ err = repl.Loop(user)
+ if err != nil {
+ return err
+ }
return nil
},
}
diff --git a/repl/command.go b/repl/command.go
new file mode 100644
index 0000000000000000000000000000000000000000..5567900ba7bf065af140e08863794346ad293653
--- /dev/null
+++ b/repl/command.go
@@ -0,0 +1,412 @@
+// Package repl implements the main event loop.
+package repl
+
+type command struct {
+ command string
+ boolFlags []string
+ strFlags []string
+ args []string
+ commands []*command
+ action func(string, *command) error
+ }
+
+var commands = []*command{
+ {
+ command: "ADD",
+ boolFlags: []string{
+ "/ALL",
+ "/BELL",
+ "/BROADCAST",
+ "/CLUSTER",
+ "/EDIT",
+ "/EXPIRATION",
+ "/EXTRACT",
+ "/FOLDER",
+ "/LOCAL",
+ "/NODES",
+ "/NOINDENT",
+ "/PERMANENT",
+ "/SUBJECT",
+ "/SHUTDOWN",
+ "/SYSTEM",
+ "/USERNAME",
+ },
+ },
+ {
+ command: "ATTACH",
+ // 2 Parameters
+ // 2 Qualifiers
+ // 2 Examples
+ },
+ {
+ command: "BACK",
+ },
+ {
+ command: "BULLETIN",
+ },
+ {
+ command: "CHANGE",
+ boolFlags: []string{
+ "/ALL",
+ "/EDIT",
+ "/EXPIRATION",
+ "/GENERAL",
+ "/HEADER",
+ "/NEW",
+ "/NUMBER",
+ "/PERMANENT",
+ "/SHUTDOWN[=nodename]",
+ "/SUBJECT",
+ "/SYSTEM",
+ "/TEXT",
+ },
+ },
+ {
+ command: "COPY",
+ boolFlags: []string{
+ "/ALL",
+ "/MERGE",
+ "/ORIGINAL",
+ },
+ },
+ {
+ command: "CREATE",
+ boolFlags: []string{
+ "/BRIEF",
+ "/DESCRIPTION",
+ "/ID",
+ "/NODE",
+ "/NOTIFY",
+ "/OWNER",
+ "/PRIVATE",
+ "/READNEW",
+ "/REMOTENAME",
+ "/SHOWNEW",
+ "/SEMIPRIVATE",
+ "/SYSTEM",
+ },
+ },
+ {
+ command: "CURRENT",
+ boolFlags: []string{
+ "/EDIT",
+ },
+ },
+ {
+ command: "DELETE",
+ boolFlags: []string{
+ "/ALL",
+ "/IMMEDIATE",
+ "/NODES",
+ "/SUBJECT",
+ "/USERNAME",
+ },
+ },
+ {
+ command: "DIRECTORY",
+ boolFlags: []string{
+ "/DESCRIBE",
+ "/EXPIRATION",
+ "/FOLDERS",
+ "/MARKED",
+ "/NEW",
+ "/REPLY",
+ "/SEARCH",
+ "/SINCE",
+ "/START",
+ "/SUBJECT",
+ },
+ },
+ {
+ command: "EXIT",
+ },
+ {
+ command: "EXTRACT",
+ },
+ {
+ command: "FILE",
+ boolFlags: []string{
+ "/ALL",
+ "/FF",
+ "/HEADER",
+ "/NEW",
+ },
+ },
+ {
+ command: "Folders",
+ },
+ {
+ command: "HELP",
+ },
+ {
+ command: "INDEX",
+ boolFlags: []string{
+ "/MARKED",
+ "/NEW",
+ "/RESTART",
+ },
+ },
+ {
+ command: "KEYPAD",
+ },
+ {
+ command: "LAST",
+ },
+ {
+ command: "MAIL",
+ boolFlags: []string{
+ "/HEADER",
+ "/SUBJECT",
+ },
+ },
+ {
+ command: "MARK",
+ },
+ {
+ command: "MODIFY",
+ boolFlags: []string{
+ "/DESCRIPTION",
+ "/ID",
+ "/NAME",
+ "/OWNER",
+ },
+ },
+ {
+ command: "MOVE",
+ boolFlags: []string{
+ "/ALL",
+ "/MERGE",
+ "/ORIGINAL",
+ },
+ },
+ {
+ command: "NEXT",
+ },
+ {
+ command: "POST",
+ boolFlags: []string{
+ "/CC",
+ "/EDIT",
+ "/EXTRACT",
+ "/NOINDENT",
+ "/SUBJECT",
+ },
+ },
+ {
+ command: "PRINT",
+ boolFlags: []string{
+ "/ALL",
+ "/FORM",
+ "/HEADER",
+ "/NOTIFY",
+ "/QUEUE",
+ },
+ },
+ {
+ command: "READ",
+ boolFlags: []string{
+ "/EDIT",
+ "/MARKED",
+ "/NEW",
+ "/PAGE",
+ "/SINCE",
+ },
+ },
+ {
+ command: "REMOVE",
+ },
+ {
+ command: "REPLY",
+ boolFlags: []string{
+ "/EXTRACT",
+ "/NOINDENT",
+ },
+ },
+ {
+ command: "RESPOND",
+ boolFlags: []string{
+ "/CC",
+ "/EDIT",
+ "/EXTRACT",
+ "/LIST",
+ "/NOINDENT",
+ "/SUBJECT",
+ },
+ },
+ {
+ command: "QUIT",
+ },
+ {
+ command: "SEARCH",
+ boolFlags: []string{
+ "/REPLY",
+ "/REVERSE",
+ "/START",
+ "/SUBJECT",
+ },
+ },
+ {
+ command: "SELECT",
+ boolFlags: []string{
+ "/MARKED",
+ },
+ },
+ {
+ command: "SET",
+ commands: []*command{
+ {
+ command: "ACCESS",
+ boolFlags: []string{
+ //3 id
+ "/ALL",
+ "/READ",
+ //3 Warning
+ },
+ },
+ {
+ command: "BBOARD",
+ boolFlags: []string{
+ "/EXPIRATION",
+ "/SPECIAL",
+ "/VMSMAIL",
+ //3 More_information
+ },
+ },
+ {
+ command: "BRIEF",
+ boolFlags: []string{
+ "/ALL",
+ "/DEFAULT",
+ "/FOLDER",
+ "/PERMANENT",
+ },
+ },
+ {
+ command: "CONTINUOUS_BRIEF",
+ },
+ {
+ command: "DEFAULT_EXPIRE",
+ },
+ {
+ command: "DIGEST",
+ },
+ {
+ command: "DUMP",
+ },
+ {
+ command: "EXPIRE_LIMIT",
+ },
+ {
+ command: "FOLDER",
+ boolFlags: []string{
+ "/MARKED",
+ },
+ },
+ {
+ command: "GENERIC",
+ boolFlags: []string{
+ "/DAYS",
+ },
+ },
+ {
+ command: "KEYPAD",
+ },
+ {
+ command: "LOGIN",
+ },
+ {
+ command: "NODE",
+ boolFlags: []string{
+ "/FOLDER",
+ },
+ },
+ {
+ command: "NOTIFY",
+ boolFlags: []string{
+ "/ALL",
+ "/DEFAULT",
+ "/FOLDER",
+ "/PERMANENT",
+ },
+ },
+ {
+ command: "PAGE",
+ },
+ {
+ command: "PRIVILEGES",
+ boolFlags: []string{
+ "/ID",
+ },
+ },
+ {
+ command: "PROMPT_EXPIRE",
+ },
+ {
+ command: "READNEW",
+ boolFlags: []string{
+ "/ALL",
+ "/DEFAULT",
+ "/FOLDER",
+ "/PERMANENT",
+ },
+ },
+ {
+ command: "SHOWNEW",
+ boolFlags: []string{
+ "/ALL",
+ "/DEFAULT",
+ "/FOLDER",
+ "/PERMANENT",
+ },
+ },
+ {
+ command: "STRIP",
+ },
+ {
+ command: "SYSTEM",
+ },
+ },
+ },
+ {
+ command: "SHOW",
+ commands: []*command{
+ {
+ command: "FLAGS",
+ },
+ {
+ command: "FOLDER",
+ boolFlags: []string{
+ "/FULL",
+ },
+ },
+ {
+ command: "KEYPAD",
+ boolFlags: []string{
+ "/PRINT",
+ },
+ },
+ {
+ command: "NEW",
+ },
+ {
+ command: "PRIVILEGES",
+ },
+ {
+ command: "USER",
+ boolFlags: []string{
+ "/ALL",
+ "/LOGIN",
+ },
+ },
+ {
+ command: "VERSION",
+ },
+ {
+ command: "SPAWN",
+ },
+ {
+ command: "UNDELETE",
+ },
+ },
+ },
+}
diff --git a/repl/repl.go b/repl/repl.go
new file mode 100644
index 0000000000000000000000000000000000000000..ce2ecd2db47cb41cb60c7abbcc7d146c8993a198
--- /dev/null
+++ b/repl/repl.go
@@ -0,0 +1,36 @@
+// Package repl implements the main event loop.
+package repl
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/chzyer/readline"
+)
+
+// Loop is the main event loop.
+func Loop(user string) error {
+ fmt.Printf("TODO: get config for user %s using xdg.", user)
+ rl, err := readline.New("BULLETIN> ")
+ if err != nil {
+ return err
+ }
+ defer rl.Close()
+
+ for {
+ line, err := rl.Readline()
+ if err != nil {
+ return err
+ }
+ words := strings.Field(strings.ReplaceAll(line, "/", " /"))
+ if len(words) {
+ continue
+ }
+ switch strings.ToUpper(words[0]) {
+ case "ADD":
+ commandAdd()
+ }
+ }
+
+ return nil
+}