@q @program jboard.muf 1 99999 d i ( jboard.muf v1.1 Jessy @ FurryMUCK 7/99 A global bulletin board program: a single action runs multiple boards. Features include controlled access to specific boards, search capabilities, and navigation aids for viewing `new' and `next' posts. INSTALLATION: Create a global action named read;write;editmsg;delete;next;board and link it to this program. JBoard.muf requires M3, but setting it Wizard is recommended. The program also requires lib-editor, lib-lmgr, lib-reflist, and lib-strings, all of which should be installed on any established MUCK. IMPORTANT NOTE: If you wish to use other command names, DO NOT manually rename the command... Instead, use the built-in aliasing and renaming #options. <cmd> #alias <alias name> <cmd> #rename <new name> Examples: To add `del' as an alias for `delete': delete #alias del To rename the `read' command to `+read': read #rename +read USE: Some user commands include: read ........................... Display list of boards read <board> ................... List posts on <board> read <board>/<post> ............ Display <post> from <board> read #search <string> .......... Search all boards for <string> read #search <board>/<string> .. Search <board> for <string> read #new <board> .............. Display new posts on <board> write <board>/<subject> ........ Write post <subject> on <board> write #noname <board>/<subject> Write an anonymous post on <board> edit <board>/<post> ............ Edit <post> on <board> delete <board>/<post> .......... Delete <post> on <board> next ........................... Show next post in <board|search> board #window <number> <units> . Set `new posts' window Administrative commands include: board #create .................. Create a new board board #destroy ................. Delete an existing board board #private <board> ......... Set <board> private board #open <board> ............ Set <board> open board #include <board>/<player> Give <player> access to <board> board #exclude <board>/<player> Remove <player>s access to <board> board #noname <board> .......... Toggle <board>s nonames allowed board #staff <board> ........... Set <board> staff-only board #general <board> ......... Set <board> general public board #add <player> ............ Add <player> to admin list board #remove <player> ......... Remove <player> from admin list #Help is available for each command. Boards and posts can be specified by name or number. #Argument strings do not have to be typed completely: typing the first one or several characters will produce the same result. For example, typing `read #s rosebud' will produce the same result as `read #search rosebud'. Next displays either then post immediately following the one you last read, or -- if you issued a #search and have not issued a standard read since then -- the next match in your last #search. Edit and delete permission is given to the author of a post, the owner of the program or trigger, wizards, and anyone included in the admin list. Private boards are visible only to #included players and admins. Because some players will not see some boards, board numbering can be different for different players. Staff-only boards can be read by anyone, but only written to by admins... intended for policy postings and the like. CHANGES: 1.1 Fixed counting errors that arose with #private boards. Fixed a crasher bug in #exclude. JBoard.muf may be freely ported. Please comment any changes. ) $include $lib/reflist $include $lib/lmgr $include $lib/editor $include $lib/strings $define Tell me @ swap notify $enddef lvar ourArg (* str: user's #arg string; may be modified *) lvar ourCom (* str: unchanged copy of lvar `command' *) lvar ourCounter (* str or int: flow-control counter *) lvar ourCounter2 (* str or int: flow-control counter *) lvar ourPostCounter (* str or int: flow-control counter *) lvar ourBoard (* str: propdir of current board *) lvar ourSubject (* str: formatted post subject *) lvar ourPost (* str: propdir of current post *) lvar ourString (* str: workspace var *) lvar ourTime (* int: time-check var *) lvar ourBoolean (* int: flow-control var *) lvar listString (* str: list-handling vars *) lvar listCounter lvar listScratch : AddListLine ( s s' -- ) (* add line s' to list s on library *) over prog LMGR-GetCount 1 + 3 pick prog LMGR-PutElem pop ; : EditLoop ( listname dbref {rng} mask currline cmdstring -- ) (* read input for list editor *) EDITORloop dup "save" stringcmp not if pop pop pop pop 3 pick 3 + -1 * rotate over 3 + -1 * rotate dup 5 + pick over 5 pick over over LMGR-DeleteList 1 rot rot LMGR-PutRange 4 pick 4 pick LMGR-GetList dup 3 + rotate over 3 + rotate ">> Post saved." Tell "" EditLoop exit then dup "abort" stringcmp not if ">> Post not saved." Tell pop pop pop pop pop pop pop pop pop exit then dup "end" stringcmp not if pop pop pop pop pop pop dup 3 + rotate over 3 + rotate over over LMGR-DeleteList 1 rot rot LMGR-PutRange ">> Post saved." Tell exit then ; : EditList ( d s -- ) (* edit list s on d *) swap ">> Welcome to the post editor. You can get help by entering `.h' on" Tell ">> a line by itself. `.end' will save and exit. `.abort' will abort" Tell ">> any changes. To save changes and continue editing, use `.save'." Tell over over LMGR-GetList "save" 1 ".i $" EditLoop ; : ShowList ( d s -- ) (* display list s on object d *) swap LMGR-GetList begin (* begin line-listing loop *) dup while dup 1 + rotate Tell 1 - repeat (* end line-listing loop *) pop ; : RemoveDir ( d s -- ) (* remove dir s from d; leave subdirs *) dup "*/" smatch not if (* add a trailing / slash if needed *) "/" strcat then (* loop through and remove props in dir *) over over nextprop swap pop begin (* begin prop-removing loop *) dup while over over nextprop 3 pick rot remove_prop repeat (* end prop-removing loop *) pop pop ; : ParseTimeString ( s -- i1 i2 ) (* convert string s to number of seconds i1. i2 is true if successful *) (* format of s is `<num> <units> eg `3 hours', `1 day', `2 weeks' *) (* tokenize string *) " " explode dup 2 = if (* check syntax and bail out if needed *) pop else begin dup while swap pop 1 - repeat pop ">> Entry not understood." Tell 0 exit then (* parse units and convert amount *) swap strip "seconds" over stringpfx if 1 else "minutes" over stringpfx if 60 else "hours" over stringpfx if 3600 else "days" over stringpfx if 86400 else "weeks" over stringpfx if 604800 else "months" over stringpfx if 1036800 else "years" over stringpfx if 12441600 else pop pop 0 exit then then then then then then then swap pop swap atoi * dup 0 < if ">> ERROR: Result out of range." Tell pid kill else 1 then ; : CheckAdminPerm ( -- ) (* return true if user has admin perms *) me @ "W" flag? (* wizard? *) prog owner me @ dbcmp (* prog owner? *) trig owner me @ dbcmp (* trig owner? *) prog "_admin" me @ REF-inlist? or or or if (* configured admin? *) 1 (* any of those qualify *) else 0 (* otherwise, no *) then ; : CheckBoardPerm ( s -- i ) (* return true if user is allowed board s *) CheckAdminPerm if pop 1 exit then (* see if board is private *) prog "_closed/" 3 pick dup "*/" smatch if dup strlen 1 - strcut pop then strcat getpropstr if (* see if user is auth for private board *) prog "_closed/" rot strcat "/" strcat me @ intostr strcat getprop if 1 else 0 then else pop 1 then ; : CheckPostPerm ( s -- i )(* return true if user can delete post s *) prog swap "/auth" strcat getprop me @ dbcmp (* author? *) CheckAdminPerm or if (* administrator? *) 1 (* if either, ok *) else 0 then ; : CheckOldPost ( s -- i ) (* return true if post is `old' for user *) (* `old' is outside window, if set, or older than last read post *) dup "." rinstr 1 - strcut pop dup "/" rinstr strcut swap pop atoi me @ "_prefs/news/lastpost" getpropstr if (* older than last? *) dup me @ "_prefs/news/lastpost" getpropstr dup "." rinstr 1 - strcut pop dup "/" rinstr strcut swap pop atoi <= if pop 1 exit then then me @ "_prefs/news/window" getprop dup if (* outside window? *) systime swap - <= if 1 else 0 then else pop pop 0 then ; : SetNew ( -- ) (* strip #n from arg and set ourBoolean *) ourArg @ " " instr dup if (* strip `#new' from arg string *) ourArg @ swap strcut swap pop strip ourArg ! else pop then 1 ourBoolean ! (* this will tell DisplayPostList to exclude old *) ; : UpdateLast ( -- ) (* update `last read' tracking props *) me @ "_prefs/news/lastpost" ourPost @ setprop ourPost @ dup "." rinstr 1 - strcut pop dup "/" rinstr strcut swap pop atoi me @ "_prefs/news/lasttime" rot setprop ; : GetCurrentCommandName ( s -- s' ) (* return current name of com s *) prog "_orign/" 3 pick strcat getpropstr dup if swap pop else pop then ; : GetOtherCommandNames ( s -- s' ) (* return formatted string of all jboard com names except s *) dup "read" smatch if pop "write" GetCurrentCommandName ", " strcat "edit" GetCurrentCommandName ", " strcat strcat "delete" GetCurrentCommandName ", " strcat strcat "next" GetCurrentCommandName ", and " strcat strcat "board" GetCurrentCommandName strcat else dup "write" smatch if pop "read" GetCurrentCommandName ", " strcat "edit" GetCurrentCommandName ", " strcat strcat "delete" GetCurrentCommandName ", " strcat strcat "next" GetCurrentCommandName ", and " strcat strcat "board" GetCurrentCommandName strcat else dup "edit" smatch if pop "read" GetCurrentCommandName ", " strcat "write" GetCurrentCommandName ", " strcat strcat "delete" GetCurrentCommandName ", " strcat strcat "next" GetCurrentCommandName ", and " strcat strcat "board" GetCurrentCommandName strcat else dup "delete" smatch if pop "read" GetCurrentCommandName ", " strcat "write" GetCurrentCommandName ", " strcat strcat "edit" GetCurrentCommandName ", " strcat strcat "next" GetCurrentCommandName ", and " strcat strcat "board" GetCurrentCommandName strcat else dup "next" smatch if pop "read" GetCurrentCommandName ", " strcat "write" GetCurrentCommandName ", " strcat strcat "edit" GetCurrentCommandName ", " strcat strcat "delete" GetCurrentCommandName ", and " strcat strcat "board" GetCurrentCommandName strcat dup "board" smatch if pop "read" GetCurrentCommandName ", " strcat strcat "write" GetCurrentCommandName ", " strcat strcat "edit" GetCurrentCommandName ", " strcat strcat "delete" GetCurrentCommandName ", and " strcat strcat "next" GetCurrentCommandName strcat then then then then then then ; : GetBoardName ( s -- s' ) (* return name of board for prop s *) "" "_boards/" subst (* remove propdir string *) "" "/" subst (* remove trailing / *) ; : GetNumPosts ( s -- s' ) (* return number of posts on board s *) 0 (* put a counter int on stack *) prog rot "/" strcat nextprop begin (* begin post-counting loop *) dup while swap 1 + swap (* bump counter for each post prop *) prog swap nextprop repeat pop intostr (* return total found *) ; : GetNumNewPosts ( s -- s' ) (* return number of posts on board s *) 0 ourPostCounter ! 0 (* put a counter int on stack *) prog rot "/" strcat nextprop begin (* begin post-counting loop *) dup while dup CheckOldPost not if ourPostCounter @ 1 + ourPostCounter ! then swap 1 + swap (* bump counter for each post prop *) prog swap nextprop repeat pop pop ourPostCounter @ intostr (* return total found *) ; : GetPostNumber ( s -- s' ) (* return number of post s *) 0 ourCounter ! dup dup "/" rinstr strcut pop prog swap nextprop begin (* begin post-counting loop *) dup while ourCounter @ 1 + ourCounter ! over over smatch if pop pop ourCounter @ intostr exit (* found it... go *) then prog swap nextprop repeat (* end post-counting loop *) pop "X" (* if post not found, return harmless, inaccurate `X' *) ; : GetBoardNumber ( -- s' ) (* return number of board ourBoard *) 0 ourCounter ! prog "_boards/" nextprop begin (* begin board-counting loop *) dup while dup CheckBoardPerm not if (* skip ones users can't see *) prog swap nextprop continue then ourCounter @ 1 + ourCounter ! dup ourBoard @ dup strlen 1 - strcut pop smatch if pop ourCounter @ intostr exit (* found it... go *) then prog swap nextprop repeat (* end board-counting loop *) pop "X" (* if board not found, return harmless, inaccurate `X' *) ; : GetBoardHeader ( s -- s' ) (* return formatted header for board s *) "" "_boards/" subst "" "/" subst toupper "-- " swap strcat " ( Board #" strcat GetBoardNumber strcat " ) -----------------------------------------------------------------" strcat 72 strcut pop ; : GetPostHeader ( -- s ) (* return formatted header for ourPost *) prog ourPost @ "/subj" strcat getprop dup not if (* get subject *) pop "<unknown>" then " ( " strcat prog ourPost @ "/auth" strcat getprop dup if (* get author *) dup ok? if dup player? if prog "_anon/" ourBoard @ dup strlen 1 - strcut pop strcat getprop prog ourPost @ "/anon" strcat getprop and if pop "<anon>" else name then else pop "<<unknown>" then else pop "<unknown>" then else pop "<unknown>" then strcat ", " strcat prog ourPost @ "/time" strcat getprop if (* get time *) "%D" else "<unknown>" then strcat " )" strcat prog ourPost @ "/time" strcat getprop timefmt (* format and return *) ; : ShowPost ( -- ) (* show post ourPost *) (* line sep *) "---------------------------------------------------------------------" Tell GetBoardNumber ":" strcat (* board number *) ourPost @ GetPostNumber strcat " " strcat (* post number *) GetPostHeader strcat Tell (* header: subj, auth, time *) " " Tell (* line sep *) prog ourPost @ ShowList (* content *) (* line sep *) "---------------------------------------------------------------------" Tell UpdateLast (* update `last read' props *) ; : FindBoardByNumber ( -- i ) (* find board by #arg number *) (* store result in ourBoard and return true if successful *) ourArg @ atoi 0 (* put board num and counter on stack *) prog "_boards/" nextprop begin (* begin board-counting loop *) dup while dup CheckBoardPerm not if (* skip boards user can't see *) prog swap nextprop continue then 3 pick 3 pick 1 + = if (* when found, store ourBoard and go *) "/" strcat ourBoard ! pop pop 1 exit (* found it... go *) else swap 1 + swap prog swap nextprop then repeat pop pop pop 0 (* if not found, clean up stack, return false *) ; : FindBoardByTitle ( -- i ) (* find board by #title *) (* store result in ourBoard and return true if successful *) (* do we have a propdir that matches #title arg? *) "_boards/" ourArg @ strcat dup prog swap getprop if "/" strcat dup CheckBoardPerm if ourBoard ! 1 else pop 0 then else pop 0 then ; : FindPostByNumber ( -- i ) (* find post by #arg number *) (* store result in ourPost and return true if successful *) 0 prog ourBoard @ nextprop begin (* begin post-counting loop *) dup while over 1 + ourPost @ atoi = if ourPost ! pop 1 exit (* found it... go *) then swap 1 + swap prog swap nextprop repeat (* end post-counting loop *) pop pop 0 (* if not found, clean up stack, return false *) ; : FindPostByTitle ( -- i ) (* find post by #title arg *) (* store result in ourPost and return true if successful *) (* full path of post prop not known; have to iterate through *) prog ourBoard @ nextprop begin (* begin post-searching loop *) dup while prog over "/subj" strcat getpropstr dup if ourPost @ smatch if ourPost ! 1 exit (* found it... go *) then else pop then prog swap nextprop repeat (* end post-searching loop *) pop 0 (* if not found, clean up stack, return false *) ; : ParsePostPath ( -- ) (* find <board>/<post> *) ourArg @ dup "/" instr strcut strip (* parse *) dup if ourPost ! (* store raw post arg; leave raw board arg on stack *) else pop ">> Syntax: " (* or show syntax *) ourCom @ strcat " <board> / <post>" strcat Tell 0 exit then (* raw board arg is on stack: trim trailing /, then find *) dup strlen 1 - strcut pop strip dup if dup ourArg ! number? if (* this way if board arg is a number... *) FindBoardByNumber not if ">> Sorry, board number " ourArg @ strcat " not found." strcat Tell pid kill then else FindBoardByTitle not if (* .. or this way if arg is a title *) ">> Sorry, board `" ourArg @ strcat "' not found." strcat Tell pid kill then then else ">> Syntax: " (* improper #arg... show syntax *) ourCom @ strcat " <board> / <post>" strcat Tell pid kill then 1 ; : MakePostProp ( -- ) (* format and store a prop for new post *) (* props are stored by systime, padded with leading zeros and stringified so alphabetic ordering will be correct, and catted with a random 3-digit number to avoid overwrites when two or more players write during same second... not foolproof but quite safe, and makes prop-parsing routines a bit cleaner than they would be if we padded by dbref *) systime intostr (* get current time *) 12 over strlen - (* pad with leading zeros to 12 character string *) begin dup while "0" rot strcat swap 1 - repeat pop "." strcat (* cat on random number string from 000 to 999 *) random 1000 % intostr 3 over strlen - begin dup while "0" rot strcat swap 1 - repeat pop strcat ourPost ! (* store in ourPost *) ; : WritePostStamps ( -- ) (* add time/auth/subj stamps for new post *) prog ourBoard @ dup strlen 1 - strcut pop systime setprop prog ourBoard @ ourPost @ strcat "#/auth" strcat me @ setprop prog ourBoard @ ourPost @ strcat "#/time" strcat systime setprop prog ourBoard @ ourPost @ strcat "#/subj" strcat ourSubject @ setprop ourBoolean @ if prog ourBoard @ ourPost @ strcat "#/anon" strcat "yes" setprop then ; : WritePost ( -- ) (* write new post on board ourBoard *) ourBoolean @ if prog "_anon/" ourBoard @ dup strlen 1 - strcut pop strcat getprop not if ">> Sorry, no-name posts are not allowed on this board." Tell exit then then prog "_staff/" ourBoard @ dup strlen 1 - strcut pop strcat getprop CheckAdminPerm not and if ">> Sorry, this board is read-only." Tell exit then prog ourBoard @ MakePostProp ourPost @ strcat EditList prog ourBoard @ ourPost @ strcat "#/" strcat nextprop if WritePostStamps then ; : EditPost ( -- ) (* edit post ourPost on board ourBoard *) ourPost @ CheckPostPerm not if (* check: user has edit permission? *) ">> Permission denied." Tell exit then "_temp/" me @ intostr strcat ourCounter ! (* store post stamps in temp dir *) prog ourCounter @ "/auth" strcat prog ourPost @ "/auth" strcat getprop setprop prog ourCounter @ "/time" strcat prog ourPost @ "/time" strcat getprop setprop prog ourCounter @ "/subj" strcat prog ourPost @ "/subj" strcat getprop setprop prog ourCounter @ "/anon" strcat prog ourPost @ "/anon" strcat getprop setprop prog ourPost @ "" "#" subst EditList (* go edit post *) (* move post stamps back to post dir *) prog ourPost @ "/auth" strcat prog ourCounter @ "/auth" strcat getprop setprop prog ourPost @ "/time" strcat prog ourCounter @ "/time" strcat getprop setprop prog ourPost @ "/subj" strcat prog ourCounter @ "/subj" strcat getprop setprop prog ourPost @ "/anon" strcat prog ourCounter @ "/anon" strcat getprop setprop prog ourCounter @ RemoveDir ; : DeletePost ( -- ) (* delete post ourPost from board ourBoard *) ourPost @ CheckPostPerm not if (* check: user has delete permission *) ">> Permission denied." Tell exit then prog ourPost @ RemoveDir prog ourPost @ remove_prop ">> Post deleted." Tell ; : ShowWindowSyntax ( -- ) (* show syntax for #window settings *) ">> Syntax: " ourCom @ strcat " #window <number> <time units>" strcat Tell ">> Example: " ourCom @ strcat " #window 6 weeks." strcat Tell ; : SetPostWindow ( -- ) (* set prop for user's #window *) ourArg @ " " instr dup if (* parse #arg *) ourArg @ swap strcut swap pop strip (* strip #window arg *) ParseTimeString if (* parse time *) me @ "_prefs/news/window" rot setprop (* set window prop *) ">> Window set." Tell else ShowWindowSyntax (* or show syntax if unable to parse *) then else (* or clear window if no time was specified *) me @ "_prefs/news/window" remove_prop ">> Window cleared." Tell pop then ; : SetBoardStaff ( -- ) (* set specified board staff-only *) CheckAdminPerm not if (* check admin perm *) ">> Permission denied." Tell exit then ourArg @ " " instr dup if (* find board to set *) ourArg @ swap strcut swap pop strip ourArg ! ourArg @ number? if FindBoardByNumber if (* this way if arg is a number... *) prog "_staff/" ourBoard @ strcat dup strlen 1 - strcut pop "yes" setprop ">> Board set `staff' (only admins can post to it)." Tell else ">> Board number " (* or say we couldn't find *) ourArg @ strcat " not found." strcat Tell exit then else (* or find this way if arg is a board name *) FindBoardByTitle if prog "_staff/" ourBoard @ strcat dup strlen 1 - strcut pop "yes" setprop ">> Board set `staff' (only admins can post to it)." Tell else ">> Board `" ourArg @ strcat "' not found." strcat Tell exit then then else pop ">> Syntax: " ourCom @ strcat " #staff <board>" strcat Tell then ; : SetBoardGeneral ( -- ) (* set specified board staff-only *) CheckAdminPerm not if (* check admin perm *) ">> Permission denied." Tell exit then ourArg @ " " instr dup if (* find board to set *) ourArg @ swap strcut swap pop strip ourArg ! ourArg @ number? if FindBoardByNumber if (* this way if arg is a number... *) prog "_staff/" ourBoard @ strcat dup strlen 1 - strcut pop remove_prop ">> Board set `general' (anyone can post to it)." Tell else ">> Board number " (* or say we couldn't find *) ourArg @ strcat " not found." strcat Tell exit then else (* or find this way if arg is a board name *) FindBoardByTitle if prog "_staff/" ourBoard @ strcat dup strlen 1 - strcut pop "yes" setprop ">> Board set `general' (anyone can post to it)." Tell else ">> Board `" ourArg @ strcat "' not found." strcat Tell exit then then else pop ">> Syntax: " ourCom @ strcat " #general <board>" strcat Tell then ; : SetBoardPrivate ( -- ) (* set specified board private *) CheckAdminPerm not if (* check admin perm *) ">> Permission denied." Tell exit then ourArg @ " " instr dup if (* find board to set *) ourArg @ swap strcut swap pop strip ourArg ! ourArg @ number? if FindBoardByNumber if (* this way if arg is a number... *) prog "_closed/" ourBoard @ strcat dup strlen 1 - strcut pop "yes" setprop (* set it *) ">> Board set `private'." Tell else ">> Board number " (* or say we couldn't find *) ourArg @ strcat " not found." strcat Tell then else (* of find this way if arg is a board name *) FindBoardByTitle if prog "_closed/" ourBoard @ strcat dup strlen 1 - strcut pop "yes" setprop (* set it *) ">> Board set `private'." Tell else ">> Board `" (* or say we couldn't find *) ourArg @ strcat "' not found." strcat Tell then then else (* show syntax if we couldn't parse *) pop ">> Syntax: " ourCom @ strcat " #close <board>" strcat Tell then ; : SetBoardOpen ( -- ) (* set specified board open *) CheckAdminPerm not if (* check admin perm *) ">> Permission denied." Tell exit then ourArg @ " " instr dup if (* find board to set *) ourArg @ swap strcut swap pop strip ourArg ! ourArg @ number? if (* find this way if arg is a number... *) FindBoardByNumber if prog "_closed/" ourBoard @ strcat dup strlen 1 - strcut pop remove_prop (* set it *) ">> Board set `open'." Tell else ">> Board number " (* or say we couldn't find *) ourArg @ strcat " not found." strcat Tell then else (* or find this way if arg is a board name *) FindBoardByTitle if prog "_closed/" ourBoard @ strcat dup strlen 1 - strcut pop remove_prop (* set it *) ">> Board set `open'." Tell else ">> Board `" (* or say we couldn't find *) ourArg @ strcat "' not found." strcat Tell then then else (* show syntax if we couldn't parse *) pop ">> Syntax: " ourCom @ strcat " #open <board>" strcat Tell then ; : SetSearchTariff ( -- ) (* set penny tariff for searches *) "set search tariff" .tell ; : ShowAddPlayerSyntax ( -- ) (* show syntax for boad #include *) ">> Syntax: " ourCom @ strcat " #include <board> / <player>" strcat Tell ; : IncPlayerClosed ( -- ) (* add specified player to private board *) CheckAdminPerm not if (* check admin perm *) ">> Permission denied." Tell exit then ourArg @ "/" instr not if (* parsable? *) ShowAddPlayerSyntax exit then ourArg @ " " instr dup if (* then parse *) ourArg @ swap strcut swap pop strip dup "/" instr dup if strcut strip .pmatch dup not if (* find player *) ">> Sorry, player not found." Tell pop exit then swap strip dup if dup strlen 1 = if pop ShowAddPlayerSyntax else dup strlen 1 - strcut pop strip (* find board *) ourArg ! ourArg @ number? if FindBoardByNumber if (* set if found both *) prog "_closed/" ourBoard @ strcat 3 pick intostr strcat "yes" setprop ">> " swap name " added to board authorization list." strcat Tell else ">> Sorry, board number " (* or say we couldn't find *) ourArg @ strcat " not found." strcat Tell pop pid kill then else FindBoardByTitle if (* set if found both *) prog "_closed/" ourBoard @ strcat 3 pick intostr strcat "yes" setprop ">> " swap name " added to board authorization list." strcat Tell else ">> Sorry, board `" (* or say we couldn't find *) ourArg @ strcat " not found." strcat Tell pop pid kill then then then else pop ShowAddPlayerSyntax (* show syntax if we couldn't parse *) then else pop pop ShowAddPlayerSyntax then else pop ShowAddPlayerSyntax then ; : ShowRemPlayerSyntax ( -- ) (* show syntax for board #exclude *) ">> Syntax: " ourCom @ strcat " #exclude <board> / <player>" strcat Tell ; : ExcPlayerClosed ( -- ) (* remove player from private board *) CheckAdminPerm not if (* check admin perm *) ">> Permission denied." Tell exit then ourArg @ "/" instr not if (* parseable? *) ShowRemPlayerSyntax exit then ourArg @ " " instr dup if (* then parse *) ourArg @ swap strcut swap pop strip dup "/" instr dup if strcut strip .pmatch dup not if (* find player *) ">> Sorry, player not found." Tell pop exit then swap strip dup if dup strlen 1 = if pop ShowRemPlayerSyntax else (* find board; set if found both; otherwise notify *) dup strlen 1 - strcut pop strip ourArg ! ourArg @ number? if FindBoardByNumber if prog "_closed/" ourBoard @ strcat 3 pick intostr strcat remove_prop ">> " swap name strcat " removed from board authorization list." strcat Tell else ">> Sorry, board number " ourArg @ strcat " not found." strcat Tell pop pid kill then else FindBoardByTitle if prog "_closed/" ourBoard @ strcat 3 pick intostr strcat remove_prop ">> " swap name " removed from board authorization list." strcat Tell else ">> Sorry, board '" ourArg @ strcat " not found." strcat Tell pop pid kill then then then else pop ShowRemPlayerSyntax (* show syntax if we couldn't parse *) then else pop pop ShowRemPlayerSyntax then else pop ShowRemPlayerSyntax then ; : ShowAddAdminSyntax ( -- ) (* show syntax for #add *) ">> Syntax: " ourCom @ strcat " #add <player>" strcat Tell ; : AddAdministrator ( -- ) (* make specified player an admin *) CheckAdminPerm not if (* check admin perm *) ">> Permission denied." Tell exit then (* parse and add player to admin reflist *) ourArg @ " " instr dup if ourArg @ swap strcut swap pop strip dup if .pmatch dup if prog "_admin" 3 pick REF-add ">> " swap name strcat " added to board administrator list." strcat Tell else ">> Sorry, player not found." Tell then else pop ShowAddAdminSyntax (* or show syntax if couldn't parse *) then else pop ShowAddAdminSyntax then ; : ShowRemAdminSyntax ( -- ) (* show syntax for board #remove *) ">> Syntax: " ourCom @ strcat " #remove <player>" strcat Tell ; : RemAdministrator ( -- ) (* remove player's admin privileges *) CheckAdminPerm not if (* check admin perm *) ">> Permission denied." Tell exit then (* parse and remove player from admin reflist *) ourArg @ " " instr dup if ourArg @ swap strcut swap pop strip dup if .pmatch dup if prog "_admin" 3 pick REF-delete ">> " swap name strcat " removed from board administrator list." strcat Tell else ">> Sorry, player not found." Tell then else pop ShowRemAdminSyntax (* or show syntax if we couldn't parse *) then else pop ShowRemAdminSyntax then ; : CreateNewBoard ( -- ) (* create a new bulletin board *) CheckAdminPerm not if (* check admin perm *) ">> Permission denied." Tell exit then ourArg @ " " instr dup if (* parse *) ourArg @ swap strcut swap pop strip else pop ">> Syntax: " (* show syntax if we can't *) ourCom @ strcat " #create <name>" strcat Tell exit then (* start propdir for board *) prog "_boards/" 3 pick strcat systime intostr setprop ">> Board `" swap strcat "' created." strcat Tell ; : DeleteBoard ( -- ) (* delete an existing board *) CheckAdminPerm not if (* check admin perm *) ">> Permission denied." Tell exit then ourArg @ " " instr dup if (* parse or notify that we can't *) ourArg @ swap strcut swap pop strip else pop ">> Syntax: " ourCom @ strcat " #destroy <name>" strcat Tell exit then dup ourArg ! number? if FindBoardByNumber not if ">> Sorry, board number " ourArg @ strcat " not found." strcat Tell exit then else FindBoardByTitle not if ">> Sorry, board `" ourArg @ strcat "' not found" strcat Tell exit then then (* this will blow away all posts on board: get confirmation *) ">> Please confirm: You wish to delete board " ourBoard @ GetBoardName strcat ", and all its posts?" strcat Tell ">> [Enter `yes' to confirm]" Tell read "yes" smatch if (* if confirmed, remove all propdirs *) prog ourBoard @ RemoveDir prog ourBoard @ dup strlen 1 - strcut pop remove_prop prog "_closed/" ourBoard @ strcat RemoveDir ">> Board deleted." Tell else ">> Aborted." Tell then ; : HelpHeader ( -- ) (* show standard first line for help pages *) " " Tell "JBoard.muf (#" prog intostr strcat ")" strcat Tell " " Tell ; : DPad35 ( s -- ) (* pad s with trailing dots and cut to 35 chars *) " .............................." strcat 35 strcut pop ; : ComNameHelp ( -- ) (* show standard #alias and #rem help lines *) " " ourCom @ strcat " #alias <alias name>" strcat DPad35 " Set an alias for `" ourCom @ strcat "' (admin only)" ourBoolean @ if "" "(admin only)" subst then strcat strcat Tell " " ourCom @ strcat " #rename <new name>" strcat DPad35 " Rename the `" ourCom @ strcat "' command (admin only)" ourBoolean @ if "" "(admin only)" subst then strcat strcat Tell ; : DoReadHelp ( -- ) (* show help scren for read command *) HelpHeader "JBoard.muf is a global bulletin board program, with `search', `new'," " and `next' features." strcat Tell " " Tell " " ourCom @ strcat DPad35 " List all boards" strcat Tell " " ourCom @ strcat " <board>" strcat DPad35 " List posts on <board>" strcat Tell " " ourCom @ strcat " <board>/<post>" strcat DPad35 " Display <post> from <board>" strcat Tell " " ourCom @ strcat " #search <string>" strcat DPad35 " Search all boards for <string>" strcat Tell " " ourCom @ strcat " #search <board>/<string>" strcat DPad35 " Search <board> for <string>" strcat Tell " " ourCom @ strcat " #new" strcat DPad35 " List number of new posts on all boards" strcat Tell " " ourCom @ strcat " #new <board>" strcat DPad35 " List all new posts on <board>" strcat Tell " " ourCom @ strcat " #last <time>" strcat DPad35 " Display all posts since <time>" strcat Tell " " ourCom @ strcat " #last <board>/<time>" strcat DPad35 " Display all posts on <board> since <time>" strcat Tell CheckAdminPerm if ComNameHelp then " " Tell "Boards and posts may be specified by either number or name." Tell " " Tell "#Argument strings do not have to be typed completely: entering `" ourCom @ strcat " #s rosebud' will produce the same result as `" ourCom @ strcat " #search rosebud'. Posts are considered new if " "they are more recent than the last post you read, and -- if you " "have a `window' set -- within your specified window (see " "board" GetCurrentCommandName strcat " #help). The <time> parameter for #last can be any positive " "number and standard time unit: `1 month', `12 hours', `6 months'." strcat strcat strcat strcat strcat strcat Tell " " Tell "See also #help for " "read" GetOtherCommandNames strcat Tell ; : DoWriteHelp ( -- ) (* show help screen for write command *) HelpHeader "The " ourCom @ strcat " command allows you to add a post to an " "existing bulletin board." strcat strcat Tell " " Tell " " ourCom @ strcat " <board>/<subject>" strcat DPad35 " Add a post about <subject> to <board>" strcat Tell " " ourCom @ strcat " #noname <board>/<subject>" strcat DPad35 " Add an anonymous post to <board>" strcat Tell CheckAdminPerm if ComNameHelp then " " Tell "The board may be specified by either name or number." Tell " " Tell "See also #help for " "write" GetOtherCommandNames strcat Tell ; : DoEditHelp ( -- ) (* show help screen for edit command *) HelpHeader "The " ourCom @ strcat " command allows you to edit a post for " "which you have edit permission (that is, you are either the author " "of the post, or have admin permission for the bulletin boards.)" strcat strcat strcat Tell " " Tell " " ourCom @ strcat " <board>/<post>" strcat DPad35 " Edit an existing post" strcat Tell CheckAdminPerm if ComNameHelp then " " Tell "The board and post may be specified by either name or number." Tell " " Tell "See also #help for " "edit" GetOtherCommandNames strcat Tell ; : DoDeleteHelp ( -- ) (* show help screen for delete command *) HelpHeader "The " ourCom @ strcat " command allows you to delete a post for " "which you have delete permission (that is, you are either the " "author of the post, or have admin permission for the bulletin " "boards" strcat strcat strcat strcat Tell " " Tell " " ourCom @ strcat " <board>/<post>" strcat DPad35 " Delete an existing post." strcat Tell CheckAdminPerm if ComNameHelp then " " Tell "The board and post may be specified by either name or number." Tell " " Tell "See also #help for " "delete" GetOtherCommandNames strcat Tell ; : DoNextHelp ( -- ) (* show help screen for next command *) HelpHeader "The " ourCom @ strcat " command allows you to page through either " "the posts of a board or the results of a #search." strcat strcat Tell " " Tell " " ourCom @ strcat DPad35 " Display next post from <board|search>" strcat Tell CheckAdminPerm if ComNameHelp then " " Tell "If you have done a standard " "read" GetCurrentCommandName strcat " more recently, then the next post frim the same board will be " "displayed. If you have done a #search more recently, then the first " "or next post containing your search string will be displayed." strcat strcat strcat Tell " " Tell "See also #help for " "next" GetOtherCommandNames strcat Tell ; : DoBoardHelp ( -- ) (* show help screen for board command *) 1 ourBoolean ! HelpHeader (* show this version for admins *) CheckAdminPerm if "The " ourCom @ " command is used to administer the bulletin " "boards. All options listed are admin-only, except for #window." strcat strcat strcat Tell " " Tell " " ourCom @ strcat " #create <name>" strcat DPad35 " Create new board named <name>" strcat Tell " " ourCom @ strcat " #destroy <board>" strcat DPad35 " Delete board <board>" strcat Tell " " ourCom @ strcat " #private <board>" strcat DPad35 " Set <board> private" strcat Tell " " ourCom @ strcat " #open <board>" strcat DPad35 " Set <board> open" strcat Tell " " ourCom @ strcat " #include <board>/<player>" strcat DPad35 " Include <player> in private <board>" strcat Tell " " ourCom @ strcat " #exclude <board>/<player>" strcat DPad35 " Exclude <player> from private <board>" strcat Tell " " ourCom @ strcat " #noname <board>" strcat DPad35 " Toggle nonames-allowed for <board>" strcat Tell " " ourCom @ strcat " #add <player>" strcat DPad35 " Add <player> to admin list" strcat Tell " " ourCom @ strcat " #remove <player>" strcat DPad35 " Remove <player> from admin list" strcat Tell " " ourCom @ strcat " #staff <board>" strcat DPad35 " Set <board> writable by staff only" strcat Tell " " ourCom @ strcat " #general <board>" strcat DPad35 " Set <board> writable by general public" strcat Tell " " ourCom @ strcat " #window <number> <units>" strcat DPad35 " Set window to <time>" strcat Tell " " ourCom @ strcat " #window" strcat DPad35 " Clear window setting" strcat Tell ComNameHelp " " Tell "Private boards are only visible to admins and players included " "in the board's authorization list. Admins are wizards, the owner " "of this program and action, or players included in the admin " "list. A `window' is the time after which a post is considered " "`old', and will not appear in `" "read" GetCurrentCommandName strcat " #new <board>' Its <time> pair can be any positive " "number and standard time unit. Examples: `300 hours', `1 day', " "`3 months'. The #noname option is not allowed by default. `" ourCom @ " #noname' toggles this option on and off." strcat strcat strcat strcat strcat strcat strcat strcat strcat Tell (* show this version for non admins *) else "The " ourCom @ " command is primarily used by the MUCK's " "administrators to configure bulletin boards. It does though " "have one purpose for non-admin users: Setting your `new' " "window." strcat strcat strcat strcat strcat Tell " " Tell "Your window is time beyond which posts are considered `old', " "and will no longer appear when you do `" "read" GetCurrentCommandName strcat " #new <board>'." strcat "For example, if you set your window to `six weeks', then " "posts older than six weeks will not appear on the #new lists." strcat strcat strcat Tell " " Tell " " ourCom @ strcat " #window <time>" strcat DPad35 " Set window to <time>" strcat Tell " " ourCom @ strcat " #window" strcat DPad35 " Clear window setting" strcat Tell " " Tell "The <time> parameter can be any positive number and " "standard time unit. Examples: '300 hours', '1 day', '2 months'." strcat Tell " " Tell "See also #help for " "board" GetOtherCommandNames strcat Tell then ; : DoCommandAlias ( -- ) (* set an alias for specified command *) (* or, if called from DoCommandRename, replace a command name *) CheckAdminPerm not if (* check admin perm *) ">> Permission denied." Tell exit then (* check for illegal action names *) ourArg @ " " instr dup if ourArg @ swap strcut swap pop strip ourString ! ourString @ "home" smatch ourString @ "here" smatch ourString @ "me" smatch ourString @ "#*" smatch or or or if ">> Sorry, invalid exit name." Tell else (* keep track of original names... may be re-aliasing *) ourCom @ ourCounter ! prog "_alias/" command @ strcat getpropstr dup if command ! else pop then then (* ourBoolean is true if we're renaming rather than aliasing *) ourBoolean @ if (* find com name and substitute new name *) trig name ";" ourCom @ strcat ";" strcat over over instr if "" swap subst trig swap setname else pop pop trig name ";" ourCom @ strcat over over instr if "" swap subst trig swap setname else pop pop trig name ourCom @ ";" strcat over over instr if "" swap subst trig swap setname else pop pop ">> ERROR: Unable to rename." Tell pid kill then then then trig name ";" strcat ourString @ strcat trig swap setname prog "_alias/" ourString @ strcat command @ setprop prog "_orign/" command @ strcat ourString @ setprop ">> Command renamed." Tell else trig name ";" ourString @ ";" strcat strcat instr trig name ";" ourString @ strcat instr trig name ourString @ ";" strcat instr or or not if trig name ";" strcat ourString @ strcat trig swap setname then prog "_alias/" ourString @ strcat command @ setprop ">> Alias created." Tell then else ">> Syntax: " ourCom @ strcat " #alias <alias name>" strcat Tell then ; : DoCommandRename ( -- ) (* rename command *) (* many routines are shared with DoCommandAlias, so do it by setting ourBoolean true and calling DoCommandAlias. *) ourArg @ " " instr dup if 1 ourBoolean ! DoCommandAlias else pop ">> Syntax: " ourCom @ strcat " #rename <new name>" strcat Tell then ; : ShowNoNameSyntax ( -- ) (* show syntax for board #noname *) ">> Syntax: " ourCom @ strcat " #noname <board>" strcat Tell ; : ToggleNoNames ( -- ) (* toggle #nonames allowed for a board *) ourArg @ " " instr dup if ourArg @ swap strcut swap pop strip ourArg ! ourArg @ number? if FindBoardByNumber not if ">> Sorry, board number " ourArg @ strcat " not found." strcat Tell pid kill then else FindBoardByTitle not if ">> Sorry, board `" ourArg @ strcat " not found." strcat Tell pid kill then then prog "_anon/" ourBoard @ dup strlen 1 - strcut pop strcat over over getprop if remove_prop ">> No-name posts are now *not* allowed for board " ourBoard @ GetBoardName strcat "." strcat Tell else "yes" setprop ">> No-name posts are now allowed for board " ourBoard @ GetBoardName strcat "." strcat Tell then else pop ShowNoNameSyntax then ; : DoNextSearch ( -- ) (* show next post in search results *) (* protect prop keeps search results for one read *) (* we're searching now so can delete it *) me @ "_prefs/news/protect" remove_prop me @ over "/" strcat nextprop (* get next post *) dup "" "_prefs/news/search/" subst ourPost ! ourPost @ dup "/" rinstr strcut pop ourBoard ! ShowPost (* show post *) me @ swap remove_prop (* update search set *) me @ "_prefs/news/search/" nextprop not if " " Tell ">> End of #search results." Tell then ; : DoNextRead ( -- ) (* show next post on current board *) pop me @ "_prefs/news/lastpost" getpropstr dup if prog swap nextprop dup if dup dup "/" rinstr strcut pop ourBoard ! ourPost ! ShowPost UpdateLast else ">> No more posts on this board." Tell then else ">> Sorry, no `last read' post currently recorded for you." Tell then ; : DoNext ( -- )(* show next post in search results or current board *) ourArg @ if ourArg @ "#" stringpfx if ourArg @ "#h" stringpfx if DoNextHelp exit else ourArg @ "#a" stringpfx if DoCommandAlias exit else ourArg @ "#r" stringpfx if DoCommandRename exit else ">> #Argument not understood." Tell exit then then then then then me @ "_prefs/news/search/_boards/" nextprop dup if DoNextSearch (* this way if we're in a search *) else DoNextRead (* or this way if we're not *) then ; : DoPostList ( -- ) (* list posts on specified board *) 0 ourCounter ! " " Tell ourBoard @ GetBoardHeader Tell (* show board header *) 0 ourCounter ! prog ourBoard @ nextprop dup if (* get posts *) begin (* begin post-showing loop *) dup while ourBoolean @ if (* skip `old' posts *) dup CheckOldPost if prog swap nextprop continue then then dup ourPost ! (* store current in ourPost, for GetPostHeader *) dup GetPostNumber (* number post *) ") " strcat dup strlen 3 = if " " strcat then GetPostHeader strcat Tell (* cat number and formatted header *) prog swap nextprop repeat (* end post-showing loop *) pop else pop ">> Sorry, there are no posts on this board yet." Tell then ; : SearchBoard ( -- ) (* search ourBoard for posts with ourSubject *) 0 ourBoolean ! 0 ourCounter2 ! prog swap nextprop begin (* begin post-searching loop *) dup while ourCounter2 @ 1 + ourCounter2 ! dup prog over "/" strcat nextprop begin (* begin line-searching loop *) dup while (* for a hit, format and display post, break *) prog over getpropstr tolower ourString @ tolower instr if me @ "_prefs/news/search/" 3 pick strcat "1" setprop 1 ourBoolean ! dup "" "_boards/" subst dup "/" instr 1 - strcut pop "/" strcat ourCounter2 @ intostr strcat " ..................................................." strcat 24 strcut pop " " strcat ourSubject ! pop prog over "/subj" strcat getpropstr ourSubject @ swap strcat " ( " strcat ourSubject ! prog over "/auth" strcat getprop dup ok? if name else pop "<unknown>" then ourSubject @ swap strcat ", %D )" strcat ourSubject ! prog over "/time" strcat getprop ourSubject @ swap timefmt Tell break then prog swap nextprop repeat (* end line-searching loop *) pop prog swap nextprop repeat (* end post-searching loop *) pop (* if we got a hit, protect search results *) ourBoolean @ if me @ "_prefs/news/protect" "yes" setprop then ; : DoBoardList ( -- ) (* display list of all boards *) background " " Tell "BULLETIN BOARDS:" Tell " " Tell 0 ourCounter ! prog "_boards/" nextprop dup if (* check: do we have any boards? *) 1 ourCounter ! (* init board-number counter *) begin (* begin board-listing loop *) dup while (* skip private boards user isn't authorized for *) dup CheckBoardPerm not if prog swap nextprop continue then ourCounter @ intostr (* format board number *) ") " strcat dup strlen 2 = if " " strcat then over GetBoardName strcat (* go format board name *) " (" strcat (* go get number of posts; format *) over GetNumPosts dup "1" smatch if " post)" else " posts)" then strcat strcat Tell (* display string for this board *) ourCounter @ 1 + ourCounter ! (* increment *) prog swap nextprop repeat (* end board-listing loop *) pop else ">> Sorry, no boards have been created." Tell then ; : DoNewBoardList ( -- ) (* display list of all boards *) background " " Tell "BULLETIN BOARDS:" Tell " " Tell 0 ourCounter ! prog "_boards/" nextprop dup if (* check: do we have any boards? *) 1 ourCounter ! (* init board-number counter *) begin (* begin board-listing loop *) dup while (* skip private boards user isn't authorized for *) dup CheckBoardPerm not if prog swap nextprop continue then ourCounter @ intostr (* format board number *) ") " strcat dup strlen 2 = if " " strcat then over GetBoardName strcat (* go format board name *) " (" strcat (* go get number of posts; format *) over GetNumNewPosts dup "1" smatch if " new post)" else " new posts)" then strcat strcat Tell (* display string for this board *) ourCounter @ 1 + ourCounter ! (* increment *) prog swap nextprop repeat (* end board-listing loop *) pop else ">> Sorry, no boards have been created." Tell then ; : GetLastFromBoard ( -- )(* show posts from ourBoard since ourTime *) 0 ourBoolean ! (* true if we got a hit *) ourBoard @ GetBoardHeader Tell (* show board header *) 1 ourPostCounter ! (* start a counter for posts this board *) begin (* begin post-checking loop *) ourPostCounter @ intostr ourPost ! FindPostByNumber while ourPost @ dup "." rinstr 1 - strcut pop (* check time *) dup "/" rinstr strcut swap pop atoi ourTime @ > if (* show if recent enough *) 1 ourBoolean ! GetPostHeader Tell GetBoardNumber ":" strcat (* board number *) ourPost @ GetPostNumber strcat " " strcat (* post number *) GetPostHeader strcat Tell (* header: subj, auth, time *) " " Tell (* line sep *) prog ourPost @ ShowList (* content *) "---------------------------------------------------------------------" Tell then ourPost @ CheckOldPost not if me @ "_prefs/news/lastpost" ourPost @ setprop then ourPostCounter @ 1 + ourPostCounter ! repeat ourBoolean @ not if " <none>" Tell then ; : ShowReadLastSyntax ( -- ) (* show syntax for read #last *) ">> Syntax: " ourCom @ " #last [<board>/]lt;number> <units>" strcat strcat Tell ">> Examples: " ourCom @ " #last 6 weeks" strcat strcat Tell " " ourCom @ " #last 3/10 days" strcat strcat Tell " " ourCom @ " #last policy/1 month" strcat strcat Tell ; : DoReadLast ( -- ) (* setup display of all posts in last <time> *) ourArg @ " " instr dup if ourArg @ swap strcut swap pop strip ourArg ! ourArg @ "/" instr dup if ourArg @ swap strcut strip ourCounter ! dup strlen 1 - strcut pop strip ourArg ! ourCounter @ ParseTimeString if systime swap - dup 0 < if ">> Sorry, MU*'s weren't even around then, so no posts." Tell exit else ourTime ! then else ShowReadLastSyntax exit then ourArg @ number? if FindBoardByNumber not if ">> Sorry, board number " ourArg @ strcat " not found." strcat Tell exit then else FindBoardByTitle not if ">> Sorry, board `" ourArg @ strcat "' not found." strcat Tell exit then then else pop ourArg @ ParseTimeString if systime swap - dup 0 < if ">> Sorry, MU*'s weren't even around then, so no posts." Tell exit else ourTime ! then else ShowReadLastSyntax exit then then else pop ShowReadLastSyntax exit then ourBoard @ if GetLastFromBoard else 1 ourCounter2 ! begin ourCounter2 @ intostr ourArg ! FindBoardByNumber while GetLastFromBoard ourCounter2 @ 1 + ourCounter2 ! repeat depth if pop then then ; : DoParseSearch ( -- ) (* parse search request; dispatch *) begin depth while pop repeat (* clear stack to keep things tidy! *) ourArg @ dup " " instr dup if (* parse arg *) strcut swap pop dup "/" instr dup if strcut strip dup if ourString ! else pop ">> Syntax: " ourCom @ strcat " #search <string> [/<board>]" strcat Tell exit then dup strlen 1 - strcut pop strip dup if ourArg ! 1 ourBoolean ! else pop ">> Syntax: " ourCom @ strcat " #search <string> [/<board>]" strcat Tell exit then else pop ourString ! then else pop pop ">> Syntax: " ourCom @ strcat " #search <string> [/<board>]" strcat Tell exit then (* if a board to search is specified, find it *) ourBoolean @ if ourArg @ number? if FindBoardByNumber not if ">> Sorry, board number " ourArg @ strcat " not found." strcat Tell exit then else FindBoardByTitle not if ">> Sorry, board `" ourArg @ strcat "' not found." strcat Tell exit then then then ">> Searching for $string..." ourString @ "$string" subst Tell ourBoard @ if (* if we have a specific board, search it... *) ourBoard @ SearchBoard else (* otherwise search all boards *) 1 begin (* begin board-getting loop *) dup intostr ourArg ! FindBoardByNumber if ourBoard @ SearchBoard (* go search one board *) else break then begin dup int? not while pop depth not if break then repeat 1 + repeat (* end board-getting loop *) then begin depth while pop repeat ">> Done." Tell ; : SetNew ( -- ) (* strip #n from arg and set ourBoolean *) ourArg @ " " instr dup if (* strip `#new' from arg string *) ourArg @ swap strcut swap pop strip ourArg ! else pop 1 ourBoolean ! DoNewBoardList pid kill then 1 ourBoolean ! (* this will tell DisplayPostList to exclude old *) ; : DoParseRead ( -- )(* find board to display; default is board list *) me @ "_prefs/news/protect" getpropstr not if me @ "_prefs/news/search/" RemoveDir then ourArg @ if ourArg @ "#" stringpfx if ourArg @ "#h" stringpfx if DoReadHelp exit else ourArg @ "#a" stringpfx if DoCommandAlias exit else ourArg @ "#r" stringpfx if DoCommandRename exit else ourArg @ "#s" stringpfx if DoParseSearch exit else ourArg @ "#l" stringpfx if DoReadLast exit else ourArg @ "#n" stringpfx if SetNew else ">> #Argument not understood." Tell exit then then then then then then then ourArg @ "/" instr if (* check: board/post specified? *) ParsePostPath not if (* ... if so, find path *) exit then ourPost @ number? if FindPostByNumber not if ">> Sorry, post number " ourPost @ strcat " not found." strcat Tell pid kill then else FindPostByTitle not if ">> Sorry, post `" ourPost @ strcat "' not found." strcat Tell pid kill then then ShowPost else ourArg @ number? if (* check: board number specified? *) FindBoardByNumber if (* ... if so, find by number *) DoPostList else ">> Sorry, board number " ourArg @ strcat " not found." strcat Tell pid kill then else (* check: board title specified *) FindBoardByTitle if (* if so, find by title *) DoPostList else ">> Sorry, board `" ourArg @ strcat "' not found." strcat Tell pid kill then then then else (* ... otherwise, show list of boards *) ourBoolean @ if DoNewBoardList (* either in #new format *) else DoBoardList (* or in `all' format *) then then ; : DoParseWrite ( -- ) (* parse write command *) ourArg @ if (* check #args *) ourArg @ "#n" stringpfx ourArg @ " " instr and if ourArg @ dup " " instr strcut swap pop strip ourArg ! 1 ourBoolean ! then ourArg @ "#" stringpfx if ourArg @ "#h" stringpfx if DoWriteHelp exit else ourArg @ "#a" stringpfx if DoCommandAlias exit else ourArg @ "#r" stringpfx if DoCommandRename exit else ">> #Argument not understood." Tell exit then then then else ourArg @ "/" instr dup if (* parse post subject *) ourArg @ swap strcut strip dup "/" instr if (* subjects with /'s would mess up propdirs *) ">> Sorry, post subjects cannot include `/' slashes." Tell pop pop pid kill else dup 45 strcut pop ourSubject ! (* limit subjs to 45 chars *) dup strlen 1 - strcut pop strip ourArg ! then strip dup strlen 1 - strcut pop ourArg ! else DoWriteHelp exit (* show help if we couldn't parse *) then ourArg @ number? if (* find board to post on *) FindBoardByNumber if WritePost (* go write post *) else ">> Sorry, board number " ourArg @ strcat " not found." strcat Tell pid kill then else FindBoardByTitle if WritePost (* go write post *) else ">> Sorry, board `" ourArg @ strcat " not found." strcat Tell pid kill then then then else DoWriteHelp (* show help if we couldn't parse *) then ; : DoParseEdit ( -- ) (* parse edit command *) ourArg @ if (* check #args *) ourArg @ "#" stringpfx if ourArg @ "#h" stringpfx if DoEditHelp exit else ourArg @ "#a" stringpfx if DoCommandAlias exit else ourArg @ "#r" stringpfx if DoCommandRename exit else ">> #Argument not understood." Tell exit then then then else (* parse *) ourArg @ "/" instr dup if ourArg @ swap strcut strip dup if ourPost ! else pop DoEditHelp exit then dup strlen 1 - strcut pop strip dup if ourArg ! else pop DoEditHelp exit then ourArg @ number? if (* find board... *) FindBoardByNumber not if ">> Sorry, board number " ourArg @ strcat " not found." strcat Tell pid kill then else FindBoardByTitle not if ">> Sorry, board `" ourArg @ strcat "' not found." strcat Tell pid kill then then ourPost @ number? if (* ... then find post *) FindPostByNumber not if ">> Sorry, post number " ourPost @ strcat " not found." strcat Tell pid kill then else FindPostByTitle not if ">> Sorry, post `" ourPost @ strcat "' not found." strcat Tell pid kill then then EditPost (* go edit post *) else pop DoEditHelp then then else DoEditHelp then ; : DoParseDelete ( -- ) (* parse delete command *) ourArg @ if (* check #args *) ourArg @ "#" stringpfx if ourArg @ "#h" stringpfx if DoDeleteHelp exit else ourArg @ "#a" stringpfx if DoCommandAlias exit else ourArg @ "#r" stringpfx if DoCommandRename exit else ">> #Argument not understood." Tell exit then then then else (* parse *) ourArg @ "/" instr dup if ourArg @ swap strcut strip dup if ourPost ! else pop DoDeleteHelp exit then dup strlen 1 - strcut pop strip dup if ourArg ! else pop DoDeleteHelp exit then ourArg @ number? if (* find board... *) FindBoardByNumber not if ">> Sorry, board number " ourArg @ strcat " not found." strcat Tell pid kill then else FindBoardByTitle not if ">> Sorry, board `" ourArg @ strcat "' not found." strcat Tell pid kill then then ourPost @ number? if (* ... then find post *) FindPostByNumber not if ">> Sorry, post number " ourPost @ strcat " not found." strcat Tell pid kill then else FindPostByTitle not if ">> Sorry, post `" ourPost @ strcat "' not found." strcat Tell pid kill then then DeletePost (* go delete post *) else pop DoDeleteHelp then then else DoDeleteHelp then ; : DoParseBoard ( -- ) (* parse board command *) ourArg @ if (* everything is an #arg; we're just routing *) ourArg @ "#ren" stringpfx if DoCommandRename else ourArg @ "#ali" stringpfx if DoCommandAlias else ourArg @ "#h" stringpfx if DoBoardHelp else ourArg @ "#c" stringpfx if CreateNewBoard else ourArg @ "#n" stringpfx if ToggleNoNames else ourArg @ "#d" stringpfx if DeleteBoard else ourArg @ "#w" stringpfx if SetPostWindow else ourArg @ "#p" stringpfx if SetBoardPrivate else ourArg @ "#o" stringpfx if SetBoardOpen else ourArg @ "#s" stringpfx if SetBoardStaff else ourArg @ "#g" stringpfx if SetBoardGeneral else ourArg @ "#i" stringpfx if IncPlayerClosed else ourArg @ "#e" stringpfx if ExcPlayerClosed else ourArg @ "#a" stringpfx if AddAdministrator else ourArg @ "#r" stringpfx if RemAdministrator else ourArg @ "#t" stringpfx if SetSearchTariff else ">> #Argument not understood." Tell then then then then then then then then then then then then then then then then else DoBoardHelp then ; : main "me" match me ! (* no imposters! *) strip ourArg ! (* store orig argument *) command @ ourCom ! (* store orig command name *) ourArg @ "#" stringpfx if ourArg @ " " instr if ourArg @ dup " " instr strcut pop strip ourString ! else ourArg @ ourString ! then (* filter out invalid #args early *) "#install" ourString @ stringpfx "#help" ourString @ stringpfx "#search" ourString @ stringpfx "#new" ourString @ stringpfx "#last" ourString @ stringpfx "#window" ourString @ stringpfx "#noname" ourString @ stringpfx "#private" ourString @ stringpfx "#open" ourString @ stringpfx "#include" ourString @ stringpfx "#exclude" ourString @ stringpfx "#add" ourString @ stringpfx "#remove" ourString @ stringpfx "#create" ourString @ stringpfx "#destroy" ourString @ stringpfx "#alias" ourString @ stringpfx "#rename" ourString @ stringpfx "#staff" ourString @ stringpfx "#general" ourString @ stringpfx "#tarrif" ourString @ stringpfx or or or or or or or or or or or or or or or or or or or not if ">> #Argument not understood." Tell exit then then (* command may be aliased; get `official' name *) prog "_alias/" command @ strcat getpropstr dup if command ! else pop then (* go to appropriate command *) command @ "read" smatch if DoParseRead else command @ "write" smatch if DoParseWrite else command @ "editmsg" smatch if DoParseEdit else command @ "delete" smatch if DoParseDelete else command @ "board" smatch if DoParseBoard else command @ "next" smatch if DoNext else ">> Command not understood. Please contact " prog owner name strcat " or a staff member." strcat Tell then then then then then then ; . c q