From 4bc1948dc256b12750b8c3b405d0be8db08f867e Mon Sep 17 00:00:00 2001 From: Aleteoryx Date: Sat, 28 Dec 2024 00:59:26 -0500 Subject: [PATCH] uh, docs --- doc/doctools/gen/irc.man | 68 +++++ doc/doctools/gen/main.man | 12 + doc/doctools/gen/router.man | 5 + doc/doctools/gen/threads.man | 5 - doc/md/irc.md | 114 +++++++++ doc/md/main.md | 10 + doc/md/{threads.md => router.md} | 6 +- src/irc.tcl | 415 ++++++++++++++++++------------- src/main.tcl | 18 ++ 9 files changed, 478 insertions(+), 175 deletions(-) create mode 100644 doc/doctools/gen/router.man delete mode 100644 doc/doctools/gen/threads.man rename doc/md/{threads.md => router.md} (69%) diff --git a/doc/doctools/gen/irc.man b/doc/doctools/gen/irc.man index d31004e..c574a4a 100644 --- a/doc/doctools/gen/irc.man +++ b/doc/doctools/gen/irc.man @@ -1,5 +1,73 @@ [manpage_begin irc tclircc 0.0.1] [titledesc {Library irc.tcl}] [description] +[para] +This is a to-spec IRCv3 client library. +It is designed to be freely extracted. +[section {Getting a channel}] +[file irc.tcl] provides 2 ways to connect to an IRC server. +[subsection [concat [cmd irc::connect] [arg hostname] [arg port] [opt [arg usetls]]]] + +Connects to [arg hostname]:[arg port], and sets up all the necessary state. +If [arg usetls] is set to true, the [package tls] module will be used to connect, +instead of the builtin [cmd socket] command. If unset, [cmd socket] will be used. + +[para] +[sectref {Channel metadata}] is initialized with [arg proto], [arg hostname], [arg port], and [arg uri] set. +[subsection [concat [cmd irc::enroll] [arg chan] [opt [arg meta]]]] + +Sets up the internal state necessary for chan to be used as an IRC socket. +It is called internally by [cmd irc::connect]. +This command is exposed for the use-case where an IRC channel might have a more bespoke acquisition process than a simple socket connection. + +[para] +[arg meta] is the initial state of the [sectref {channel metadata}]. +[section {Listening to it}] +[file irc.tcl] provides an event dispatch system, via a [cmd fileevent] script registered on the IRC channel. +Events are dispatched by matching their patterns against incoming messages. +[subsection [concat [cmd irc::listen] [arg subcommand] [arg chan]]] +Enable or disable the [cmd fileevent] script for the dispatch system. + +[list_begin definitions] +[call [cmd irc::listen] [const on] [arg chan]] +Apply the [cmd fileevent] wrapper to [arg chan]. +Returns the previous [cmd fileevent] wrapper. +[call [cmd irc::listen] [const off] [arg chan]] +Remove the [cmd fileevent] wrapper from [arg chan]. +Errors if the channel does not currently have the irc handler set. +[list_end] +[subsection [concat [cmd irc::listener] [arg subcommand] [arg chan] [opt [arg arg]...]]] +Configure [cmd listener]-type event handlers. +[para] +[cmd listener]-type handlers are scripts, executed in either a sub-interpreter or a seperate thread. +Interpreter listeners should yield themselves with [cmd after] during long operations, and never block for extended periods. +If that is infeasible, threaded listeners are recommended. +[para] +[cmd listener]-type scripts are spawned with the variable [var dispatch] set to the channel they are to recieve dispatches over. +Each line will be a [cmd dict] with contents as described in [sectref {Event Dispatch Contents}]. +Interpreters are given access to the [sectref {Dispatch-aliased IRC commands}], while threads are given the variable [var parent], the ID of the main thread. +[para] +When a listener is removed, it will recieve a message of just [const end]. +It should perform necessary cleanup quickly, as the application is likely exiting. +Threads may simply [cmd thread::release] themselves, while interps may call the provided [cmd selfdestruct]. + +[list_begin definitions] +[call [cmd irc::listener] [const add] [arg chan] [opt [option -thread]] [arg patlist] [arg script]] +Registers [arg script] as a [cmd listener]-type handler on [arg chan], using [arg patlist] as the [sectref {Message Pattern List}]. +Returns an id that can be passed to [cmd irc::listener] [const remove] or [cmd irc::patlist]. +[para] +If [option -thread] is passed, it will be created as a threaded listener, otherwise it will be created in a sub-interpreter. +[call [cmd irc::listener] [const remove] [arg chan] [arg id]] +Unregisters the listener identified by [arg id] from [arg chan]. +[para] +Ignores requests for nonexistent handlers or handlers of the wrong type. +[list_end] +[subsection [concat [cmd irc::handler] [arg subcommand] [arg chan] [opt [arg arg]...]]] + +[subsection [concat [cmd irc::is] [arg type] [arg value]]] +Validation helper. + +[para] + [manpage_end] diff --git a/doc/doctools/gen/main.man b/doc/doctools/gen/main.man index 6f177ff..342f96a 100644 --- a/doc/doctools/gen/main.man +++ b/doc/doctools/gen/main.man @@ -1,5 +1,17 @@ [manpage_begin main_thread tclircc 0.0.1] [titledesc {Thread main}] [description] +[para] +This is the application entrypoint. It does the following. +[list_begin itemized] +[item] +brings up the routing system +[item] +brings up the database +[item] +loads the other core threads +[item] +opens a window +[list_end] [manpage_end] diff --git a/doc/doctools/gen/router.man b/doc/doctools/gen/router.man new file mode 100644 index 0000000..0eb2ffe --- /dev/null +++ b/doc/doctools/gen/router.man @@ -0,0 +1,5 @@ +[manpage_begin router tclircc 0.0.1] +[titledesc {Component router.tcl}] +[description] +[manpage_end] + diff --git a/doc/doctools/gen/threads.man b/doc/doctools/gen/threads.man deleted file mode 100644 index e834b5c..0000000 --- a/doc/doctools/gen/threads.man +++ /dev/null @@ -1,5 +0,0 @@ -[manpage_begin threads tclircc 0.0.1] -[titledesc {Component threads.tcl}] -[description] -[manpage_end] - diff --git a/doc/md/irc.md b/doc/md/irc.md index 51e921f..0642b42 100644 --- a/doc/md/irc.md +++ b/doc/md/irc.md @@ -14,6 +14,120 @@ irc \- Library irc\.tcl - [Table Of Contents](#toc) + - [Synopsis](#synopsis) + - [Description](#section1) + - [Getting a channel](#section2) + + - [__irc::connect__ *hostname* *port* + ?*usetls*?](#subsection1) + + - [__irc::enroll__ *chan* ?*meta*?](#subsection2) + + - [Listening to it](#section3) + + - [__irc::listen__ *subcommand* *chan*](#subsection3) + + - [__irc::listener__ *subcommand* *chan* + ?*arg*\.\.\.?](#subsection4) + + - [__irc::handler__ *subcommand* *chan* + ?*arg*\.\.\.?](#subsection5) + + - [__irc::is__ *type* *value*](#subsection6) + +# SYNOPSIS + +[__irc::listen__ __on__ *chan*](#1) +[__irc::listen__ __off__ *chan*](#2) +[__irc::listener__ __add__ *chan* ?__\-thread__? *patlist* *script*](#3) +[__irc::listener__ __remove__ *chan* *id*](#4) + # DESCRIPTION + +This is a to\-spec IRCv3 client library\. It is designed to be freely extracted\. + +# Getting a channel + +"irc\.tcl" provides 2 ways to connect to an IRC server\. + +## __irc::connect__ *hostname* *port* ?*usetls*? + +Connects to *hostname*:*port*, and sets up all the necessary state\. If +*usetls* is set to true, the __tls__ module will be used to connect, +instead of the builtin __socket__ command\. If unset, __socket__ will be +used\. + +__Channel metadata__ is initialized with *proto*, *hostname*, *port*, +and *uri* set\. + +## __irc::enroll__ *chan* ?*meta*? + +Sets up the internal state necessary for chan to be used as an IRC socket\. It is +called internally by __irc::connect__\. This command is exposed for the +use\-case where an IRC channel might have a more bespoke acquisition process than +a simple socket connection\. + +*meta* is the initial state of the __channel metadata__\. + +# Listening to it + +"irc\.tcl" provides an event dispatch system, via a __fileevent__ script +registered on the IRC channel\. Events are dispatched by matching their patterns +against incoming messages\. + +## __irc::listen__ *subcommand* *chan* + +Enable or disable the __fileevent__ script for the dispatch system\. + + - __irc::listen__ __on__ *chan* + + Apply the __fileevent__ wrapper to *chan*\. Returns the previous + __fileevent__ wrapper\. + + - __irc::listen__ __off__ *chan* + + Remove the __fileevent__ wrapper from *chan*\. Errors if the channel + does not currently have the irc handler set\. + +## __irc::listener__ *subcommand* *chan* ?*arg*\.\.\.? + +Configure __listener__\-type event handlers\. + +__listener__\-type handlers are scripts, executed in either a sub\-interpreter +or a seperate thread\. Interpreter listeners should yield themselves with +__after__ during long operations, and never block for extended periods\. If +that is infeasible, threaded listeners are recommended\. + +__listener__\-type scripts are spawned with the variable __dispatch__ set +to the channel they are to recieve dispatches over\. Each line will be a +__dict__ with contents as described in __Event Dispatch Contents__\. +Interpreters are given access to the __Dispatch\-aliased IRC commands__, +while threads are given the variable __parent__, the ID of the main thread\. + +When a listener is removed, it will recieve a message of just __end__\. It +should perform necessary cleanup quickly, as the application is likely exiting\. +Threads may simply __thread::release__ themselves, while interps may call +the provided __selfdestruct__\. + + - __irc::listener__ __add__ *chan* ?__\-thread__? *patlist* *script* + + Registers *script* as a __listener__\-type handler on *chan*, using + *patlist* as the __Message Pattern List__\. Returns an id that can be + passed to __irc::listener__ __remove__ or __irc::patlist__\. + + If __\-thread__ is passed, it will be created as a threaded listener, + otherwise it will be created in a sub\-interpreter\. + + - __irc::listener__ __remove__ *chan* *id* + + Unregisters the listener identified by *id* from *chan*\. + + Ignores requests for nonexistent handlers or handlers of the wrong type\. + +## __irc::handler__ *subcommand* *chan* ?*arg*\.\.\.? + +## __irc::is__ *type* *value* + +Validation helper\. diff --git a/doc/md/main.md b/doc/md/main.md index 8032bc2..d37b488 100644 --- a/doc/md/main.md +++ b/doc/md/main.md @@ -17,3 +17,13 @@ main\_thread \- Thread main - [Description](#section1) # DESCRIPTION + +This is the application entrypoint\. It does the following\. + + - brings up the routing system + + - brings up the database + + - loads the other core threads + + - opens a window diff --git a/doc/md/threads.md b/doc/md/router.md similarity index 69% rename from doc/md/threads.md rename to doc/md/router.md index f0d6007..8d2f9e6 100644 --- a/doc/md/threads.md +++ b/doc/md/router.md @@ -2,13 +2,13 @@ toc: false --- -[//000000001]: # (threads \- ) +[//000000001]: # (router \- ) [//000000002]: # (Generated from file 'doctools' by tcllib/doctools with format 'markdown') -[//000000003]: # (threads\(tclircc\) 0\.0\.1 "") +[//000000003]: # (router\(tclircc\) 0\.0\.1 "") # NAME -threads \- Component threads\.tcl +router \- Component router\.tcl # Table Of Contents diff --git a/src/irc.tcl b/src/irc.tcl index 495c66d..e73f7ed 100644 --- a/src/irc.tcl +++ b/src/irc.tcl @@ -2,6 +2,9 @@ # [manpage_begin irc tclircc 0.0.1] # [titledesc {Library irc.tcl}] # [description] +# [para] +# This is a to-spec IRCv3 client library. +# It is designed to be freely extracted. # handler types: # chan // irc::listener @@ -21,6 +24,10 @@ package require Thread +#*** +# [section {Getting a channel}] +# [file irc.tcl] provides 2 ways to connect to an IRC server. + namespace eval ::irc { variable log [logger::init irc] variable logd [logger::init irc::dispatch] @@ -29,8 +36,247 @@ namespace eval ::irc { variable chan.handlers variable chan.interceptors - # documented - proc is {type value {cap {}}} { + #*** + # [subsection [concat [cmd irc::connect] [arg hostname] [arg port] [opt [arg usetls]]]] + # + # Connects to [arg hostname]:[arg port], and sets up all the necessary state. + # If [arg usetls] is set to true, the [package tls] module will be used to connect, + # instead of the builtin [cmd socket] command. If unset, [cmd socket] will be used. + # + # [para] + # [sectref {Channel metadata}] is initialized with [arg proto], [arg hostname], [arg port], and [arg uri] set. + proc connect {hostname port {usetls 0}} { + if $usetls { + if {[info commands ::tls::socket] == ""} { package require tls } + set chan [::tls::socket $hostname $port] + set proto ircs + } else { + set chan [socket $hostname $port] + set proto irc + } + + irc::enroll $chan [dict create uri $proto://$hostname:$port \ + proto $proto \ + hostname $hostname \ + port $port] + + return $chan + } + + #*** + # [subsection [concat [cmd irc::enroll] [arg chan] [opt [arg meta]]]] + # + # Sets up the internal state necessary for chan to be used as an IRC socket. + # It is called internally by [cmd irc::connect]. + # This command is exposed for the use-case where an IRC channel might have a more bespoke acquisition process than a simple socket connection. + # + # [para] + # [arg meta] is the initial state of the [sectref {channel metadata}]. + proc enroll {chan {meta {}}} { + variable chan.meta + variable chan.handlers + variable chan.interceptors + fconfigure $chan -translation crlf -blocking 0a + set chan.meta($chan) $meta + set chan.handlers($chan) {} + set chan.interceptors($chan) {} + } + + + #*** + # [section {Listening to it}] + # [file irc.tcl] provides an event dispatch system, via a [cmd fileevent] script registered on the IRC channel. + # Events are dispatched by matching their patterns against incoming messages. + + #*** + # [subsection [concat [cmd irc::listen] [arg subcommand] [arg chan]]] + # Enable or disable the [cmd fileevent] script for the dispatch system. + # + # [list_begin definitions] + proc listen {subcommand chan} { + switch -- $subcommand { + on { + #*** + # [call [cmd irc::listen] [const on] [arg chan]] + # Apply the [cmd fileevent] wrapper to [arg chan]. + # Returns the previous [cmd fileevent] wrapper. + fileevent $chan readable [list ::irc::int-onmsg $chan] + } + off { + #*** + # [call [cmd irc::listen] [const off] [arg chan]] + # Remove the [cmd fileevent] wrapper from [arg chan]. + # Errors if the channel does not currently have the irc handler set. + set oldfe [fileevent $chan readable] + if {[fileevent $chan readable] != [list ::irc::int-onmsg $chan]} { + return -code error "channel \"$chan\" not listening for irc" + } else { fileevent $chan readable "" } + } + default { return -code error "unknown subcommand \"$subcommand\": must be off or on" } + } + } + #*** + # [list_end] + + #*** + # [subsection [concat [cmd irc::listener] [arg subcommand] [arg chan] [opt [arg arg]...]]] + # Configure [cmd listener]-type event handlers. + # [para] + # [cmd listener]-type handlers are scripts, executed in either a sub-interpreter or a seperate thread. + # Interpreter listeners should yield themselves with [cmd after] during long operations, and never block for extended periods. + # If that is infeasible, threaded listeners are recommended. + # [para] + # [cmd listener]-type scripts are spawned with the variable [var dispatch] set to the channel they are to recieve dispatches over. + # Each line will be a [cmd dict] with contents as described in [sectref {Event Dispatch Contents}]. + # Interpreters are given access to the [sectref {Dispatch-aliased IRC commands}], while threads are given the variable [var parent], the ID of the main thread. + # [para] + # When a listener is removed, it will recieve a message of just [const end]. + # It should perform necessary cleanup quickly, as the application is likely exiting. + # Threads may simply [cmd thread::release] themselves, while interps may call the provided [cmd selfdestruct]. + # + # [list_begin definitions] + proc listener {subcommand chan args} { + variable chan.handlers + switch -- $subcommand { + add { + #*** + # [call [cmd irc::listener] [const add] [arg chan] [opt [option -thread]] [arg patlist] [arg script]] + # Registers [arg script] as a [cmd listener]-type handler on [arg chan], using [arg patlist] as the [sectref {Message Pattern List}]. + # Returns an id that can be passed to [cmd irc::listener] [const remove] or [cmd irc::patlist]. + # [para] + # If [option -thread] is passed, it will be created as a threaded listener, otherwise it will be created in a sub-interpreter. + set thread false + if {[lindex $args 0] == "-thread"} { + set thread true + set args [lrange $args 1 end] + } + + if {[llength $args] != 2} { return -code error "wrong # args: should be \"irc::listener add chan ?-thread? patlist script\"" } + lassign $args patlist script + set id [format "%016x" [expr {round(rand() * (2**64))}]] + + if !$thread { + set interp [interp create] + irc::int-setaliases $interp + interp share {} $chan $interp + + lassign [chan pipe] reader writer + interp transfer {} $reader $interp + $interp alias selfdestruct ::irc::int-rminterp $interp + $interp eval [list set dispatch $reader] + $interp eval [list after idle $script] + + lappend chan.handlers($chan) [list $patlist chan $id $writer $interp] + } else { + set thread [thread::create -preserved] + lassign [chan pipe] reader writer + + thread::transfer $thread $reader + thread::send -async $thread [list set dispatch $reader] + thread::send -async $thread [list set parent [thread::id]] + thread::send -async $thread $script + + lappend chan.handlers($chan) [list $patlist tchan $id $writer $thread] + } + + return $id + } + remove { + #*** + # [call [cmd irc::listener] [const remove] [arg chan] [arg id]] + # Unregisters the listener identified by [arg id] from [arg chan]. + # [para] + # Ignores requests for nonexistent handlers or handlers of the wrong type. + if {[llength $args] != 1} { return -code error "wrong # args: should be \"irc::listener remove chan id\"" } + lassign $args rmid + set newlist "" + foreach handler [set chan.handlers($chan)] { + lassign $handler _ type handlerid writer iot + if {$handlerid != $rmid || $type ni {chan tchan}} { + lappend newlist $handler + } elseif {$type == "chan"} { + puts $writer end + flush $writer + } elseif {$type == "tchan"} { + puts $writer end + flush $writer + thread::release $iot + } + } + set chan.handlers($chan) $newlist + } + default { return -code error "unknown subcommand \"$subcommand\": must be add or remove" } + } + } + #*** + # [list_end] + + #*** + # [subsection [concat [cmd irc::handler] [arg subcommand] [arg chan] [opt [arg arg]...]]] + # + proc handler {subcommand chan args} { + variable chan.handlers + switch -- $subcommand { + add { + set thread false + if {[lindex $args 0] == "-thread"} { + set thread true + set args [lrange $args 1 end] + } + + if {[llength $args] ni {2 3}} { return -code error "wrong # args: should be \"irc::handlers add chan ?-thread? patlist script ?interp-or-thread?\"" } + set iot [lassign $args patlist script] + set id [format "%016x" [expr {round(rand() * (2**64))}]] + + if !$thread { + if [llength $iot] { + irc::int-setaliases {*}$iot + interp share {} $chan {*}$iot + } + + lappend chan.handlers($chan) [list $patlist script $id $script {*}$iot] + } else { + if ![llength $iot] { + set iot [list [thread::create -preserved]] + } else { + thread::preserve {*}$iot + } + + thread::send -async $iot [list set parent [thread::id]] + + lappend chan.handlers($chan) [list $patlist tscript $id $script {*}$iot] + } + + return $id + } + remove { + if {[llength args] != 1} { return -code error "wrong # args: should be \"irc::listener remove chan id\"" } + lassign $args rmid + set newlist "" + foreach handler [set chan.handlers($chan)] { + set iot [lassign $handler _ type handlerid _] + if {$handlerid != $rmid || $type ni {script tscript}} { + lappend newlist $handler + } elseif {$type == "script" && [llength $iot]} { + interp delete {*}$iot + } elseif {$type == "tscript"} { + thread::release {*}$iot + } + } + set chan.handlers($chan) $newlist + } + default { return -code error "unknown subcommand \"$subcommand\": must be add or remove" } + } + } + + + #*** + # [subsection [concat [cmd irc::is] [arg type] [arg value]]] + # Validation helper. + # + # [para] + # + proc is {type value} { # validation helper. # cap is a list of negotiated capabilities. switch -- $type { @@ -111,52 +357,6 @@ namespace eval ::irc { } } - # documented - proc connect {hostname port {usetls 0}} { - if $usetls { - if {[info commands ::tls::socket] == ""} { package require tls } - set chan [::tls::socket $hostname $port] - set proto ircs - } else { - set chan [socket $hostname $port] - set proto irc - } - - irc::enroll $chan [dict create uri $proto://$hostname:$port \ - proto $proto \ - hostname $hostname \ - port $port] - - return $chan - } - - # documented - proc enroll {chan {meta {}}} { - variable chan.meta - variable chan.handlers - variable chan.interceptors - fconfigure $chan -translation crlf -blocking 0 - set chan.meta($chan) $meta - set chan.handlers($chan) {} - set chan.interceptors($chan) {} - } - - # documented - proc listen {subcommand chan} { - switch -- $subcommand { - on { - fileevent $chan readable [list ::irc::int-onmsg $chan] - } - off { - set oldfe [fileevent $chan readable] - if {[fileevent $chan readable] != [list ::irc::int-onmsg $chan]} { - return -code error "channel \"$chan\" not listening for irc" - } else { fileevent $chan readable "" } - } - default { return -code error "unknown subcommand \"$subcommand\": must be off or on" } - } - } - # nodoc # helper function that rebrands dict command errors proc int-dictsub args { @@ -322,125 +522,6 @@ namespace eval ::irc { proc int-rminterp {interp} { interp delete $interp } - # documented - proc listener {subcommand chan args} { - variable chan.handlers - switch -- $subcommand { - add { - set thread false - if {[lindex $args 0] == "-thread"} { - set thread true - set args [lrange $args 1 end] - } - - if {[llength $args] != 2} { return -code error "wrong # args: should be \"irc::listener add chan ?-thread? patlist script\"" } - lassign $args patlist script - set id [format "%016x" [expr {round(rand() * (2**64))}]] - - if !$thread { - set interp [interp create] - irc::int-setaliases $interp - interp share {} $chan $interp - - lassign [chan pipe] reader writer - interp transfer {} $reader $interp - $interp alias selfdestruct ::irc::int-rminterp $interp - $interp eval [list set dispatch $reader] - $interp eval [list after idle $script] - - lappend chan.handlers($chan) [list $patlist chan $id $writer $interp] - } else { - set thread [thread::create -preserved] - lassign [chan pipe] reader writer - - thread::transfer $thread $reader - thread::send -async $thread [list set dispatch $reader] - thread::send -async $thread [list set parent [thread::id]] - thread::send -async $thread $script - - lappend chan.handlers($chan) [list $patlist tchan $id $writer $thread] - } - - return $id - } - remove { - if {[llength $args] != 1} { return -code error "wrong # args: should be \"irc::listener remove chan id\"" } - lassign $args rmid - set newlist "" - foreach handler [set chan.handlers($chan)] { - lassign $handler _ type handlerid writer iot - if {$handlerid != $rmid || $type ni {chan tchan}} { - lappend newlist $handler - } elseif {$type == "chan"} { - puts $writer end - flush $writer - } elseif {$type == "tchan"} { - puts $writer end - flush $writer - thread::release $iot - } - } - set chan.handlers($chan) $newlist - } - default { return -code error "unknown subcommand \"$subcommand\": must be add or remove" } - } - } - - # documented - proc handler {subcommand chan args} { - variable chan.handlers - switch -- $subcommand { - add { - set thread false - if {[lindex $args 0] == "-thread"} { - set thread true - set args [lrange $args 1 end] - } - - if {[llength $args] ni {2 3}} { return -code error "wrong # args: should be \"irc::handlers add chan ?-thread? patlist script ?interp-or-thread?\"" } - set iot [lassign $args patlist script] - set id [format "%016x" [expr {round(rand() * (2**64))}]] - - if !$thread { - if [llength $iot] { - irc::int-setaliases {*}$iot - interp share {} $chan {*}$iot - } - - lappend chan.handlers($chan) [list $patlist script $id $script {*}$iot] - } else { - if ![llength $iot] { - set iot [list [thread::create -preserved]] - } else { - thread::preserve {*}$iot - } - - thread::send -async $iot [list set parent [thread::id]] - - lappend chan.handlers($chan) [list $patlist tscript $id $script {*}$iot] - } - - return $id - } - remove { - if {[llength args] != 1} { return -code error "wrong # args: should be \"irc::listener remove chan id\"" } - lassign $args rmid - set newlist "" - foreach handler [set chan.handlers($chan)] { - set iot [lassign $handler _ type handlerid _] - if {$handlerid != $rmid || $type ni {script tscript}} { - lappend newlist $handler - } elseif {$type == "script" && [llength $iot]} { - interp delete {*}$iot - } elseif {$type == "tscript"} { - thread::release {*}$iot - } - } - set chan.handlers($chan) $newlist - } - default { return -code error "unknown subcommand \"$subcommand\": must be add or remove" } - } - } # nodoc proc int-onextern {ichan chan} { diff --git a/src/main.tcl b/src/main.tcl index e53e2e5..1278b95 100755 --- a/src/main.tcl +++ b/src/main.tcl @@ -4,6 +4,11 @@ # [manpage_begin main_thread tclircc 0.0.1] # [titledesc {Thread main}] # [description] +# [para] +# This is the application entrypoint. It does the following. +# [list_begin itemized] + + set path [file dirname [dict get [info frame 0] file]] set version v0.0.1 @@ -21,6 +26,9 @@ package require sqlite3 ${log}::info "tclircc $version " ${log}::info "running from $path" +#*** +# [item] +# brings up the routing system src router.tcl proc on_routes_update {} { @@ -48,6 +56,9 @@ proc start_thread {name} { ${log}::debug "started $name thread." } +#*** +# [item] +# brings up the database start_thread db thread::send [r::ns tclircc::db] {path_to_core} core_db_path @@ -56,11 +67,17 @@ src migrate_core.tcl src plugins.tcl +#*** +# [item] +# loads the other core threads start_thread irc start_thread ui plugins::load [file join $path .. testplugin] +#*** +# [item] +# opens a window ${log}::debug "opening initial window..." r::exec tclircc::ui { mk_toplevel name @@ -73,4 +90,5 @@ ${log}::info "entering event loop" vwait nil #*** +# [list_end] # [manpage_end] -- 2.45.2