diff --git a/NOTES.md b/NOTES.md
index 16aa923e155942dfdd07ac96fdfcde6441b590f0..dcea2dbc93ca931b2db98cd2c08763bdf8b1dff0 100644
--- a/NOTES.md
+++ b/NOTES.md
@@ -60,7 +60,7 @@ repl.commands?
     * Commands for a local mail system?
     * Commands to connect to Mattermost or mastodon?
     * Commands to manage users.
-  * `SHOW VERSION` - versioninfo doesn't work; what else?
+  * `SHOW VERSION` - [versioninfo](https://github.com/earthboundkid/versioninfo) will work for go install.
   * Check db version; notify user if it changes; refuse to write to db if it has.
 
 ## Module links
diff --git a/accounts/accounts.go b/accounts/accounts.go
index b905cb16e99e4475cf70f5c6d718dbec4d578a44..becd7b99c8edc58abdcaea8bac39bf869f2d0b9e 100644
--- a/accounts/accounts.go
+++ b/accounts/accounts.go
@@ -36,7 +36,7 @@ func ValidName(login string) error {
 }
 
 // Open verifies that an account exists.
-func Open(login string) error {
+func Open(login, name string) error {
 	err := ValidName(login)
 	if err != nil {
 		return err
@@ -55,10 +55,13 @@ func Open(login string) error {
 		user.Login = login
 		user.Admin = 0
 
-		fmt.Printf("Welcome new user %s\n", login)
-		user.Name, err = GetLine("please enter your name: ")
-		if err != nil {
-			return err
+		user.Name = name
+		if name == "" {
+			fmt.Printf("Welcome new user %s\n", login)
+			user.Name, err = GetLine("please enter your name: ")
+			if err != nil {
+				return err
+			}
 		}
 
 		err = User.Folders.AddUser(*user)
diff --git a/dclish/dclish.go b/dclish/dclish.go
index 237f1978d6f0328c5241cf7d91aa803ea685b382..3e6cb9e00c49d45f3323388bd3fd4d425c4291da 100644
--- a/dclish/dclish.go
+++ b/dclish/dclish.go
@@ -27,7 +27,7 @@ type Command struct {
 	Args        []string
 	MaxArgs     int
 	MinArgs     int
-	Commands    []*Command
+	Commands    Commands
 	Action      ActionFunc
 	Description string
 }
@@ -108,11 +108,13 @@ func split(line string) []string {
 
 // ParseAndRun parses a command line and runs the command.
 func (c Commands) ParseAndRun(line string) error {
-	// TODO: this doesn't handle a DCL command line completely.
+	// Split into words.
 	words := split(line)
-	cmd, ok := c[strings.ToUpper(words[0])]
+
+	// Find the command.
+	wordup := strings.ToUpper(words[0])
+	cmd, ok := c[wordup]
 	if !ok {
-		wordup := strings.ToUpper(words[0])
 		possibles := []string{}
 		for word := range c {
 			if strings.HasPrefix(word, wordup) {
@@ -124,13 +126,26 @@ func (c Commands) ParseAndRun(line string) error {
 			fmt.Printf("ERROR: Unknown command '%s'\n", words[0])
 			return nil
 		case 1:
-			cmd = c[possibles[0]]
+			wordup = possibles[0]
+			cmd = c[wordup]
 		default:
 			fmt.Printf("ERROR: Ambiguous command '%s' (matches %s)\n",
 				words[0], strings.Join(possibles, ", "))
 			return nil
 		}
 	}
+
+	// Deal with subcommands.
+	if len(cmd.Commands) > 0 {
+		if len(words) == 1 {
+			fmt.Printf("ERROR: missing subcommand for %s.\n", wordup)
+			return nil
+		}
+		// TODO: quoting is probably an issue here - break ParseAndRun into ParseAndRun + Run.
+		newline := strings.Join(words[1:], " ")
+		return cmd.Commands.ParseAndRun(newline)
+	}
+
 	if cmd.Action == nil {
 		fmt.Printf("ERROR: Command not implemented:\n%s\n", cmd.Description)
 		return nil
@@ -139,6 +154,7 @@ func (c Commands) ParseAndRun(line string) error {
 		cmd.Flags[flg].Value = cmd.Flags[flg].Default
 	}
 	cmd.Args = []string{}
+
 	if len(words) == 1 {
 		if len(cmd.Args) < cmd.MinArgs {
 			fmt.Println("ERROR: Not enough args.")
diff --git a/folders/sql/1_create_table.up.sql b/folders/sql/1_create_table.up.sql
index c8f55c2f0fe97672adcf357c6e56bc2f817c8b41..74bb11c115363182ad8a337d34b7d7cc3ae0cce8 100644
--- a/folders/sql/1_create_table.up.sql
+++ b/folders/sql/1_create_table.up.sql
@@ -3,6 +3,7 @@ CREATE TABLE users (
   name        VARCHAR(53)  NOT NULL,
   admin       INT          DEFAULT 0,
   disabled    INT          DEFAULT 0,
+  last_login  TIMESTAMP    DEFAULT CURRENT_TIMESTAMP NOT NULL,
   create_at   TIMESTAMP    DEFAULT CURRENT_TIMESTAMP NOT NULL,
   update_at   TIMESTAMP    DEFAULT CURRENT_TIMESTAMP NOT NULL
 ) WITHOUT ROWID;
@@ -140,3 +141,18 @@ CREATE TABLE mark (
     ON DELETE CASCADE
     ON UPDATE CASCADE
 ) WITHOUT ROWID;
+
+--- TODO: The following is incomplete:
+--- User configs.
+CREATE TABLE config (
+  login       VARCHAR(25) REFERENCES users(login) ON DELETE CASCADE ON UPDATE CASCADE,
+  folder      VARCHAR(25) REFERENCES folders(name) ON DELETE CASCADE ON UPDATE CASCADE,
+  always      INT NOT NULL,
+  alert       INT NOT NULL DEFAULT 0,  --- 0=no, 1=brief, 2=readnew
+) WITHOUT ROWID;
+
+--- System configs.
+CREATE TABLE system (
+  default_expire  INT NOT NULL DEFAULT -1,
+  expire_limit    INT NOT NULL DEFAULT -1,
+) WITHOUT ROWID;
diff --git a/go.mod b/go.mod
index 406d51c905576656a91fcd4844fb61d2a00a1d78..ef5e66584e5598fc3ff29d78b56d626b898ed9b8 100644
--- a/go.mod
+++ b/go.mod
@@ -5,8 +5,7 @@ go 1.24.2
 require (
 	github.com/adrg/xdg v0.5.3
 	github.com/chzyer/readline v1.5.1
-	github.com/davecgh/go-spew v1.1.1
-	github.com/gdamore/tcell/v2 v2.7.1
+	github.com/gdamore/tcell/v2 v2.8.1
 	github.com/golang-migrate/migrate/v4 v4.18.3
 	github.com/jmoiron/sqlx v1.4.0
 	github.com/rivo/tview v0.0.0-20250501113434-0c592cd31026
@@ -15,26 +14,24 @@ require (
 )
 
 require (
+	github.com/carlmjohnson/versioninfo v0.22.5 // indirect
 	github.com/dustin/go-humanize v1.0.1 // indirect
-	github.com/earthboundkid/versioninfo/v2 v2.24.1 // indirect
-	github.com/gdamore/encoding v1.0.0 // indirect
-	github.com/golang-migrate/migrate v3.5.4+incompatible // indirect
+	github.com/gdamore/encoding v1.0.1 // indirect
 	github.com/google/uuid v1.6.0 // indirect
 	github.com/hashicorp/errwrap v1.1.0 // indirect
 	github.com/hashicorp/go-multierror v1.1.1 // indirect
 	github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
-	github.com/mattn/go-runewidth v0.0.15 // indirect
-	github.com/mitchellh/go-wordwrap v1.0.1 // indirect
+	github.com/mattn/go-runewidth v0.0.16 // indirect
 	github.com/ncruces/go-strftime v0.1.9 // indirect
 	github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
 	github.com/rivo/uniseg v0.4.7 // indirect
 	go.uber.org/atomic v1.11.0 // indirect
 	golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
 	golang.org/x/sys v0.33.0 // indirect
-	golang.org/x/term v0.30.0 // indirect
-	golang.org/x/text v0.23.0 // indirect
-	modernc.org/libc v1.65.1 // indirect
+	golang.org/x/term v0.32.0 // indirect
+	golang.org/x/text v0.25.0 // indirect
+	modernc.org/libc v1.65.2 // indirect
 	modernc.org/mathutil v1.7.1 // indirect
 	modernc.org/memory v1.10.0 // indirect
 )
diff --git a/go.sum b/go.sum
index ec0708642f0bb9ccde75b2211857926955fbfb65..5fafbe6e01c71a8cc434e12b0802789ecbb39313 100644
--- a/go.sum
+++ b/go.sum
@@ -1,25 +1,30 @@
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
 filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
 github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
 github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
+github.com/carlmjohnson/versioninfo v0.22.5 h1:O00sjOLUAFxYQjlN/bzYTuZiS0y6fWDQjMRvwtKgwwc=
+github.com/carlmjohnson/versioninfo v0.22.5/go.mod h1:QT9mph3wcVfISUKd0i9sZfVrPviHuSF+cUtLjm2WSf8=
+github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
 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 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
 github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
-github.com/earthboundkid/versioninfo/v2 v2.24.1 h1:SJTMHaoUx3GzjjnUO1QzP3ZXK6Ee/nbWyCm58eY3oUg=
-github.com/earthboundkid/versioninfo/v2 v2.24.1/go.mod h1:VcWEooDEuyUJnMfbdTh0uFN4cfEIg+kHMuWB2CDCLjw=
-github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
-github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
-github.com/gdamore/tcell/v2 v2.7.1 h1:TiCcmpWHiAU7F0rA2I3S2Y4mmLmO9KHxJ7E1QhYzQbc=
-github.com/gdamore/tcell/v2 v2.7.1/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg=
+github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
+github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
+github.com/gdamore/tcell/v2 v2.8.1 h1:KPNxyqclpWpWQlPLx6Xui1pMk8S+7+R37h3g07997NU=
+github.com/gdamore/tcell/v2 v2.8.1/go.mod h1:bj8ori1BG3OYMjmb3IklZVWfZUJ1UBQt9JXrOCOhGWw=
+github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
 github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
-github.com/golang-migrate/migrate v3.5.4+incompatible h1:R7OzwvCJTCgwapPCiX6DyBiu2czIUMDCB118gFTKTUA=
-github.com/golang-migrate/migrate v3.5.4+incompatible/go.mod h1:IsVUlFN5puWOmXrqjgGUfIRIbU7mr8oNBE2tyERd9Wk=
 github.com/golang-migrate/migrate/v4 v4.18.3 h1:EYGkoOsvgHHfm5U/naS1RP/6PL/Xv3S4B/swMiAmDLs=
 github.com/golang-migrate/migrate/v4 v4.18.3/go.mod h1:99BKpIi6ruaaXRM1A77eqZ+FWPQ3cfRa+ZVy5bmWMaY=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
+github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -29,18 +34,19 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
 github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
 github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
 github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
+github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
 github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
 github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
-github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
+github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
 github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
-github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
-github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
 github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
 github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
@@ -50,6 +56,8 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
 github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
 github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
 github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/urfave/cli/v3 v3.3.2 h1:BYFVnhhZ8RqT38DxEYVFPPmGFTEf7tJwySTXsVRrS/o=
 github.com/urfave/cli/v3 v3.3.2/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@@ -57,54 +65,104 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
 go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
+golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
+golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
 golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
 golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
+golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
+golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
+golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
+golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-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=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
 golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
+golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
 golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
-golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
-golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
+golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
+golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
+golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
+golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
-golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
+golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
+golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
+golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
+golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
+golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-modernc.org/libc v1.65.0 h1:e183gLDnAp9VJh6gWKdTy0CThL9Pt7MfcR/0bgb7Y1Y=
-modernc.org/libc v1.65.0/go.mod h1:7m9VzGq7APssBTydds2zBcxGREwvIGpuUBaKTXdm2Qs=
-modernc.org/libc v1.65.1 h1:EwykJ3C7c5pCiZTU3dLkgRm3VdFGNFc8UXXzhhEZvbQ=
-modernc.org/libc v1.65.1/go.mod h1:+LU/iIPTqxVVdAl3E++KC9npafs4zI4pkLiolMVDatc=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
+modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
+modernc.org/ccgo/v4 v4.27.1 h1:emhLB4uoOmkZUnTDFcMI3AbkmU/Evjuerit9Taqe6Ss=
+modernc.org/ccgo/v4 v4.27.1/go.mod h1:543Q0qQhJWekKVS5P6yL5fO6liNhla9Lbm2/B3rEKDE=
+modernc.org/fileutil v1.3.1 h1:8vq5fe7jdtEvoCf3Zf9Nm0Q05sH6kGx0Op2CPx1wTC8=
+modernc.org/fileutil v1.3.1/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
+modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
+modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
+modernc.org/libc v1.65.2 h1:drWL1QO9fKXr3kXDN8y+4lKyBr8bA3mtUBQpftq3IJw=
+modernc.org/libc v1.65.2/go.mod h1:VI3V2S5mNka4deJErQ0jsMXe7jgxojE2fOB/mWoHlbc=
 modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
 modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
 modernc.org/memory v1.10.0 h1:fzumd51yQ1DxcOxSO+S6X7+QTuVU+n8/Aj7swYjFfC4=
 modernc.org/memory v1.10.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
+modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
+modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
+modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
+modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
 modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI=
 modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM=
+modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
+modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
+modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
+modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
diff --git a/main.go b/main.go
index e6acf8f7386ba4dd7d407a44e613aa45eda76519..65011cada29ab9c795fc1e7b03c2b5f9d93cf67a 100644
--- a/main.go
+++ b/main.go
@@ -26,6 +26,11 @@ func main() {
 				Usage:    "user to run bulletin as",
 				Required: true,
 			},
+			&cli.StringFlag{
+				Name:    "name",
+				Aliases: []string{"n"},
+				Usage:   "TODO: name for development",
+			},
 			&cli.StringFlag{
 				Name:    "batch",
 				Aliases: []string{"b"},
@@ -41,7 +46,7 @@ func main() {
 					fmt.Println("ERROR: can only run batch commands as SYSTEM.")
 					os.Exit(1)
 				}
-				err := accounts.Open(user)
+				err := accounts.Open(user, cmd.String("name"))
 				if err != nil {
 					fmt.Printf("ERROR: %s.", err)
 					os.Exit(1)
@@ -60,7 +65,8 @@ func main() {
 				}
 				os.Exit(exitcode)
 			}
-			err := accounts.Open(user)
+
+			err := accounts.Open(user, cmd.String("name"))
 			if err != nil {
 				return err
 			}
diff --git a/repl/command.go b/repl/command.go
index 11913d98e5e115c859d880e661a41e87cffa280e..9472a1cb11994eb76df9ea20ed280da2890ab998 100644
--- a/repl/command.go
+++ b/repl/command.go
@@ -147,8 +147,8 @@ changed, a file can be specified  which contains the text. If the editor
 is used for  changing the text, the old message  text will be extracted.
 This can be suppressed by the qualifier /NEW.
 
-    Format:
-      CHANGE [file-name]`,
+  Format:
+    CHANGE [file-name]`,
 		MaxArgs: 1,
 		Action:  ActionChange,
 		Flags: dclish.Flags{
@@ -712,36 +712,17 @@ specified as xxx%"""address""".`,
 		Description: `Sets the  current or message-id  message as marked. Marked  messages are
 displayed with  an asterisk  in the  left hand  column of  the directory
 listing.  A  marked  message  can  serve  as  a  reminder  of  important
-information. The UNMARK  command sets the current  or message-id message
-as unmarked.
-
-   Format:
-
-       MARK [message-number or numbers]
-       UNMARK [message-number or numbers]
+information.
 
-NOTE:   The   list  of   marked   messages   are   stored  in   a   file
-username.BULLMARK. The files are created  in the directory pointed to by
-the logical name BULL_MARK. If  BULL_MARK is not defined, SYS$LOGIN will
-be used.`,
+  Format:
+    MARK [message-number or numbers]`,
 		MaxArgs: 1,
 	},
 	"UNMARK": {
-		Description: `Sets the  current or message-id  message as marked. Marked  messages are
-displayed with  an asterisk  in the  left hand  column of  the directory
-listing.  A  marked  message  can  serve  as  a  reminder  of  important
-information. The UNMARK  command sets the current  or message-id message
-as unmarked.
-
-   Format:
+		Description: `Sets the current  or message-id message as unmarked.
 
-       MARK [message-number or numbers]
-       UNMARK [message-number or numbers]
-
-NOTE:   The   list  of   marked   messages   are   stored  in   a   file
-username.BULLMARK. The files are created  in the directory pointed to by
-the logical name BULL_MARK. If  BULL_MARK is not defined, SYS$LOGIN will
-be used.`,
+  Format:
+    UNMARK [message-number or numbers]`,
 		MaxArgs: 1,
 	},
 	"MODIFY": {
@@ -844,8 +825,8 @@ released to the print queue until you exit, unless you add the qualifier
 /NOW or change one of the  print job's qualifiers. Multiple messages are
 concatenated into one print job.
 
-   Format:
-       PRINT [message_number][-message_number1],[...]
+  Format:
+    PRINT [message_number][-message_number1],[...]
 
 A range  of messages  to be  printed can  optionally be  specified, i.e.
 PRINT 2-5.
@@ -910,8 +891,8 @@ will be  displayed. However, if  there are  new messages, the  first new
 message will  be displayed. Each  time you  enter the command,  the next
 page, or if there are no more pages, the next message will be displayed.
 
-  Format:
-    READ [message-number]
+    Format:
+      READ [message-number]
 
 The message's relative number is found  by the DIRECTORY command. If you
 specify a number greater than the  number of messages in the folder, the
@@ -995,8 +976,8 @@ the help on the SEEN command.`,
 		Description: `Removes a folder.  Only the owner of  a folder or a  privileged user can
 remove the folder.
 
-  Format:
-    REMOVE folder-name`,
+    Format:
+      REMOVE folder-name`,
 		MinArgs: 1,
 		MaxArgs: 1,
 		Action:  ActionRemove,
@@ -1006,8 +987,8 @@ remove the folder.
 read message with "RE:" preceeding it.  Format and qualifiers is exactly
 the same as the ADD command except for /NOINDENT and /EXTRACT.
 
-  Format:
-    REPLY [file-name]`,
+    Format:
+      REPLY [file-name]`,
 		MaxArgs: 1,
 		Action:  ActionReply,
 		Flags: dclish.Flags{
@@ -1033,8 +1014,8 @@ the same as the ADD command except for /NOINDENT and /EXTRACT.
 		Description: `Invokes the  VAX/VMS Personal Mail Utility  (MAIL) to send a  reply mail
 message to the owner of the currently read message.
 
-  Format:
-    RESPOND [file-name]
+    Format:
+      RESPOND [file-name]
 
 If  you  wish  to  use  another method  for  sending  the  mail,  define
 BULL_MAILER to point to a command procedure. This procedure will then be
@@ -1097,9 +1078,8 @@ username and subject of the message.`,
 		Description: `Searches the  currently selected folder  for the message  containing the
 first occurrence of the specified text string.
 
-   Format:
-
-       SEARCH [search-string]
+    Format:
+      SEARCH [search-string]
 
 The search  starts from  the first  message in  the current  folder. The
 search  includes both  the  text  of the  message,  and the  description
@@ -1151,46 +1131,22 @@ search can be aborted by typing a CTRL-C.`,
 track of  messages on a per  message basis. Seen messages  are displayed
 with  a greater  than sign  in  the left  hand column  of the  directory
 listing. Once  you have  used the  SEEN command  once, messages  will be
-automatically  be set  as  being SEEN  when they  are  read. The  UNSEEN
-command sets the current or message-id message as unseen.
-
-   Format:
+automatically  be set  as  being SEEN  when they  are  read.
 
-       SEEN [message-number or numbers]
-       UNSEEN [message-number or numbers]
+  Format:
+    SEEN [message-number or numbers]
 
 If you  have used  the SEEN  command and wish  to disable  the automatic
 marking of messages in regular folders  as SEEN when they are read, type
-the command SEEN/NOREAD. To reenable, simply use the SEEN command again.
-
-NOTE: The list of SEEN messages  are stored in a file username.BULLMARK.
-NEWSMARK.  The files  are created  in the  directory pointed  to by  the
-logical name BULL_MARK.  If BULL_MARK is not defined,  SYS$LOGIN will be
-used.`,
+the command SEEN/NOREAD. To reenable, simply use the SEEN command again.`,
 		MinArgs: 1,
 		MaxArgs: 1,
 	},
 	"UNSEEN": {
-		Description: `Sets the current or message-id message  as seen. This allows you to keep
-track of  messages on a per  message basis. Seen messages  are displayed
-with  a greater  than sign  in  the left  hand column  of the  directory
-listing. Once  you have  used the  SEEN command  once, messages  will be
-automatically  be set  as  being SEEN  when they  are  read. The  UNSEEN
-command sets the current or message-id message as unseen.
-
-   Format:
-
-       SEEN [message-number or numbers]
-       UNSEEN [message-number or numbers]
-
-If you  have used  the SEEN  command and wish  to disable  the automatic
-marking of messages in regular folders  as SEEN when they are read, type
-the command SEEN/NOREAD. To reenable, simply use the SEEN command again.
+		Description: `Sets the current or message-id message as unseen.
 
-NOTE: The list of SEEN messages  are stored in a file username.BULLMARK.
-NEWSMARK.  The files  are created  in the  directory pointed  to by  the
-logical name BULL_MARK.  If BULL_MARK is not defined,  SYS$LOGIN will be
-used.`,
+  Format:
+    UNSEEN [message-number or numbers]`,
 		MinArgs: 1,
 		MaxArgs: 1,
 	},
@@ -1201,9 +1157,8 @@ READ, etc. will apply only to those messages.  Use the CREATE command to
 create  a  folder.   Use the DIRECTORY/FOLDER command to see the list of
 folders that have been created.
 
- Format:
-
-     SELECT [folder-name]
+  Format:
+    SELECT [folder-name]
 
 The complete folder name need not be specified.  BULLETIN  will  try  to
 find the closest matching name.  I.e. INFOV can be used for INFOVAX.
@@ -1228,11 +1183,11 @@ message.`,
 		Description: `The SET command  is  used  with  other  commands  to  define  or  change
 characteristics of the BULLETIN Utility.
 
-  Format:
-    SET option`,
-		Flags: dclish.Flags{
+    Format:
+      SET option`,
+		Commands: dclish.Commands{
 			"ACCESS": {
-				Description: `  Controls  access  to  a  private  folder.   A private folder can only be
+				Description: `Controls  access  to  a  private  folder.   A private folder can only be
 selected by users who have been granted access.  Only the owner of  that
 folder is allowed to grant access.
 
@@ -1264,7 +1219,7 @@ messages,  and thus will not be able to set any login flags.  (NOTE:  If
 such a user selects such a folder and then uses SET ACCESS to grant  him
 or  herself  access,  the user must reselect the folder in order for the
 new access to take affect in order to be able to set login flags.)
-3 id
+
 The id-name can be one or  more  ids  contained  in  the  system  Rights
 Database.   This  includes  usernames  and  UICs.  A UIC that contains a
 comma must be enclosed in quotes.   UICs  can  contain  wildcards,  i.e.
@@ -1276,32 +1231,40 @@ is useful when the folder is shared among nodes in a cluster.
 Alternatively,  the  id-name  can be a filename which contains a list of
 ids.  The filename should be preceeded by a "@".  If the suffix  is  not
 specified, it will be assumed that the suffix is ".DIS" .
-3 /ALL
-Specifies that access to the folder is granted to all users.   If  /READ
-is  not  specified,  the  folder will no longer be private.  If /READ is
-specified, all users will have read access, but  only  privileged  users
-will  have  write access (of course non-privileged users can gain access
-via a later SET ACCESS command.)
-
-Format:
-
-    SET ACCESS /ALL [folder-name]
-3 /READ
-Specifies that access to the folder will be limited to being able to
-read the messages.
-3 Warning
-If  a  user  logs  in after a private folder has been created but before
-being given access, and then is given  access,  any  defaults  that  the
-folder  has,  i.e. /BRIEF, /READNEW, & /NOTIFY, will not be set for that
-user. This is because if the  id  is  not  a  username,  it  becomes  an
-extremely  lengthy  operation  to check each user to see if have that id
-assigned to them.  The alternative is to set the defaults for all  users
-after  every  SET  ACCESS,  but that might cause problems with users who
-have manually reset those defaults.  The  correct  solution  requires  a
-large programming modification, which will be done in a later version.`,
+
+Warning
+
+  If  a  user  logs  in after a private folder has been created but before
+  being given access, and then is given  access,  any  defaults  that  the
+  folder  has,  i.e. /BRIEF, /READNEW, & /NOTIFY, will not be set for that
+  user. This is because if the  id  is  not  a  username,  it  becomes  an
+  extremely  lengthy  operation  to check each user to see if have that id
+  assigned to them.  The alternative is to set the defaults for all  users
+  after  every  SET  ACCESS,  but that might cause problems with users who
+  have manually reset those defaults.  The  correct  solution  requires  a
+  large programming modification, which will be done in a later version.`,
+				MinArgs: 1,
+				MaxArgs: 1,
+				Flags: dclish.Flags{
+					"/ALL": {
+						Description: `  Specifies that access to the folder is granted to all users.   If  /READ
+  is  not  specified,  the  folder will no longer be private.  If /READ is
+  specified, all users will have read access, but  only  privileged  users
+  will  have  write access (of course non-privileged users can gain access
+  via a later SET ACCESS command.)
+
+  Format:
+    SET ACCESS /ALL[=folder-name]`,
+						OptArg: true,
+					},
+					"/READ": {
+						Description: `  Specifies that access to the folder will be limited to being able to
+  read the messages.`,
+					},
+				},
 			},
 			"ALWAYS": {
-				Description: `  Specifies  that  the  selected  folder  has  the ALWAYS attribute.  This
+				Description: `Specifies  that  the  selected  folder  has  the ALWAYS attribute.  This
 causes messages in the folder to be displayed differently  when  logging
 in.  SYSTEM messages will be displayed every time a user logs in, rather
 than just once.  Non-SYSTEM message will also be  displayed  every  time
@@ -1311,10 +1274,16 @@ meant  for  messages which are very important, and thus you want to make
 sure they are read.
 
   Format:
-    SET [NO]ALWAYS`,
+    SET ALWAYS`,
+			},
+			"NOALWAYS": {
+				Description: `Removes ALWAYS attribute from the selected folder.
+
+  Format:
+    SET NOALWAYS`,
 			},
 			"BRIEF": {
-				Description: `  Controls whether you will be alerted upon logging  that  there  are  new
+				Description: `Controls whether you will be alerted upon logging  that  there  are  new
 messages  in the currently selected folder.  A new message is defined as
 one that has been created since the last time you logged in or  accessed
 BULLETIN.   Note  the  difference between BRIEF and READNEW.  The latter
@@ -1323,41 +1292,53 @@ and  prompts  the user to read the messages.  Setting BRIEF will clear a
 READNEW setting (and visa versa).
 
   Format:
-    SET [NO]BRIEF
-3 /ALL
-Specifies that the SET [NO]BRIEF option is the default for all users for
-the specified folder.  This is a privileged qualifier.
-3 /DEFAULT
-Specifies that the [NO]BRIEF option is the default for the specified
-folder.  This is a privileged qualifier.  It will only affect brand new
-users (or those that have never logged in).  Use /ALL to modify all users.
-3 /FOLDER
-   /FOLDER=foldername
-
-Specifies the folder for which the option is to modified.  If not
-specified, the selected folder is modified. Valid only with NOBRIEF.
-3 /PERMANENT
-   /[NO]PERMANENT
+    SET BRIEF`,
+				Flags: dclish.Flags{
+					"/ALL": {
+						Description: `  Specifies that the  SET BRIEF option is the default  for all users for
+  the specified folder. This is a privileged qualifier.`,
+					},
+					"/DEFAULT": {
+						Description: `  Specifies  that the  BRIEF option  is  the default  for the  specified
+  folder. This is a privileged qualifier.  It will only affect brand new
+  users (or  those that have  never logged in).  Use /ALL to  modify all
+  users.`,
+					},
+					"/PERMANENT": {
+						Description: `/[NO]PERMANENT
 
 Specifies that BRIEF is a permanent flag and cannot be changed by the
 individual, except if changing to SHOWNEW or READNEW.  This is a
 privileged qualifier.`,
+					},
+				},
 			},
-			"CONTINUOUS_BRIEF": {
-				Description: `  Specifies that if BRIEF is set for a folder, and there are new messages,
-the notification message "there are new messages" will be displayed every
-time when logging in, until the new messages are read.  Normally, the
-BRIEF setting causes notification only at the first time that new messages
-are detected.
+			"NOBRIEF": {
+				Description: `Turns off BRIEF for the selected or specified folder.
 
   Format:
-    SET [NO]CONTINUOUS_BRIEF
-
-NOTE: Both SET GENERIC and SET CONTINUOUS_BRIEF cannot be set for the
-same user.`,
+    SET NOBRIEF`,
+				Flags: dclish.Flags{
+					"/ALL": {
+						Description: `  Specifies that the SET NOBRIEF option is the default for all users for
+  the specified folder. This is a privileged qualifier.`,
+					},
+					"/DEFAULT": {
+						Description: `  Specifies that  the NOBRIEF  option is the  default for  the specified
+  folder. This is a privileged qualifier.  It will only affect brand new
+  users (or  those that have  never logged in).  Use /ALL to  modify all
+  users.`,
+					},
+					"/FOLDER": {
+						Description: `/FOLDER=foldername
+
+  Specifies the folder for which the option is to modified.  If not
+  specified, the selected folder is modified.`,
+					},
+				},
 			},
 			"DEFAULT_EXPIRE": {
-				Description: `  Specifies  the  number  of days the message created by BBOARD (or direct
+				Description: `Specifies  the  number  of days the message created by BBOARD (or direct
 PMDF path) is to be retained.  The default  is  14  days.   The  highest
 limit  that  can  be  specified is 30 days.  This can be overridden by a
 user with privileges.
@@ -1367,8 +1348,8 @@ If no expiration date is  entered  when  prompted  for  a  date,  or  if
 prompting  has been disabled via SET NOPROMPT_EXPIRE, this value will be
 used.
 
-  Format:
-    SET DEFAULT_EXPIRE days
+    Format:
+      SET DEFAULT_EXPIRE days
 
 If -1 is specified, messages will become permanent.  If 0 is  specified,
 no  default expiration date will be present.  The latter should never be
@@ -1377,153 +1358,79 @@ disappear.
 
 NOTE: This value is the same value that SET BBOARD/EXPIRATION specifies.
 If one is changed, the other will change also.`,
-			},
-			"DUMP": {
-				Description: `  Specifies that messages deleted from the  selected  folder  are  written
-into  a dump (or log) file.  The name of the log file is foldername.LOG,
-and it is located in the folder directory.
-
-  Format:
-    SET [NO]DUMP
-
-The  command  SHOW  FOLDER/FULL  will show if dump has been set.  (NOTE:
-SHOW FOLDER/FULL is a privileged command.)`,
+				MinArgs: 1,
+				MaxArgs: 1,
 			},
 			"EXPIRE_LIMIT": {
-				Description: `  Specifies expiration limit that is allowed for messages.  Non-privileged
-users cannot specify an expiration  that  exceeds  the  number  of  days
-specified.  Privileged users can exceed the limit.
+				Description: `Specifies expiration limit that  is allowed for messages. Non-privileged
+users  cannot specify  an expiration  that  exceeds the  number of  days
+specified.  Privileged users  can exceed  the limit.  Setting days  to 0
+removes the expiration limit.
 
-    SET [NO]EXPIRE_LIMIT [days]
+  Format:
+    SET EXPIRE_LIMIT days
 
 The command SHOW FOLDER/FULL will show  the  expiration  limit,  if  one
 exists.  (NOTE: SHOW FOLDER/FULL is a privileged command.)`,
+				MinArgs: 1,
+				MaxArgs: 1,
 			},
 			"FOLDER": {
-				Description: `  Select a folder of messages.  Identical to the SELECT command.  See help
+				Description: `Select a folder of messages.  Identical to the SELECT command.  See help
 on that command for more information.
 
   Format:
-    SET FOLDER [folder-name]
-3 /MARKED
-Selects messages that have been marked (indicated by an asterisk).
-After using /MARKED, in order to see all messages, the folder will have
-to be reselected.
-`,
-			},
-			"GENERIC": {
-				Description: `  Specifies  that  the  given  account is a "generic" account, i.e used by
-many different people.  If an  account  is  specified  as  GENERIC,  new
-messages  placed in the GENERAL folder will be displayed upon logging in
-for a specific number of days,  rather  than  only  once.   The  default
-period is 7 days.  This command is a privileged command.
-
-  Format:
-    SET [NO]GENERIC username
-
-NOTE: Both SET GENERIC and SET CONTINUOUS_BRIEF cannot be set for the
-same user.
-3 /DAYS
- /DAYS=number_of_days
-
-Specifies the number days that new GENERAL messages will be displayed
-for upon logging in.
-`,
-			},
-			"KEYPAD": {
-				Description: `  Controls whether the keypad has been enabled such that the keys  on  the
-keypad correspond to command definitions.  These definitions can be seen
-via the SHOW KEYPAD command.  The default is NOKEYPAD unless the /KEYPAD
-qualifier has been added to the BULLETIN command line.
-
-  Format:
-    SET [NO]KEYPAD
-`,
-			},
-			"LOGIN": {
-				Description: `  Controls  whether  the  specified  user will be alerted of any messages,
-whether system or non-system, upon logging in.  If an  account  has  the
-DISMAIL  flag  set, SET NOLOGIN is automatically applied to that account
-during the first time that the account logs in.  However, this will  not
-occur  if DISMAIL is set for an old account.  Additionally, removing the
-DISMAIL flag will not automatically enable LOGIN.  (The reason  for  the
-above  was to avoid extra overhead for constant checking for the DISMAIL
-flag.)  This command is a privileged command.
-
-  Format:
-    SET [NO]LOGIN username
-`,
-			},
-			"NODE": {
-				Description: `  Modifies the selected folder from a local folder to a remote folder.   A
-remote folder is a folder in which the messages are actually stored on a
-folder at a remote DECNET node.  The SET NODE command specifies the name
-of  the  remote  node, and optionally the name of the remote folder.  If
-the remote folder name is not included, it is assumed to be the same  as
-the  local  folder.   When  the command is executed, the selected folder
-will then point to the remote folder.  If there  were  messages  in  the
-local folder, they will be deleted.  This feature is present only if the
-BULLCP process is running on the remote node.
-
-  Format:
-    SET NODE nodename [remotename]
-    SET NONODE
-
-NOTE: If one node adds a message to a remote node, other nodes connected
-to the same folder will not immediately be aware  of  the  new  message.
-This  info  is  updated  every  15  minutes,  or if a user accesses that
-folder.
-3 /FOLDER
-   /FOLDER=foldername
-
-Specifies the folder for which the node information is to modified.
-If not specified, the selected folder is modified.
-`,
+    SET FOLDER [folder-name]`,
+				MaxArgs: 1,
+				Action:  ActionSetFolder,
+				Flags: dclish.Flags{
+					"/MARKED": {
+						Description: `  Selects messages that have been marked (indicated by an asterisk). After
+  using /MARKED, in order to see all  messages, the folder will have to be
+  reselected.`,
+					},
+				},
 			},
 			"NOTIFY": {
-				Description: `  Specifies whether you will be notified via a broadcast  message  when  a
+				Description: `Specifies whether you will be notified via a broadcast  message  when  a
 message is added to the selected folder.
 
   Format:
-    SET [NO]NOTIFY
+    SET NOTIFY
 
 In a cluster, if the logical name MAIL$SYSTEM_FLAGS is defined so that
 bit 1 is set, users will be notified no matter which node they are logged
 in to.  If you wish to disable this, you should define BULL_SYSTEM_FLAGS
-so that bit 1 is cleared.
-3 /ALL
-Specifies that the SET [NO]NOTIFY option is the default for all users for
-the specified folder.  This is a privileged qualifier.
-3 /DEFAULT
-Specifies that the [NO]NOTIFY option is the default for the specified
-folder.  This is a privileged qualifier.  It will only affect brand new
-users (or those that have never logged in).  Use /ALL to modify all users.
-3 /FOLDER
-   /FOLDER=foldername
-
-Specifies the folder for which the option is to modified.  If not
-specified, the selected folder is modified. Valid only with NONOTIFY.
-3 /PERMANENT
-   /[NO]PERMANENT
-
-Specifies that NOTIFY is a permanent flag and cannot be changed by the
-individual. /DEFAULT must be specified. This is a privileged qualifier.
-`,
-			},
-			"PAGE": {
-				Description: `  Specifies whether any directory listing or message reading  output  will
-pause  when  it  reaches  the end of the page or not.  Setting NOPAGE is
-useful for terminals that can store more than one screenful at  a  time,
-and  that  have a remote printer that can then print the contents of the
-terminal's memory.  The default is PAGE, unless the default was  changed
-by specifying /NOPAGE on the command line to invoke BULLETIN.
+so that bit 1 is cleared.`,
+				Flags: dclish.Flags{
+					"/ALL": {
+						Description: `  Specifies that the SET NOTIFY option  is the default for all users for
+  the specified folder. This is a privileged qualifier.`,
+					},
+					"/DEFAULT": {
+						Description: `  Specifies  that the  NOTIFY option  is the  default for  the specified
+  folder. This is a privileged qualifier.  It will only affect brand new
+  users (or  those that have  never logged in).  Use /ALL to  modify all
+  users.`,
+					},
+					"/PERMANENT": {
+						Description: `/[NO]PERMANENT
+
+  Specifies that  NOTIFY is a  permanent flag  and cannot be  changed by
+  the  individual. /DEFAULT  must  be specified.  This  is a  privileged
+  qualifier.`,
+					},
+				},
+			},
+			"NONOTIFY": {
+				Description: `Removes notification from the current folder or optional folder-name.
 
   Format:
-    SET [NO]PAGE
-`,
+    SET NONOTIFY [folder-name]`,
+				MaxArgs: 1,
 			},
 			"PRIVILEGES": {
-				Description: `  Specifies  either  process  privileges  or  rights  identifiers that are
+				Description: `Specifies  either  process  privileges  or  rights  identifiers that are
 necessary to use privileged commands.  Use the SHOW  PRIVILEGES  command
 to see what is presently set.  This is a privileged command.
 
@@ -1532,31 +1439,37 @@ to see what is presently set.  This is a privileged command.
 
 The parameters are one or  more  privileges  separated  by  commas.   To
 remove  a privilege, specify the privilege preceeded by "NO".  If /ID is
-specified, the parameters are rights identifiers.
-3 /ID
- /[NO]ID
+specified, the parameters are rights identifiers.`,
+				Flags: dclish.Flags{
+					"/ID": {
+						Description: `/[NO]ID
 
-If specified, then the rights identifier which is specified as the
-parameter will allow users holding that rights identifier to execute
-privileged commands.  If /NOID is specified, the identifier is removed.
-`,
+  If specified,  then the  rights identifier which  is specified  as the
+  parameter will allow  users holding that rights  identifier to execute
+  privileged commands. If /NOID is specified, the identifier is removed.`,
+					},
+				},
 			},
 			"PROMPT_EXPIRE": {
-				Description: `Specifies  that  a  user  will  be  prompted for an expiration date when
-adding  a  message.  If  NOPROMPT_EXPIRE is  specified,  the  user  will
-not  be prompted,  and  the  default expiration  (which  is  set by  SET
-DEFAULT_EXPIRE) will be used. If the value specified is greater than the
-expiration  limit, and  the  user  does not  have  privileges, then  the
-expiration limit will be used as the default expiration. (If there is no
-expiration limit,  and the user  doesn't have privileges, then  an error
-will result.) PROMPT_EXPIRE is the default.
+				Description: `Specifies  that a  user will  be prompted  for an  expiration date  when
+adding a message. If the value  specified is greater than the expiration
+limit, and the user does not  have privileges, then the expiration limit
+will  be used  as the  default expiration.  (If there  is no  expiration
+limit, and the user doesn't have privileges, then an error will result.)
+PROMPT_EXPIRE is the default.
 
   Format:
-    SET  [NO]PROMPT_EXPIRE
-`,
+    SET PROMPT_EXPIRE`,
+			},
+			"NOPROMPT_EXPIRE": {
+				Description: `The user will not be prompted,  and the default expiration (which is set
+by SET DEFAULT_EXPIRE) will be used.
+
+  Format:
+    SET NOPROMPT_EXPIRE`,
 			},
 			"READNEW": {
-				Description: `  Controls whether you will be prompted upon logging in  if  you  wish  to
+				Description: `Controls whether you will be prompted upon logging in  if  you  wish  to
 read new non-system or folder messages (if any exist).  A new message is
 defined as one that has been  added  since  the  last  login,  or  since
 accessing BULLETIN.  The default setting for READNEW is dependent on how
@@ -1565,7 +1478,7 @@ the folder was created by the owner.
 In  order  to  apply  this to a specific folder, first select the folder
 (using the SELECT command), and then enter the SET READNEW command.
 
-  Format:
+    Format:
     SET [NO]READNEW
 
 NOTE:  If  you  have several folders with READNEW enabled, each folder's
@@ -1576,30 +1489,37 @@ However,  if  you enter BULLETIN, you will be told that new messages are
 present in those other folders.  Also, it is not possible  to  EXIT  the
 READNEW mode if there are SYSTEM folders which have new messages. Typing
 the EXIT command will cause you to skip to those folders.  (See HELP SET
-SYSTEM for a description of a SYSTEM folder).
-3 /ALL
-Specifies that the SET [NO]READNEW option is the default for all users for
-the specified folder.  This is a privileged qualifier.  The difference
-between this and /DEFAULT is that the latter will only apply to new users
-(i.e. any users which have never executed BULLETIN).
-3 /DEFAULT
-Specifies that the [NO]READNEW option is the default for the specified
-folder.  This is a privileged qualifier.  It will only affect brand new
-users (or those that have never logged in).  Use /ALL to modify all users.
-3 /FOLDER
-   /FOLDER=foldername
-
-Specifies the folder for which the option is to modified.  If not
-specified, the selected folder is modified. Valid only with NOREADNEW.
-3 /PERMANENT
-   /[NO]PERMANENT
-
-Specifies that READNEW is a permanent flag and cannot be changed by the
-individual.  This is a privileged qualifier.
-`,
+SYSTEM for a description of a SYSTEM folder).`,
+				Flags: dclish.Flags{
+					"/ALL": {
+						Description: `  Specifies that the SET [NO]READNEW option is the default for all users
+  for  the  specified  folder.  This  is  a  privileged  qualifier.  The
+  difference  between this  and /DEFAULT  is that  the latter  will only
+  apply  to  new  users  (i.e.  any  users  which  have  never  executed
+  BULLETIN).`,
+					},
+					"/DEFAULT": {
+						Description: `  Specifies that the [NO]READNEW option is the default for the specified
+  folder. This is a privileged qualifier.  It will only affect brand new
+  users (or  those that have  never logged in).  Use /ALL to  modify all
+  users.`,
+					},
+					"/FOLDER": {
+						Description: `/FOLDER=foldername
+
+  Specifies  the folder  for which  the option  is to  modified. If  not
+  specified, the selected folder is modified. Valid only with NOREADNEW.`,
+					},
+					"/PERMANENT": {
+						Description: `/[NO]PERMANENT
+
+  Specifies that  READNEW is a permanent  flag and cannot be  changed by
+  the individual. This is a privileged qualifier.`,
+					},
+				},
 			},
 			"SHOWNEW": {
-				Description: `  Controls whether a directory listing of new  messages  for  the  current
+				Description: `Controls whether a directory listing of new  messages  for  the  current
 folder  will  be displayed when logging in.  This is similar to READNEW,
 except you will not be prompted to read the messages.   The  default  is
 dependent  on how the folder was created by the owner.  A new message is
@@ -1609,35 +1529,43 @@ accessing BULLETIN.
 In order to apply this to a specific folder,  first  select  the  folder
 (using  the  SELECT  command),  and  then enter the SET SHOWNEW command.
 
-  Format:
-    SET [NO]SHOWNEW
-3 /ALL
-Specifies that the SET [NO]SHOWNEW option is the default for all users for
-the specified folder.  This is a privileged qualifier.  The difference
-between this and /DEFAULT is that the latter will only apply to new users
-(i.e. any users which have never executed BULLETIN).
-3 /DEFAULT
-Specifies that the [NO]SHOWNEW option is the default for the specified
-folder.  This is a privileged qualifier.  It will only affect brand new
-users (or those that have never logged in).  Use /ALL to modify all users.
-3 /FOLDER
-   /FOLDER=foldername
-
-Specifies the folder for which the option is to modified.  If not
-specified, the selected folder is modified. Valid only with NOSHOWNEW.
-3 /PERMANENT
-   /[NO]PERMANENT
-
-Specifies that SHOWNEW is a permanent flag and cannot be changed by the
-individual, except if changing to READNEW. This is a privileged qualifier.
-`,
+    Format:
+    SET [NO]SHOWNEW`,
+				Flags: dclish.Flags{
+					"/ALL": {
+						Description: `  Specifies that the SET [NO]SHOWNEW option is the default for all users
+  for  the  specified  folder.  This  is  a  privileged  qualifier.  The
+  difference  between this  and /DEFAULT  is that  the latter  will only
+  apply  to  new  users  (i.e.  any  users  which  have  never  executed
+  BULLETIN).`,
+					},
+					"/DEFAULT": {
+						Description: `  Specifies that the [NO]SHOWNEW option is the default for the specified
+  folder. This is a privileged qualifier.  It will only affect brand new
+  users (or  those that have  never logged in).  Use /ALL to  modify all
+  users.`,
+					},
+					"/FOLDER": {
+						Description: `/FOLDER=foldername
+
+  Specifies  the folder  for which  the option  is to  modified. If  not
+  specified, the selected folder is modified. Valid only with NOSHOWNEW.`,
+					},
+					"/PERMANENT": {
+						Description: `/[NO]PERMANENT
+
+  Specifies that  SHOWNEW is a permanent  flag and cannot be  changed by
+  the individual,  except if changing  to READNEW. This is  a privileged
+  qualifier.`,
+					},
+				},
 			},
 			"SYSTEM": {
-				Description: `  Specifies that the selected folder is a SYSTEM folder.  A SYSTEM folder
+				Description: `Specifies that the selected folder is a SYSTEM folder.  A SYSTEM folder
 is allowed to have SYSTEM and SHUTDOWN messages added to it.  This is a
 privileged command.
 
-  Format:
+    Format:
     SET [NO]SYSTEM
 
 By default, the GENERAL folder is a SYSTEM folder, and the setting for
@@ -1652,52 +1580,41 @@ folder at the other node is also a SYSTEM folder.
 	"SHOW": {
 		Description: `The SHOW command displays information about certain characteristics.
 `,
-		Flags: dclish.Flags{
+		Commands: dclish.Commands{
 			"FLAGS": {
-				Description: `  Shows whether BRIEF, NOTIFY, READNEW, or SHOWNEW has been set for the
+				Description: `Shows whether BRIEF, NOTIFY, READNEW, or SHOWNEW has been set for the
 currently selected folder.
 `,
 			},
 			"FOLDER": {
-				Description: `  Shows information about a folder of messages.  Owner and description are
-shown.  If the folder name is omitted, and a folder has been selected via
+				Description: `Shows information about a folder  of messages. Owner and description are
+shown. If the folder name is omitted, and a folder has been selected via
 the SELECT command, information about that folder is shown.
 
   Format:
-    SHOW FOLDER [folder-name]
-3 /FULL
-Control whether all information of the folder is displayed.  This
-includes DUMP & SYSTEM settings, the access list if the folder is
-private, and BBOARD information.  This information is only those who
-have access to that folder.
-`,
-			},
-			"KEYPAD": {
-				Description: `  Displays the keypad command definitions.  If the keypad has been enabled
-by either the SET KEYPAD COMMAND, or /KEYPAD is specified on the command
-line, the keypad keys will be defined as commands.  SHOW KEYPAD is the
-equivalent of HELP KEYPAD.
-
-NOTE: If the keypad is not enabled, PF2 is defined to be SET KEYPAD.
-3 /PRINT
-Prints the keypad definitions on the default printer (SYS$PRINT).
-`,
+    SHOW FOLDER [folder-name]`,
+				Flags: dclish.Flags{
+					"/FULL": {
+						Description: `  Control  whether all  information  of the  folder  is displayed.  This
+  includes the SYSTEM setting, the access list if the folder is private,
+  and BBOARD information. This information is only those who have access
+  to that folder.`,
+					},
+				},
 			},
 			"NEW": {
-				Description: `  Shows folders which have new unread messages for which BRIEF or READNEW
-have been set.  (Note: If you enter BULLETIN but do not read new unread
-messages, you will not be notified about them the next time you enter
-BULLETIN.  This is a design "feature" and cannot easily be changed.)
-`,
+				Description: `Shows folders which have new unread  messages for which BRIEF or READNEW
+have been set. (Note:  If you enter BULLETIN but do  not read new unread
+messages, you  will not be notified  about them the next  time you enter
+BULLETIN. This is a design "feature" and cannot easily be changed.)`,
 			},
 			"PRIVILEGES": {
-				Description: `  Shows the privileges necessary to use privileged commands.  Also shows
-any rights identifiers that would also give a user privileges.  (The
-latter are ACLs which are set on the BULLUSER.DAT file.)
-`,
+				Description: `Shows the  privileges necessary to  use privileged commands.  Also shows
+any  rights identifiers  that would  also give  a user  privileges. (The
+latter are ACLs which are set on the BULLUSER.DAT file.)`,
 			},
 			"USER": {
-				Description: `  Shows the last time that a user logged in, or if /FOLDER  is  specified,
+				Description: `Shows the last time that a user logged in, or if /FOLDER  is  specified,
 the  latest  message which a user has read in the folder.  If NOLOGIN is
 set for  a  user,  this  information  will  be  displayed.   This  is  a
 privileged  command.   Non-privileged users will only be able to display
@@ -1713,56 +1630,52 @@ NOTE: The last logged in time displayed is that which is stored when the
 BULLETIN/LOGIN command is executed, not that  which  VMS  stores.   Some
 sites  make  BULLETIN/LOGIN  an  optional  command for users to store in
 their own LOGIN.COM, so this command can be used  to  show  which  users
-have done this.
-3 /ALL
-Specifies that information for all users is to be displayed.  This is  a
-privileged command.
-3 /LOGIN
- /[NO]LOGIN
-
-Specifies that only those users which do not have NOLOGIN set are to be
-displayed.  If negated, only those users with NOLOGIN set are displayed.
-This is a privileged command.  The qualifier /ALL need not be specified.
-3 /FOLDER
-   /FOLDER=[foldername]
-
-Specifies to display the latest message that was read by the user(s) for
-the  specified  foldername.   A newsgroup can be specified, but the info
-can only be shown if the user has subscribed to the newsgroup.   If  the
-foldername is not specified, the selected folder will be used.
-3 /SINCE
-   /SINCE=[date]
-
-Specifies  to display only those users whose latest read message date is
-the same date  or  later  than  the  specified  date.   If  no  date  is
-specified,  the  date  of  the  current message is used.  Only valid for
-folders or with /LOGIN.  Use /START for newsgroups.
-3 /START
-   /START=[number]
-
-Specifies  to  display only those users whose latest read message number
-is equal to or greather than the specified  number.   If  no  number  is
-specified,  the  message  number  of  the current message is used.  Only
-valid for newsgroups.  Use /SINCE for folders and with /LOGIN.
-`,
+have done this.`,
+				MaxArgs: 1,
+				Flags: dclish.Flags{
+					"/ALL": {
+						Description: `  Specifies that information for all users is to be displayed. This is a
+  privileged command.`,
+					},
+					"/LOGIN": {
+						Description: `/[NO]LOGIN
+
+  Specifies that only  those users which do not have  NOLOGIN set are to
+  be  displayed. If  negated,  only  those users  with  NOLOGIN set  are
+  displayed. This is  a privileged command. The qualifier  /ALL need not
+  be specified.`,
+					},
+					"/FOLDER": {
+						Description: `/FOLDER=[foldername]
+
+  Specifies to display  the latest message that was read  by the user(s)
+  for the  specified foldername. A  newsgroup can be specified,  but the
+  info can only be shown if the user has subscribed to the newsgroup. If
+  the foldername is not specified, the selected folder will be used.`,
+					},
+					"/SINCE": {
+						Description: `/SINCE=[date]
+
+  Specifies to display  only those users whose latest  read message date
+  is the  same date  or later  than the  specified date.  If no  date is
+  specified, the  date of the  current message  is used. Only  valid for
+  folders or with /LOGIN. Use /START for newsgroups.`,
+					},
+					"/START": {
+						Description: `/START=[number]
+
+  Specifies to display only those users whose latest read message number
+  is equal  to or greather  than the specified  number. If no  number is
+  specified, the  message number  of the current  message is  used. Only
+  valid for newsgroups. Use /SINCE for folders and with /LOGIN.`,
+					},
+				},
 			},
 			"VERSION": {
-				Description: `  Shows  the  version  of  BULLETIN  and  the date that the executable was
-linked.
-`,
+				Description: `  Shows the  version of BULLETIN  and the  date that the  executable was
+  linked.`,
+				Action: ActionShowVersion,
 			},
 		},
 	},
-	"UNDELETE": {
-		Description: `Undeletes  he  specified  message  if  the message was deleted using the
-DELETE command.  Deleted messages are  not  actually  deleted  but  have
-their  expiration  date  set to 15 minutes in the future and are deleted
-then.  Undeleting the message will reset the expiration date back to its
-original  value.   Deleted  messages  will  be  indicated as such by the
-string (DELETED) when either reading or doing a directory listing.
-
-  Format:
-    UNDELETE [message-number]
-`,
-	},
 }
diff --git a/repl/repl.go b/repl/repl.go
index 07afdf823180cb9de6a1f48f59ccc8b17a996ddb..2a11baa3644c2f982f5d1273b0023baa4f75e45a 100644
--- a/repl/repl.go
+++ b/repl/repl.go
@@ -31,12 +31,21 @@ func Loop() error {
 
 	// TODO: Remove once commands are implemented.
 	unimplemented := 0
-	total := 0
+	total := len(commands)
 	for c := range commands {
 		if commands[c].Action == nil {
 			unimplemented++
 		}
-		total++
+		if len(commands[c].Commands) > 0 {
+			total--
+			unimplemented--
+			for subc := range commands[c].Commands {
+				if commands[c].Commands[subc].Action == nil {
+					unimplemented++
+				}
+			}
+			total += len(commands[c].Commands)
+		}
 	}
 	fmt.Printf("TODO: %d out of %d commands still to be implemented.\n",
 		unimplemented, total)
diff --git a/repl/set.go b/repl/set.go
new file mode 100644
index 0000000000000000000000000000000000000000..4b296bc016601005c9229d7aca4d8d7edb34e34f
--- /dev/null
+++ b/repl/set.go
@@ -0,0 +1,35 @@
+// Package repl implements the main event loop.
+package repl
+
+import (
+	"errors"
+	"fmt"
+	"strings"
+
+	"git.lyda.ie/kevin/bulletin/accounts"
+	"git.lyda.ie/kevin/bulletin/dclish"
+)
+
+// ActionSetFolder handles the `SET FOLDER` command.  This selects a folder.
+func ActionSetFolder(cmd *dclish.Command) error {
+	if len(cmd.Args) != 1 {
+		return errors.New("TODO: need to add code for /MARK")
+	}
+	// TODO: Move shared code here and in ActionSelect and ActionDirectory into a function.
+	if strings.Contains(cmd.Args[0], "%") {
+		return errors.New("Folder name cannot contain a %")
+	}
+	folder := accounts.User.Folders.FindFolder(cmd.Args[0])
+	if folder == "" {
+		return errors.New("Unable to select the folder")
+	}
+	if accounts.User.Folders.IsFolderAccess(folder, accounts.User.Login) {
+		accounts.User.CurrentFolder = folder
+		fmt.Printf("Folder has been set to '%s'.\n", folder)
+		return nil
+	}
+	// TODO: Should be:
+	//       WRITE(6,'('' You are not allowed to access folder.'')')
+	//       WRITE(6,'('' See '',A,'' if you wish to access folder.'')')
+	return errors.New("Unable to select the folder")
+}
diff --git a/repl/show.go b/repl/show.go
new file mode 100644
index 0000000000000000000000000000000000000000..1ffa7259d4cde9745a7e102c2aa8b00a0b920eee
--- /dev/null
+++ b/repl/show.go
@@ -0,0 +1,18 @@
+// Package repl implements the main event loop.
+package repl
+
+import (
+	"fmt"
+
+	"github.com/carlmjohnson/versioninfo"
+
+	"git.lyda.ie/kevin/bulletin/dclish"
+)
+
+// ActionShowVersion handles the `SHOW VERSION` command.  This selects a folder.
+func ActionShowVersion(_ *dclish.Command) error {
+	fmt.Printf("BULLETIN Version %s\n", versioninfo.Version)
+	fmt.Printf("Linked on %s\n",
+		versioninfo.LastCommit.Format("2006-05-04 15:02:01"))
+	return nil
+}