Apache Rivet and DIO

Apache Rivet 提供了 DIO 套件存取資料庫。而 DIO::Postgresql 是以 Pgtcl 作為底層的套件。

我先在 PostgreSQL 建立一個表格 Notes 作為測試:
create table if not exists Notes (id uuid DEFAULT md5(random()::text || clock_timestamp()::text)::uuid, 
title varchar(255), body text, created timestamp, PRIMARY KEY (id));

接下來撰寫一個簡單的程式測試 Apache Rivet。

建立檔案 tdbcnoteservices.tcl,將資料庫的實作部份放在這裡(使用 tdbc::postgres):
package require tdbc::postgres

proc getAllNotes {} {
    set rows {}

    tdbc::postgres::connection create db -user danilo -password danilo -port 5432
    set stmt [db prepare {select * from Notes order by created}]
    $stmt execute
    set rows [$stmt allrows -as lists]

    $stmt close
    db close

    return $rows

proc getNote {id} {
    set myparams [dict create id $id]

    tdbc::postgres::connection create db -user danilo -password danilo -port 5432
    set stmt [db prepare {select * from Notes where id = :id}]
    $stmt execute $myparams
    set rows [$stmt allrows -as lists]
    $stmt close
    db close

    return $rows    

proc addNote {title body} {
    set myparams [dict create title $title body $body]
    tdbc::postgres::connection create db -user danilo -password danilo -port 5432
    set stmt [db prepare {INSERT INTO Notes (Title, Body, Created) values (:title, :body, now())}]
    set resultset [$stmt execute $myparams]
    set rowcount [$resultset rowcount]

    $resultset close
    $stmt close
    db close

    return $rowcount

proc updateNote {id title body} {
    set myparams [dict create id $id title $title body $body]
    tdbc::postgres::connection create db -user danilo -password danilo -port 5432
    set stmt [db prepare {UPDATE Notes SET Title = :title, Body = :body where Id = :id}]
    set resultset [$stmt execute $myparams]
    set rowcount [$resultset rowcount]

    $resultset close
    $stmt close
    db close

    return $rowcount

proc deleteNote {id} {
    set myparams [dict create id $id]
    tdbc::postgres::connection create db -user danilo -password danilo -port 5432
    set stmt [db prepare {DELETE FROM Notes where Id = :id}]
    set resultset [$stmt execute $myparams]
    set rowcount [$resultset rowcount]

    $resultset close
    $stmt close
    db close

    return $rowcount

再來就是測試我比較不熟悉的 DIO 套件,建立 dionoteservices.tcl 檔案,然後實作同樣的功能:
package require DIO

proc getAllNotes {} {
    set rows {}
    set db [::DIO::handle Postgresql -host localhost -port 5432 -user danilo -pass danilo -db danilo]

    set rows [list]
    $db forall {select id, title, body, created from Notes order by created} row {
        set myrow [list $row(id) $row(title) $row(body) $row(created)]    
        lappend rows $myrow

    $db destroy
    return $rows

proc getNote {id} {
    set rows {}    
    set db [::DIO::handle Postgresql -host localhost -port 5432 -user danilo -pass danilo -db danilo]

    set query "select id, title, body, created from Notes where id = '$id'"
    set rows [list]
    $db forall $query row {
        set myrow [list $row(id) $row(title) $row(body) $row(created)]
        lappend rows $myrow

    $db destroy
    return $rows    

proc addNote {title body} {
    set db [::DIO::handle Postgresql -host localhost -port 5432 -user danilo -pass danilo -db danilo]

    set arrayVar(Title) $title
    set arrayVar(Body) $body
    set arrayVar(Created) "now()"
    set rowcount [$db insert Notes arrayVar]

    $db destroy
    return $rowcount

proc updateNote {id title body} {
    set db [::DIO::handle Postgresql -host localhost -port 5432 -user danilo -pass danilo -db danilo]

    set arrayVar(Id) $id
    set arrayVar(Title) $title
    set arrayVar(Body) $body
    set rowcount [$db update arrayVar -table Notes -keyfield Id]

    $db destroy
    return $rowcount

proc deleteNote {id} {
    set db [::DIO::handle Postgresql -host localhost -port 5432 -user danilo -pass danilo -db danilo]

    set rowcount [$db delete $id -table Notes -keyfield Id]

    $db destroy    
    return $rowcount

這樣只要 source 的檔案不同,就可以測試不同的資料庫實作部份。


tclws 2.6.2

ANNOUNCE: tclws 2.6.2 released

Web Services for Tcl provides both client side access to Web Services and server side creation of Web Services. Currently only document/literal and rpc/encoded with HTTP Soap transport are supported on the client side.

tclws 這次只有 client 端的修正。我也更新了我自己的 openSUSE RPM spec


Tcl: argv


$argv is a global variable provided by tclsh and wish mainline code.

下面就是 Tcler's wiki 的範例(我只有改寫第一行):
#!/usr/bin/env tclsh

if { $::argc > 0 } {
    set i 1
    foreach arg $::argv {
        puts "argument $i is $arg"
        incr i
} else {
    puts "no command line argument passed"


HTTP/3 explained

HTTP/3 explained
HTTP/3 協定出爐
The Road to QUIC

可以開始閱讀 HTTP/3 相關的文件。

From https://http3-explained.haxx.se/images/quic-stack.png

HTTP/3 主要在 UDP 上建立一個新的協定,QUIC (Transfer protocol over UDP)。

Apache Rivet 3.1.0 released

Apache Rivet 3.1.0 released

Apache Rivet is a module providing Tcl scripting capability with the Apache HTTP Web Server. With Apache Rivet you can run pure Tcl scripts or parse Rivet templates. Rivet templates allow Tcl code to be embedded in HTML pages, in pretty the same way you can do with PHP. Rivet extends Tcl with several commands providing functionalities needed to control script execution within the Apache web server or other tasks common to the web programming. Rivet comes with a library of Tcl packages providing session management, HTML form output and validation, DBMS abstraction and more

Release Notes:

Rivet 3.1.0 is a bug fix release of Rivet 3.0.3. The decision of releasing a new point release has been suggested by the nature of the fix that, even though unlikely, in principle might break existing code developed on wrong assumptions about the behavior of commands ::rivet::var_ps and ::rivet::var_post

Web site: http://tcl.apache.org/
Distribution: http://www.apache.org/dyn/closer.lua/tcl/rivet


Apache Rivet 3.0.3 released

Rivet 3.0.3 is a bug fix release of rivet 3.0. This release includes
  • Packages in DIO are not assuming anymore the rivet command set had been imported into the global namespace
  • A minor change to ::rivet::xml provides a new form to generate self closing elements
如果要看 3.0 系列的 Release Notes,可以看這個網頁。如果要下載的話,可以去 download page。 


Tcl/Expect: Auto login to PTT BBS (using ssh) on Linux

使用 expect 套件:
#!/usr/bin/env tclsh

# A script to login ptt BBS
# tclsh -encoding big5 login.tcl host user username password
package require Expect

set timeout 60

encoding system big5
spawn ssh [lindex $argv 1]@[lindex $argv 0]

expect "請輸入代號,或以 guest 參觀,或以 new 註冊:" {
    exp_send "[binary decode base64 [lindex $argv 2]]\r"
    expect "請輸入您的密碼:" { exp_send "[binary decode base64 [lindex $argv 3]]\r" }


我只是將 username/password 使用 base64 編碼,所以這裡需要解回來。然後這個 script 需要使用 BIG5 編碼存檔(執行時也需要使用 -encoding 設定編碼)。

然後寫一個 script 呼叫這個 tcl script,下面是一個例子:
#!/usr/bin/env bash

tclsh -encoding big5 ~/bin/login.tcl ptt.cc bbs dXNlcg== cGFzc3dk

再來將這個 shell script 設為可執行,就可以執行以後自動登入 PTT BBS。


Print PATH Entries (no duplicate)

#!/usr/bin/env tclsh

set path [lsort -unique [split $::env(PATH) ":"]]

foreach p $path {
    puts $p

這只是一個簡單的練習。在取得目前的 PATH 環境變數列表以後使用 lsort 排序並且將重覆的部份移除 (-unique option),然後印出來。


Tcl/Tk 8.6.9 Release Candidates

Tcl/Tk 8.6.9 Release Candidates

This collection includes release candidates of packages

     Itcl    4.1.2
     Thread    2.8.4
     sqlite3    3.25.2
     tdbc*    1.1.0 

所以 Tcl/Tk 釋出了 8.6.9 Release Candidates,如果沒有大問題的話,應該 2018/11/09 就會釋出 8.6.9 正式版本。

釋出新的 Release Candidate
SQLite3 更新到  3.25.3
如果沒什麼大問題,預期在 2018/11/16 釋出 8.6.9 正式版本。

Tcl/Tk 8.6.9 RELEASED


NaviServer 4.99.17

NaviServer 釋出新的版本,.4.99.17。

因為 NaviServer 檔案已經放上去 SourceForge,所以我更新了我自己的 naviserver-spec,準備開始測試是否可以在 openSUSE 正確編譯。

下面是 NaviServer 4.99.17 NEWS 的內容:

New Features:

  - Added brotli support for delivering static content
    The brotli compression format (RFC 7932) achieves better results for
    compression for typical web content compared to e.g. gzip and is
    supported by most current browsers. The definition of static
    content delivery is exactly the same for "brotli" as for static
    "gzip" content.

    New configuration options for ns/fastpath:
    * "brotli_static": should static brotli delivery be checked?
    * "brotli_refresh": should outdated brotli compressed files be refreshed?
    * "brotli_cmd": OS-level command with options used for brotli compression

  - "ns_conn" additions:
    * New subcommand "ns_conn acceptedcompression" to return accepted
      compressions for the current connection (gzip or brotli)

    * New subcommands "ns_conn currentaddr" and "ns_conn currentport"
      to refer to the address and port of the currently open server side
      of the socket.

  - New feature for GDPR: add mask IP addresses optionally in access log
    This feature is similar to "anonip" IP anonymizer of the Swiss
    privacy foundation): When this feature is activated, all IP
    addresses are masked in the log file, such that the host-specific
    (= person- specific) information is masked out and the IP-address
    does not match a particular individual anymore. Still, with the
    masking turned on, reverse DNS lookup and/or geolocation is

    The option can be configured via the following parameters in the
    ".../module/nslog" section of the config file of NaviServer:
     * "masklogaddr": boolean value to turn feature on/off (default off)
     * "maskipv4": mask for IPv4 addresses (default
     * "maskipv6": mask for IPv6 addresses (default ff:ff:ff:ff::)

  - New atomic "nsv_set" commands/options

    * Obtain (old) value from nsv ARRAY and set it to a new value set foo

        set foo [nsv_set -reset ARRAY KEY NEWVALUE]

      After the operation, ARRAY(KEY) has a new value.
      The operation is similar to "nsv_set ARRAY KEY NEWVALUE",
      but the variant with "-reset" returns the old value.
      Similar to GETSET in REDIS.

   * Obtain a value from nsv ARRAY and unset it (no new value is provided).

        set foo [nsv_set -reset ARRAY KEY]

     The operations returns the old value of ARRAY(KEY) or empty, if
     it does not exist.  After the operation, the ARRAY(KEY) is unset.

   * Set a default value for a nsv ARRAY

        nsv_set -default ARRAY KEY DEFAULTVALUE

      Do nothing when ARRAY(KEY) has already a value.
      Return an error, when DEFAULTVALUE is not provided.
      Similar to SETNX in REDIS.

  - Extended functionality of "ns_http"

    * Changed return value for "ns_http run" and "ns_http wait".
      Now the commands return attribute value pairs (Tcl dict).
      Previously, the commands returned different results,
      depending on the options

      - "ns_http run" returned the name of the queued command (which
        was completely useless, this the queued command was already
      - "ns_http wait" returned sometimes the body of the reply (when
        the reply was not spooled) or empty (spooled) or "1", when the
        option "-R" was specified

      Now the result is always a dict containing "status" (HTTP status
      code), "time" (elapsed time), "headers" (ns_set of the reply
      header fields, and either the "body" (reply body) or "file" (the
      name of the spooled file). The values in the dict might mirror
      the result of other (optional) output variables, which continue
      to work.

      Since the results were irregular and not documented in the man
      pages, the backward compatibility should be high. Also, the
      regression test of NaviServer was not effected by this change.

    * Support for asynchronous “ns_http” tasks:

      When the new flag "-donecallback” is provided, then a “ns_http”
      task is started in the background and does not require an
      “ns_http wait” anymore. Instead, when it finishes it executes
      the provided donecallback which receives as additional arguments
      the Tcl result (in form of an integer) and the result dict,
      returned otherwise from e.g. “ns_http run”.

      As a consequence, requests of the form
         ns_http queue … -donecallback …
      differ from client requests without the callback
      in the following points:

      a) the command does not return a handle for “ns_http wait”
      b) The command is not automatically cleaned up (canceled)
         at the end of a connection requests
      c) Typically, donecallbacks are executed in a different
         thread than it was started.

      Some more options, which were previously only valid on
      “ns_http wait” (such as e.g. spoolsize) are now valid
      for “ns_http queue” as well.

      The new flag allows a developer to start many requests in
      parallel without having to start many threads or complex
      synchronization tasks.

    * New option "-body_file" for "ns_http wait" (similar to "ns_http
      queue" which can be used to specify a filename for the
      downloaded content)

    * New option "-outputfile" to "ns_http run" and "ns_http wait":
      by specifying the option "-outputfile" the received file will be
      always written to the disk, no matter how large it is.

    * New subcommand "ns_http stats": returns a dict (flat list of
      attributes and values). The list contains "task", "url",
      "requestlength", "sent", "replylength" and "received".

  - Support resolving a hostname against multiple IP addresses

    * In case, one domain name has multiple IP addresses registered,
      previous versions of NaviServer tried only the first returned
      address and returned an error, when this failed. This happened
      when using client functions (e.g. ns_http).

    * host names with multiple IP addresses are nowadays quite common,
      e.g. "localhost" having an IPv4 and an IPv6 address registered. In
      previous versions, when e.g. localhost has as first address the IPv6
      address, but IPv6 was not active, it returned an error.

    * resolving against IP addresses continues to work as always.

  - Extended crypto functionality

     * Added HMAC-based Extract-and-Expand Key Derivation Function (HKDF)
          ::ns_crypto::md hkdf -digest sha256 ....
       (requires OpenSSL 1.1.0 or newer)

     * Added support for elliptic curve cryptography
        . ns_crypto::eckey import ?-string string? ?-encoding encoding?
        . ns_crypto::eckey priv ?-pem pem? ?-encoding encoding?
        . ns_crypto::eckey pub ?-pem pem? ?-encoding encoding?
        . ns_crypto::eckey sharedsecret ?-pem pem? ?-encoding encoding? pubkey
        . ns_crypto::eckey generate ?-name name? ?-pem pem?

     * Added support for getting an arbitrary number of random bytes
       based on crypto support on OpenSSL:
        . ns_crypto::randombytes ?-encoding encoding? bytes

     * Added support for Authenticated Encryption with Associated Data
       (AEAD) scheme, which provides confidentiality, integrity, and authenticity.

        . ns_crypto::aead::encrypt string ?-aad aad? ?-cipher cipher? ?-encoding encoding? ?-iv iv? ?-key key? input"
        . ns_crypto::aead::decrypt string ?-aad aad? ?-cipher cipher? ?-encoding encoding? ?-iv iv? ?-key key? ?-tag tag? input"

     * Added "-encoding hex|base64url|base64" option for output encoding
        for the crypto functions. Previously, all these functions returned the
        result in "hex" format. If the option is not specified, the result
        is the same as before (hex). The encoding option was added to the
        following functions:
        - ns_crypto::hmac string
        - ns_crypto::hmac get
        - ns_crypto::md string
        - ns_crypto::md get
        - ns_crypto::md vapidsign
        - ns_crypto::enc

  - "ns_connchan" improvements

    * Callback suspension: a Tcl socket callback function might now
      return the value "2" to flag that the callback will be
      suspended, but it keeps the socket open. Previously, the Tcl
      callback could return just "0" (callback is cancelled, socket is
      closed) or "1" (continue callback handling, keep socket open).
      This can be used to suspend callback handling on bottlenecks and
      continue later, when situation improves.

    * "ns_connchan write": The function returns now the number of
      bytes sent, which might be less than the length of the data to
      be sent.

    * "ns_connchan list": the function returns now two more fields per
      entry: a) cmd name of the callback (eases debugging, when
      callbacks are changed dynamically) b) conditions used for
      registering the callback.

    * The "ns_connchan" callbacks are now listed via the nsstats
      interface as well (with proper callback info).

  - Added Possibility to add per-server "initcmds" to config file

    This new feature provides an easy means to add command which
    should be executed after server initialization into a configuration
    files. By using the "initcmds" one can start multiple instances
    of NaviServer differing just in the initialization command
    by providing different config files. The "initcmds" approach
    has the advantage over e.g. "ns_atserverstart" that it is
    executed in a state when the server is fully initialized.

  - Improved readability for config files

    The "ns_section" command has new an optional last argument for the
    parameters of its configuration section. Instead of writing e.g.

       ns_section ns/parameters
       ns_param serverlog    $logroot/error.log
       ns_param pidfile      $logroot/nsd.pid
       ns_param home         $homedir

    one can write now

       ns_section ns/parameters {
           ns_param serverlog   $logroot/error.log
           ns_param pidfile     $logroot/nsd.pid
           ns_param home        $homedir

    This block notation has two advantages: a) it shows clearly, where
    the section ends and b) when using an editor with automatic
    indentation, the log file looks nicer without manual indentation
    work. This change is fully backward compatible, old style config
    files continue to work.

  - Further new commands and features

    * "ns_hash": provide a interface for the HashStringKey() function
      (very useful for e.g. cache partitioning)

    * "ns_reflow_text".
          ns_reflow_text ?-width width? ?-offset offset? ?-prefix prefix? ?--? text
      Tcl-based solutions are surprisingly slow on largish input.

    * "ns_base64urlendcode", "ns_base64urldedcode": URL-save variants of base64,
      used in various new RFCs for passing base64 values in HTTP requests.

   * "ns_uuid": runtime efficient version of a version 4 UUID
      according to RFC 4122: A Universally Unique IDentifier (UUID)
      URN Namespace

   * Improved results of [ns_cache_stats -contents ...]. The command
     returns now a tcl list instead of a string, including reuse count.

  - Improved results of "ns_striphtml":
     * Added resolving of all HTML4 entities (including   etc.)
     * Stripped as well HTML comments.

Performance Improvements:

  - Check for epoch updates on blue-prints as well in cleanup phase
    (deallocate trace) to reduce latency for client requests.

Bug Fixes:

  - Don't try to serve redirected files in case the connection is
    already closed (will lead only to confusing messages)

  - Ns_IsBinaryMimeType(): When a chartype is specified, never assume
    the mimetype is binary.

  - ns_getformfile: Don't rely on global variables
    (see https://sourceforge.net/p/naviserver/bugs/80/)
    Before this change, Depending on configured values,
    ns_getformfile could have missed uploaded files.

  - Added missing fetch commands for obtaining handles (on obviously
    seldom used subcommands "ns_db setexception|sp_setparam")

  - ns_http:
    * Timeout and connection behavior show works now consistent
      for HTTP and HTTPS URLs.
    * Fixed a potential bug (infinite loop) when requests
      run into timeouts.

  - "ns_set print": align with documented behavior

  - "ns_parseurl": make results more robust, when a URL with a colon
    in the path was given, but no port provided (detected a port

  - "ns_parseformfile": handle also www-form-urlencoded (in
     addition to multipart/form-data as before)

  - Windows:
    * Make sure to create temporary files correctly under windows (many
      thanks to Oleg Oleinik)
    * Incorrect handle was used to perform operations on the
      process in Ns_WaitForProcess (many thanks to Oleg Oleinik)

Documentation improvements:

  - Updated several man pages
    * admin-db.man:
    * admin-install.man
    * admin-maintenance.man
    * adp-overview.man
    * commandlist.man
    * main-features.man
    * main-history.man
    * ns_accesslog.man
    * ns_adp.man
    * ns_adp_argc.man
    * ns_adp_argv.man
    * ns_adp_bind_args.man
    * ns_adp_eval.man
    * ns_adp_flush.man
    * ns_adp_info.man
    * ns_adp_safeeval.man
    * ns_atexit.man
    * ns_atexit.man
    * ns_atshutdown.man
    * ns_atsignal.man
    * ns_base64.man
    * ns_base64encode.man
    * ns_conn.man
    * ns_connchan.man
    * ns_cookie.man
    * ns_db.man
    * ns_driver.man
    * ns_fmttime.man
    * ns_getcontent.man
    * ns_getform.man
    * ns_hmac.man
    * ns_hotp.man
    * ns_http.man
    * ns_httpget.man
    * ns_httpopen.man
    * ns_ictl.man
    * ns_job.man
    * ns_kill.man
    * ns_md.man
    * ns_memoize.man
    * ns_mutex.man
    * ns_parsequery.man
    * ns_perm.man
    * ns_proxy.man
    * ns_queryexists.man
    * ns_queryget.man
    * ns_querygetall.man
    * ns_quotehtml.man
    * ns_rand.man
    * ns_register.man
    * ns_return.man
    * ns_rlimit.man
    * ns_schedule_proc.man
    * ns_sendmail.man
    * ns_server.man
    * ns_set.man
    * ns_set.man:
    * ns_setexpires.man
    * ns_sockopen.man
    * ns_sockselect.man
    * ns_striphtml.man
    * ns_thread.man
    * ns_time.man
    * ns_tmpnam.man
    * ns_totp.man
    * ns_urlspace.man
    * ns_uuencode.man
    * ns_writer.man
    * nscgi.man
    * nsperm.man
    * nssock.man
    * nsv.man
    * returnstatus-cmds.man
    * tcl-lib-file.man
    * tcl-lib-nstrace.man
    * tcl-libraries.man
    * tcl-overview.man

Tcl API Changes:

  - "ns_conn isconnected": returns now false, when the connection is
     already closed. Otherwise we have to add a new subcommand
     "ns_conn closed", which seems somewhat odd (isconnected true and
     closed true?)

  - "ns_cache_stats" returns now hitrate in form of a float instead of
    an integer.

C API Changes:

   - Ns_SockTimedConnect2() returns now Ns_ReturnCode to be able to
     distinguish between timeouts and errors

   - Ns_SockConnectError(): new call to provide consistent error messages
     and to set Tcl error info consistently

Configuration Changes:

  - Use same initialization for "enablelclpages" when configured over
    "ns/server/$server" or over "ns/server/$server/adp" to avoid subtle
    differences on global variables.

  - Extended sample config files:

    * nsd-config.tcl
       . Added parameter "masklogaddr" for "nslog" section
       . Added parameter for "gzip" and "brotli" static delivery
       . Added parameter for "initcmds"
       . Added setting for recommended environment variables
         HOME, LANG and RANDFILE. RANDFILE is sometimes needed 
          to avoid surprises with permissions, when OpenSSL 
          crypto functions are called from the server

    * openacs-config.tcl
       . Switched to PostgreSQL's conninfo notation in the datasource
         to hint that more such parameters are possible as well
         (e.g. connections via SSL)
       . Added comments and examples for configuring OpenACS caches
       . Added comments how to configure EmailDeliveryMode via config file
       . Added comments how to configure IncludeCallingInfo of OpenACS API browser
       . Added comments how to configure WithDeprecatedCode
       . Added example for activating more intense SQL logging
       . Added subsite-based error pages
       . Added section indicating how to configure for nsstats module
       . Added parameter settings for "reuseport"
       . Added parameter "masklogaddr" for "nslog" section
       . Added parameter for "brotli" static delivery
       . Preconfigure customized error pages based on subsites

  - Config files changed to block notation (see above)

  - Improved handling of configuration errors:
    * Provided better log message, when configured value is out of range
    * Updated config values when range check corrects the value to get the
      finally configured value via introspection.

Code Changes:

  - Extended regression test:
        * tests/adp.test
        * tests/compat.test
        * tests/http.test
        * tests/ns_base64.test
        * tests/ns_base64encode.test
        * tests/ns_cache.test
        * tests/ns_conn.test
        * tests/ns_crypto.test
        * tests/ns_nsv.test
        * tests/ns_parseurl.test
        * tests/ns_reflow_text.test
        * tests/ns_server.test
        * tests/ns_striphtml.test

 - Added version tag of NaviServer to "starting" message in log file
   to ease analysis in case of problems.

 - Implemented deprecated commands as Tcl proc and complain on
   its usage
      . ns_adp_eval
      . ns_adp_safeeval

  - Improved Platform support:
    * Extended list of platforms for manual provisioning of __PRIPTR_PREFIX
      in case it is not defined.
    * Do not rely on implementation defined print/scan format "%p", 
      but use instead standardized integer types.
    * Windows:
      . Improved type-cleanness (many thanks to Oleg Oleinik)
      . Support to compile with MSVC 2017 (many thanks to Oleg Oleinik)
      . When compiling with VS 2012, use built-in support for
        intptr_t and uintptr_t.
      . Added support to run regression test under Windows 
        (many thanks to Oleg Oleinik)

  - Changed preset encodings (when not specified in config file) for
    files with extensions .htm, .html, and .adp from iso8859-1 to

  - Improved debugging support:

    * Added compiler-flag NS_MUTEX_NAME_DEBUG for ease locating
      anonymous mutexes.
    * Provided names for all remaining mutexes to make it easier
      to pinpoint bottlenecks due to mutexes
    * Improve debugging output in various messages, when debugging
      options are activated.

  - Minor update of mime types based on actual IANA media types.

  - Provided compatibility with Tcl 8.7.2 (removed CONST and VOID macros)
  - Further improved robustness by avoiding potential NULL dereferences
    found be static code analyzers (mostly error cases)
  - Improved configure scripts
  - Make sure, we have enough space for the terminating null character
  - Added "pure" specifier for improved code generation

  - Various code smell removals
    * Prefer boolean type over int
    * Improved type cleanness
    * Reduced implicit conversions
    * Reduced number of return statements before end of function
    * Don't pass implementation-defined NULL after the last typed argument
      to a variadic function
    * Removed dead assignments
    * Added "const" declarations
    * Fixed typos, improved comments



tcl-caca: Tcl bindings for Colour ASCII Art library (libcaca)



tklib spec update

ANNOUNCE: Multi-entry widget package Mentry 3.8

因為 Mentry 更新,而發佈的文章說也有包含在 tklib 中,所以我更新自己的 tklib RPM spec 到目前的 code base,這樣理論上就會同時更新到最近的變更。


Tcl: Show platform/arch info


#!/usr/bin/env tclsh

puts "Platform: $tcl_platform(os)"
puts "Arch: $tcl_platform(machine)"

在 Ubuntu 14.04 64bit 上,答案是這樣:
Platform: Linux
Arch: x86_64


Tcl: {*}

{*} was new in Tcl 8.5, and resulted in the Endekalogue becoming the Dodekalogue.

{*} 可以說是 Tcl 語法上一個比較大的修改(也可以說是語法糖)。將 List 的每個 item 轉變為目前 command 的參數。這是 TIP 293  的提案。

set command [list set a 5]
set value [eval {*}$command]
puts $value

我最常用的是在使用 exec 時:
set command [list ls -al]
exec >@stdout 2>@stderr {*}$command



Next Scripting Framework 2.2.0

Changes to NSF in version 2.2.0
ANNOUNCE: Next Scripting Framework 2.2.0 is available

NSF 釋出了一個新的版本,2.2.0。可以在這裡下載。 所以我也更新了我自己的 RPM spec


SQLite3 and UUID


使用 SQLite3 產生一個亂數的值,然後拿到以後進行處理。因為 SQLite 支援 In-Memory Databases,我加上一點處理的 code,所以可以這樣用:
proc uuid {} {
    try {
        package require sqlite3
        sqlite3 db1 ":memory:"
        set u [db1 onecolumn {select (hex(randomblob(16)))}]
        db1 close
        return [string range $u 0 7]-[string range $u 8 11]-[string range $u 12 15]-[string range $u 16 19]-[string range $u 20 end]        
    } on error {em} {
        error $em

這樣就可以使用 SQLite Tcl interface 產生一個 UUID string。


Java 11 Released

Java 11 Released
JDK 11
Java Is Still Free
Do not fall into Oracle's Java 11 trap
The future of Java and OpenJDK updates without Oracle support

但是 Java 加快了開發的循環以後,對我而言,tclBlend 要使用哪一個 JDK 版本就變成一個麻煩的問題。

我已經在之前將我自己使用的 RPM spec 設為 java-11-openjdk-devel,而在下個 JDK 版本(也就是 12)來臨之前,應該有六個月的時間可以考慮。

另外一個考慮的重點是如果我升上 openSUSE LEAP 15.1,我就會以 openSUSE Leap 15.1 為主要的考量點。

另外,目前 OpenJDK 與 Oracle JDK 在技術上來說已經一致,所以接下來在六個月之後的長期更新版本負擔將會在 OpenJDK 上。但是如果六個月就要跳一次版本,這會是個麻煩的議題。

tklib/tablelist 更新


tablelist 釋出了 6.3 版,所以我更新了 tklib 與 tablelist 的 openSUSE RPM spec。


tcl-zstd 更新


因為 Zstandard 有更新內容,所以我也跟著更新了 RPM spec。在做的過程中,我出現了嚴重的錯誤,我居然誤殺了自己 openSUSE build service 的 Home Project,還好殺完以後 OBS 有做防呆機制,所以使用者如果嘗試建立一個新的,OBS 會提供選項回復 Home Project 的資料。這表示我最近的情況都不在狀態內,我想我可能需要休息一下。


2018/09/21 更新


tcl.mqttc v0.4


paho.mqtt.c 的檔案升到 v1.3.0。加入一個 option -version,用來指定 MQTT protocol 的版本(3.1, 3.1.1, 5 以及不指定的就使用預設值,目前確定 MQTT 3.1/3.1.1 是正確的,但是 MQTT 5 還沒有測試。

接下來等 MQTT 5 broker 的部份準備好再測試(如果那個時候我有時間的話),同時如果 paho.mqtt.c 的版本如果有更新,我再更新檔案。

使用 VerneMQ 測試 Websockets 的部份,確定可以使用,所以我更新了 README.md。

2018/09/18 更新
要支援 MQTT5 的連線需要加入更多的 code,我使用 VerneMQ 測試以後,基本的連線功能看起來 OK,所以我更新了 source code,同時將版本更新到 v0.5。


MonetDB and SHA224_Update

在 openSUSE 升版到 LEAP 15.0 以後,我發現編譯 MonetDB Mar2018 SP1 成功,但是 MonetDB mapi library 在 load library 使用時載入失敗。一開始出現的是 SHA224_Update undefined,這是 openSSL 的 function,所以我嘗試在 configure 刪除 SHA224_Update 的部份。(更新:按照 MonetDB 的更新記錄,Mar2018 SP1 和 Aug2018 都會有這問題)

然後就出現更奇怪的結果,結果會發生 mnstr_settimeout undefined 的問題。但是這是 MonetDB stream library 的 function,所以有可能 MonetDB 在 mapi library 連結時的設定有問題。

目前還沒有解法。所以 tclmonetdb 會載入 mapi library 失敗,導致無法使用。

使用 nm 檢查 libmapi.so 的情況:
nm -D libmapi.so.10.1.0
                 U block_stream
0000000000213329 B __bss_start
                 U calloc
                 U close
                 U closedir
                 U close_stream
                 U connect
                 U __ctype_b_loc
                 w __cxa_finalize
0000000000213329 D _edata
0000000000213360 B _end
                 U __errno_location
                 U exit
                 U fclose
                 U fflush
                 U fgets
000000000000f5ac T _fini
                 U fopen
                 U __fprintf_chk
                 U fputc
                 U fputs
                 U free
                 U freeaddrinfo
                 U fwrite
                 U gai_strerror
                 U getaddrinfo
                 U gettimeofday
                 U getuid
                 w __gmon_start__
00000000000030e8 T _init
                 U isa_block_stream
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
                 U malloc
0000000000007f10 T mapi_bind
00000000000080a0 T mapi_bind_numeric
0000000000007fc0 T mapi_bind_var
000000000000a8a0 T mapi_cache_freeup
0000000000008ea0 T mapi_cache_limit
000000000000a7a0 T mapi_cache_shuffle
0000000000008110 T mapi_clear_bindings
00000000000083e0 T mapi_clear_params
00000000000074b0 T mapi_close_handle
000000000000a6a0 T mapi_connect
0000000000007780 T mapi_destroy
0000000000007740 T mapi_disconnect
0000000000006d30 T mapi_error
0000000000006d40 T mapi_error_str
000000000000bf00 T mapi_execute
0000000000006d60 T mapi_explain
0000000000006e20 T mapi_explain_query
0000000000006f00 T mapi_explain_result
000000000000c420 T mapi_fetch_all_rows
000000000000c610 T mapi_fetch_field
000000000000c740 T mapi_fetch_field_len
000000000000ab20 T mapi_fetch_line
000000000000a840 T mapi_fetch_reset
000000000000c390 T mapi_fetch_row
000000000000ad00 T mapi_finish
000000000000d1a0 T mapi_get_active
00000000000070f0 T mapi_get_autocommit
000000000000d090 T mapi_get_dbname
000000000000cc80 T mapi_get_digits
000000000000c870 T mapi_get_field_count
0000000000007060 T mapi_get_from
000000000000d0b0 T mapi_get_host
000000000000d0d0 T mapi_get_lang
000000000000c990 T mapi_get_last_id
000000000000cbe0 T mapi_get_len
000000000000cfd0 T mapi_get_maloptimizertime
000000000000d0f0 T mapi_get_mapi_version
000000000000d100 T mapi_get_monet_version
000000000000d150 T mapi_get_motd
000000000000c9f0 T mapi_get_name
000000000000cdc0 T mapi_get_query
000000000000cf70 T mapi_get_querytime
000000000000ce10 T mapi_get_querytype
000000000000c930 T mapi_get_row_count
000000000000cd20 T mapi_get_scale
000000000000d030 T mapi_get_sqloptimizertime
000000000000cb40 T mapi_get_table
000000000000ce90 T mapi_get_tableid
0000000000007020 T mapi_get_to
00000000000070a0 T mapi_get_trace
000000000000ca90 T mapi_get_type
000000000000d0e0 T mapi_get_uri
000000000000d0c0 T mapi_get_user
000000000000d190 T mapi_is_connected
0000000000007140 T mapi_log
0000000000007d70 T mapi_mapi
00000000000079d0 T mapi_mapiuri
00000000000072e0 T mapi_more_results
00000000000072d0 T mapi_needmore
00000000000073c0 T mapi_new_handle
0000000000007230 T mapi_next_result
0000000000006d50 T mapi_noexplain
0000000000008360 T mapi_param
0000000000008370 T mapi_param_numeric
0000000000008280 T mapi_param_string
0000000000008180 T mapi_param_type
000000000000c010 T mapi_ping
0000000000008570 T mapi_prepare
00000000000084e0 T mapi_prepare_handle
000000000000bf60 T mapi_query
0000000000008d90 T mapi_query_done
000000000000c1d0 T mapi_query_handle
0000000000008b80 T mapi_query_part
0000000000008a90 T mapi_query_prep
000000000000ad70 T mapi_quote
0000000000008a80 T mapi_read_response
0000000000009010 T mapi_reconnect
00000000000086b0 T mapi_release_id
000000000000a6d0 T mapi_resolve
00000000000071e0 T mapi_result_error
0000000000007200 T mapi_result_errorcode
000000000000cf10 T mapi_rows_affected
000000000000a950 T mapi_seek_row
000000000000c130 T mapi_send
00000000000085e0 T mapi_setAutocommit
0000000000008650 T mapi_set_size_header
000000000000c360 T mapi_split_line
000000000000c290 T mapi_store_field
000000000000c090 T mapi_stream_query
0000000000008450 T mapi_timeout
0000000000008750 T mapi_trace
000000000000ad40 T mapi_unquote
0000000000008770 T mapi_virtual_result
000000000000ee20 T mcrypt_BackendSum
000000000000dd30 T mcrypt_getHashAlgorithms
000000000000ee30 T mcrypt_hashPassword
000000000000dd40 T mcrypt_MD5Sum
000000000000ec70 T mcrypt_RIPEMD160Sum
000000000000deb0 T mcrypt_SHA1Sum
000000000000e060 T mcrypt_SHA224Sum
000000000000e280 T mcrypt_SHA256Sum
000000000000e4e0 T mcrypt_SHA384Sum
000000000000e830 T mcrypt_SHA512Sum
                 U MD5_Final
                 U MD5_Init
                 U MD5_Update
                 U memmove
                 U memset
                 U mnstr_destroy
                 U mnstr_errnr
                 U mnstr_error
                 U mnstr_flush
                 U mnstr_init
                 U mnstr_printf
                 U mnstr_read
                 U mnstr_read_block
                 U mnstr_set_byteorder
                 U mnstr_settimeout
                 U mnstr_write
000000000000dbd0 T mo_add_option
000000000000d1b0 T mo_builtin_settings
000000000000d730 T mo_find_option
000000000000dcb0 T mo_free_options
000000000000d5f0 T mo_print_options
000000000000db30 T mo_system_config
                 U opendir
                 U open_wastream
                 U __printf_chk
                 U puts
                 U readdir
                 U realloc
                 U RIPEMD160_Final
                 U RIPEMD160_Init
                 U RIPEMD160_Update
                 U sendmsg
                 U SHA1_Final
                 U SHA1_Init
                 U SHA1_Update
                 U SHA224_Final
                 U SHA224_Init
                 U SHA224_Update
                 U SHA256_Final
                 U SHA256_Init
                 U SHA256_Update
                 U SHA384_Final
                 U SHA384_Init
                 U SHA384_Update
                 U SHA512_Final
                 U SHA512_Init
                 U SHA512_Update
                 U __snprintf_chk
                 U socket
                 U socket_rastream
                 U socket_wastream
                 U __sprintf_chk
                 U sscanf
                 U __stack_chk_fail
                 U stderr
                 U stdout
                 U strcat
                 U strchr
                 U strcmp
                 U strcpy
                 U __strdup
                 U strerror
                 U strlen
                 U strncmp
                 U strncpy
                 U strrchr
                 U strstr
                 U strtod
                 U strtof
                 U strtol
                 U strtoll
                 U strtoul
                 U strtoull
                 U __xstat

sed -i 's/WIN32?//g' clients/mapilib/Makefile.ag
sed -i 's/@WIN32_TRUE@//g' clients/mapilib/Makefile.in
sed -i 's/WIN32?//g' common/stream/Makefile.ag
sed -i 's/@WIN32_TRUE@//g' common/stream/Makefile.in

這是因為 MonetDB team 想要避免多次連結的解法而產生的問題。MonetDB team 沒有考慮到,如果有使用者使用 Load Library 的方式載入 MAPI library 使用,就會產生 undefined function 的問題而無法正確載入。


cmark-gfm and tcl-cmark (cmark-0.28.3.gfm.15)


cmark-0.28.3.gfm.15 是個不向前相容的版本(Normalised header and define names),所以如果使用這個版本,tcl-cmark 會編譯失敗。而且 cmark-gfm library 的名稱也有變動,所以無法單純在 source code 使用 cmark-gfm 的版本區隔來處理。

我先做了緊急處理,讓 tcl-cmark 加個 patch 以後不會編譯失敗。等原作者發現然後修改以後,再來看要怎麼做進一步處理。


EMQ/emqttd (Erlang MQTT Broker)

EMQ (Erlang MQTT Broker) is a distributed, massively scalable, highly extensible MQTT message broker written in Erlang/OTP.

在 openSUSE 從 source code 安裝,需要下列的套件:
sudo zypper in erlang erlang-observer

(erlang/otp version 需要 >= R17)

再來是下載 source code 並且編譯:
git clone https://github.com/emqtt/emq-relx.git
cd emq-relx && make

cd _rel/emqttd && ./bin/emqttd console

再來使用 tcl.mqttc 驗證是否可以正確連線,這只是進行簡單的驗證。
package require mqttc

mqttc client "tcp://localhost:1883" "USERSSub" 1 -cleansession 1 
client subscribe  "MQTT Examples" 1
while 1 {
    if {[catch {set result [client  receive]}]} {
        puts "Receive error!!!"
    if {[llength $result] > 0} {
        puts "[lindex $result 0] - [lindex $result 1]"
        if {![string compare -nocase [lindex $result 1] "Exit"]} {
client unsubscribe  "MQTT Examples"
client close

package require mqttc

mqttc client "tcp://localhost:1883" "USERSPub" 1 -timeout 1000
client publishMessage "MQTT Examples" "Hello MQTT!" 1 0
client publishMessage "MQTT Examples" "Exit" 1 0
client close

先執行 Subscribe 的部份,再使用 Publish 發送訊息,確定可以正確執行。


Apache Rivet transition from svn to git complete

Rivet transition from svn to git complete

The migration from svn is now complete, the svn repository is now read only and every new commit has to be pushed to either GitBox (https://gitbox.apache.org/repos/asf/tcl-rivet.git) or GitHub (https://github.com/apache/tcl-rivet.git)

Apache Rivet 從 SVN 遷移到 Git,而且在 Github 有一個 mirror。


tDOM 0.9.1



The most notable changes since the last release are:
- The included expat is now 2.2.5, the most recent expat release.
- An interface to use expat as pull parser (StAX like).
- Some minor options for more control about parsing ([dom parse
  -keepCDATA]) and serialization (asXML -nogtescape
- A few bug fixes important to whom are bitten by them.

因為下載網址已經放上去檔案了,所以我更新了我自己的 openSUSE RPM spec,將版本更新到 0.9.1。


TDBC and MariaDB (use MySQL driver)

#!/usr/bin/env tclsh

package require tdbc::mysql
tdbc::mysql::connection create db -user root -passwd admin -host -port 3306 -database test

set statement [db prepare {
    SELECT VERSION() as version

$statement foreach row {
    puts [dict get $row version]

$statement close

db close

在 openSUSE Leap 15.0 上測試,需要安裝 libmariadb-devel 才能夠正確執行。我的 libmariadb-devel 來源是 http://download.opensuse.org/repositories/server:/database/openSUSE_Leap_15.0/,因為我之前需要安裝跟資料庫有關的軟體,所以有使用這個 repository。


MAWT - Movie Automation With Tcl

MAWT - Movie Automation With Tcl

MAWT is a Tcl package based on FFmpeg. It provides high level procedures for movie automation with Tcl.

如果要下載的話,可以到 Downloads

我還沒有自己編譯過,會在 openSUSE 上嘗試是否可以編譯成功。


Apache Rivet 3.0.2 released

Rivet 3.0.2 is a bug fix release of rivet 3.0. This release includes
  • Fixed a bug in the default error handler
  • Other minor changes are relevant to the code and have no interest at the application level

如果要看 3.0 系列的 Release Notes,可以看這個網頁。如果要下載的話,可以去 download page



Zstandard (@ Tcler's Wiki)

Tcler's Wiki 上一樣已經有人使用 Critcl 寫好了壓縮與解壓縮的 code。我只是拿這份 code 來使用(不過有做一點點的修改,因為我要編譯出套件而不是採用 Tcl Module 的模式,而且不知道為什麼 openSUSE Build Service 在測試的時候會有相容性錯誤,為了通過測試,所以加改了三行左右)。

不過要注意的是 libzstd1 不存在於 openSUSE LEAP 42.3 軟體庫中,openSUSE Leap 15.0 才開始有收入軟體庫中。

真正神奇的是原作者利用了 Critcl 的特性,所以一開始的版本是 Tcl Module,而只要你有安裝 Critcl,就可以使用 libzstd 來壓縮與解壓縮。


tcl-lmdb v0.4.0


tcl-lmdb - Tcl interface to the Lightning Memory-Mapped Database


This is the Lightning Memory-Mapped Database (LMDB) extension for Tcl using the Tcl Extension Architecture (TEA).

LMDB is a Btree-based database management library with an API similar to BerkeleyDB. The library is thread-aware and supports concurrent read/write access from multiple processes and threads. The DB structure is multi-versioned, and data pages use a copy-on-write strategy, which also provides resistance to corruption and eliminates the need for any recovery procedures. The database is exposed in a memory map, requiring no page cache layer of its own. This extension provides an easy to use interface for accessing LMDB database files from Tcl.

Main Change

  1. Add command to handle byte array
  2. Add test cases for new command


事情有點複雜。 我在 Github 上有一個 Use byte arrays for keys and values 的 pull request。可是如果我直接 merge,就會造成 API 之間行為的不一致。所以我關掉那個要求,但是說我會加新的 command 來處理這件事。

可是加完以後,我覺得這樣事情只做了一半,所以最後我加入更多的 command 來處理 byte array 的部份,同時也加入一些 test cases 確定我加入的部份是 OK 的。

所以現在你可以將 key/value 視為 string,或者是 byte array,看你使用的 command 而定。如果沒有 byte array 的需求,那這個版本對你而言只是版本號的增加,那就…… 沒影響;如果需要視為 byte array 處理,那這個版本就會符合需求。因為修改了介面,所以跳升一個版本,變成 0.4.0。


brotli (@ Tcler's Wiki)

Tcler's Wiki 上已經有人使用 Critcl 寫好了壓縮與解壓縮的 code。我只是拿這份 code 來使用。

不過要注意的是 libbrotlidec1/libbrotlidec1/libbrotlicommon1 不存在於 openSUSE LEAP 42.3 軟體庫中,openSUSE Leap 15.0 才開始有收入軟體庫中。


Google Sites tclmonetdb is gone

簡單的說,就是 tclmonetdb  進行 Google 新的協作平台轉換以後版面直接炸掉,在試著調整了一下以後,我發現調整好是個有點麻煩的任務,我本來想說放棄新的保留舊的,但是 Google 顯然沒考慮有人會想這麼做,所以變成一個 zombie 的狀態。

經過思考,我直接刪除新的,然後在 tclmonetdb 舊的 Google Sites 網頁 留下訊息,這個站已經移往 Github 了,將在 2018/06/22 刪除。

所以事情就是這樣了,Google Sites tclmonetdb is gone. 如果 Google 服務繼續這樣,那他們會有麻煩,問題不在技術高低,問題在於使用的過程。

所以 Source code 你可以在 Github 找到:

如果是 openSUSE,你可以找到 OBS 上的檔案:
tclmonetdb @ OBS

至於 Windows 平台,其實我今天本來是要放新的檔案,但是因為炸掉了所以也直接被我刪除,就…… 跳過這件事吧。

我也刪除了這個部落格關於 tclmonetdb 文章(因為都會指向錯誤的連結)。




This function takes a theme name as an argument. It looks to see if the theme has been loaded, and if not, requires the package, and calls ttk to use the theme. While the code actually also sets a variable, that variable is local to the function and thus isn't available.

可以使用這個函式來設定 Ttk 要使用的 theme。

關於相關的 theme 列表,可以查看 List of ttk Themes。Ttk 已經有內建一些 theme 可以使用。


try and finally

在 comp.lang.tcl 看到這個寫法,所以我寫了一個練習用的程式來學習這件事:
try 是 Tcl 8.6 內建命令,可以在 finally 釋放所使用的資源。

#!/usr/bin/env tclsh
package require http
package require tls

http::register https 443 [list ::tls::socket -ssl3 0 -ssl2 0 -tls1 1]

set tok {}
set url {https://duckduckgo.com/}

try {
    set tok [http::geturl $url -method GET -timeout 3000]
    puts "Status: [::http::ncode $tok]"
    puts "Status Text: [http::status $tok]"
    puts "Headers: [http::meta $tok]"
} on error {em} {
    puts "Error: $em"
} finally {
    # cleanup here
    if {[info exists tok]==1} {
        http::cleanup $tok

(更新:拔掉網路線以後測試,我想需要檢查 tok 是否存在)

try ... finally ... (@ Tclers Wiki!)
TIP 329: Try/Catch/Finally syntax

tklib spec

tklib-spec (20180605)

因為 tablelist package 的關係,所以我也更新了 tklib 的 RPM spec。執行 build.tcl 就會 checkout 在 Github 的 mirror (@ 20180605) 並且進行處理,然後使用 rpmbuild 製造出 RPM 檔案。

這樣 tklib 所包含的 tablelist package 應該就是最近 release 的。


tablelist 6.2

tablelist @ Tclers Wiki!

tablelist is a multi-column listbox and treeview widget for Tk.

Tklib 已經有納入這個套件,不過這個套件本身仍然在繼續修正問題與增加功能,然後(應該有)同步到 Tklib。

因為 tablelist 和 Tklib 的進版速度是不同的,所以 tablelist 我也寫了 RPM spec(嗯…… 其實是我一開始沒發覺他已經在 Tklib 裡了),這樣如果要使用較新的版本或者是需要單獨安裝的使用可以使用。


TDBC-ODBC: Get Data Sources list

TDBC-ODBC 有提供取得目前 data sources 名單的函式 ::tdbc::odbc::datasources。下面的 code 就是取得目前的名單以後,將資料增加到 tablelist 的片段。
# Get data sources list and list
tablelist::tablelist .t -columns {0 "DSN" 0 "Driver"} -stretch all \
    -background white -font {Helvetica -14}
pack .t -fill both -expand 1 -side top
set sources [::tdbc::odbc::datasources]
foreach {dsn driver} $sources {
   .t insert end [list $dsn $driver]

這樣就可以取得目前的 ODBC data sources。

unixODBC 可以在 /etc/unixODBC 目錄中設定 odbc.ini 與 odbcinst.ini,或者是在家目錄下設定 .odbc.ini。

::tdbc::odbc::datasources 如果沒有特別設定,會取得 system 和 user 的名單。TDBC-ODBC 可以使用選項 -system 或者是 -user 來取得個別的名單。


tcl-opencc: Tcl bindings for OpenCC (libopencc)

Open Chinese Convert (OpenCC, 開放中文轉換) is an opensource project for conversion between Traditional Chinese and Simplified Chinese, supporting character-level conversion, phrase-level conversion, variant conversion and regional idioms among Mainland China, Taiwan and Hong kong.

OpenCC 是一個優秀的開源專案,以 C++ 開發,支援 Linux、Mac OS X、Windows、iOS、Android 等平台,同時有 Node.js bindings,可以用來做中文繁簡轉換的工作。OpenCC 已經提供了一個命令列工具,可以用來指定輸入檔案、輸出檔案與指定設定檔,然後就可以利用這個命令列工具來做中文繁簡轉換。

一個更好的地方是,OpenCC 將核心函式庫獨立出來,所以我們可以使用 OpenCC 提供的函式庫來開發一些工具,或者是與自己的程式結合產生更多的功能。

所以 tcl-opencc 就是這樣寫出來的,這樣子如果我需要在 Tcl 進行繁簡轉換的工作,就可以使用 OpenCC 的函式庫,而不用呼叫外部的執行檔。


openSUSE Leap 15.0 and OpenAL Soft

升級到 openSUSE Leap 15.0 以後,使用 tclopenal 播放音樂,都會跑出來下面的錯誤訊息:

Cannot connect to server socket err = No such file or directory
Cannot connect to server request channel
jack server is not running or cannot be started

一開始我沒有搞清楚狀況,所以以為是沒有安裝 Jack Audio Server,但是後來我又想,Linux 一般的預設應該是 ALSA/PulseAudio,所以不應該是沒有安裝 Jack Audio Server 的問題。最後才想到,可能是 OpenAL Soft 的設定問題。

OpenAL Soft 是有支援 Jack 的,所以可能是我之前使用的版本不用設定,或者是之前的版本已經有設定好。但是不管如何,這個錯誤訊息在 openSUSE Leap 15.0 跑出來,所以我需要消除掉這個錯誤訊息。

接下來我發現 /etc/openal 目錄下沒有 alsoft.conf 這個檔案。這給了我提示,所以在搜尋以後,找到答案,我需要建立 alsoft.conf 檔案,並且設定如下:
drivers = -jack,

經過測試,這樣確實可以消除無法連接到 Jack 的錯誤訊息。


tcl-cairo: Tcl bindings for Cairo library


使用 SWIG。資訊來自於 tclcairo。我只是透過範例來學習 SWIG 該如何使用,而剛好 tclcairo 網頁有範例可以用來測試,所以就嘗試看看。

Tcl and SWIG as a C/C++ Development Tool

Tcl and SWIG as a C/C++ Development Tool
SWIG and Tcl (3.0 doc)
Swig (Tcler's Wiki)

接下來我會花一點時間使用看看 SWIG,然後看能不能成功的將我需要的 C library 部份轉成 Tcl interface。因為不是很急迫,所以我只是開始收集資料而已。


GNOCL: Tcl/GTk Bindings


GNOCL 可以讓你在 Tcl 使用 Gtk+ 2 library。這個 spec 目前使用 2018/05/10 的 Gnocl-Nightly-Build。

因為版本更替的關係,所以一開始的時候我以為 Label 的 markup 功能是失效的,後來才發現要使用 -useMarkup 來開啟功能。

再來是有個地方造成了一些困擾,就是如果直接按 x 想要離開,會發生無法離開的狀況,這需要使用 -onDestroy 並且指定 exit 才行。


package require Gnocl
# create submenu for menu "File" with standard items "New" and 
# "Quit" (text, icon and accelerator). 'N', 'Q' respectively, 
# are underlined and are used as mnemonics.
set menu [gnocl::menu]
$menu add [gnocl::menuItem -text "%#New" -tooltip "Make new" \
      -onClicked {puts "That's new"}]
$menu add [gnocl::menuSeparator]
$menu add [gnocl::menuItem -text "%#Quit" -onClicked exit \
      -tooltip "Quit program"]

# create menu "File", 'F' is underlined and used as mnemonic
set file [gnocl::menuItem -text "%__File" -submenu $menu]

# create menu "Help" with item "About" 
set menu [gnocl::menu]
$menu add [gnocl::menuItem -text "%__About" \
      -tooltip "Show about dialog" \
      -onClicked {puts "Mini example (c) 2001 - 2003 P.G. Baum"}]
set help [gnocl::menuItem -text "%__Help" -submenu $menu]

# create toolbar with standard items "Quit" and "New"
set toolBar [gnocl::toolBar -style both]
$toolBar add item -text "%#Quit" -tooltip "Tooltip Quit" \
      -onClicked exit
$toolBar add space
$toolBar add item -text "%#New" -tooltip "Tooltip new" \
      -onClicked {puts "That's new"}

# create GTK+ application with menu, toolBar, statusbar 
# and a label as main widget
set box [gnocl::box -orientation vertical -borderWidth 0 -spacing 0]
set win [gnocl::window -child $box -title "Test Application" -onDestroy { exit } ]
$box add [gnocl::menuBar -children [list $file $help]]
$box add $toolBar 
$box add [gnocl::label -useMarkup 1 -text \
      {%<<span foreground="blue" size="large">Hello</span>\
      <span foreground="red" size="large">World</span>}] -expand 1
$box add [gnocl::statusBar] 

# enter GTK+ main loop


XQilla library 2.3.4: compatibility issue

我發現 XQilla 2.3.4 這個版本需要 Xerces-C++ 3.2.0 以上的版本。然後當我處理完相依性問題編譯成功以後,我發現 XQilla .so 的副檔名有改變(從 .so.3 變成 .so.2)。

結果重新編譯 tclxqilla 以後,會出現找不到 .so.3 的問題。我感覺 XQilla library 在 2.3.4 有相容性問題沒有處理好,所以在測試過以後,我決定跳過這個版本。

這也讓我的 openSUSE build service 上面的相關項目發生問題,因為我要重新編譯的關係,所以刪除項目以後重建一次,版本號沒有改變,但是 md5 變了,如果使用 zypper 安裝會出現警告和問題。

我稍微修改了 openSUSE build service 上面的 tclxqilla RPM spec,讓 build version 往上增加,這樣就不會同版本比對而可以消除使用 zypper 安裝會出現警告和問題。


Tcl: identity function

identity function: The identity function accepts one value and simply returns that value.

Tcl 8.6 可以使用下列的方式:
string cat $var

lindex $var



NaviServer and Markdown


還是記錄一下這件事。和之前寫的東西類似,也就是 Markdown 檔案輸出為 HTML 格式。在研究 NaviServer 目前的 code 以後,我發現在 NaviServer tcl 目錄下加上 markdown.tcl,就可以用來處理 /*.md。

下面是 markdown.tcl 檔案的內容:
# markdown.tcl --
#   Add tclllib markdown support to NaviServer

# Register the ns_markdownfie handler for .md files

package require Markdown

ns_register_proc GET  /*.md ns_markdownfie

# ns_markdownfie --
#   Callback for Makrdown file.

proc ns_markdownfie {args} {

    set path [ns_url2file [ns_conn url]]
    if {![ns_filestat $path stat]} {

    set infile [open $path]
    set md [read $infile]
    close $infile
    set data [::Markdown::convert $md]

    ns_headers 200 "text/html;charset=utf-8"
    ns_write $data


重開 NaviServer 以後測試,我發現這個寫法是可行的。

Tclunqlite v0.3.4




This is the UnQLite extension for Tcl using the Tcl Extension Architecture (TEA).

UnQLite is a in-process software library which implements a self-contained, serverless, zero-configuration, transactional NoSQL (Key/Value store and Document-store) database engine. This extension provides an easy to use interface for accessing UnQLite database files from Tcl.

Main Change

  1. Update UnQLite to 1.1.9


這是一個小更新版本,整合 UnQLite 的版本更新。


Apache Rivet and mod_actions


雖然 mod_actions 的文件是寫 CGI script,但是你可以使用 Apache Rivet, PHP 或者是類似的工具來撰寫。

下面是測試用的程式,用來將 markdown 檔案 (*.md) 轉換為 HTML 格式以後輸出:
package require Markdown


if {[info exists ::request::env(PATH_TRANSLATED)]==0} {
    ::rivet::headers numeric 400

if {[file exists $::request::env(PATH_TRANSLATED)]==0} {
    ::rivet::headers numeric 404

set in [open $::request::env(PATH_TRANSLATED)]
set md [read $in]
close $in
set data [::Markdown::convert $md]

::rivet::headers type "text/html;charset=utf-8"
puts $data

再來加上設定(我加在 VirtualHost configure file):
Action markdown /mdhandler.tcl
AddHandler markdown .md

最後重開 Apache HTTP server 然後進行測試。我把程式放在 Github 上。


tcl-curses: A "minimalist" tcl package for interfacing to curses


大部份的 code 來自 Minimalist Curses。我只是加上 TEA 的檔案,這樣比較好編譯(對我而言),然後加上我自己需要的部份。

只有在 openSUSE LEAP 42.3 使用 ncurses 測試過。




Augeas is a free software configuration-management library, written in the C programming language. Augeas is a tool for accessing and modifying data stored in configuration files of various formats.

可以用來設定檔案的管理,這樣可以有一個更好的方式來進行設定。在 Tcler's Wiki 條目上有一個 Tk 示範程式,列出來目前的設定。如果要做一些軟體設定,就不用自己寫一個分析設定檔案的程式, 而是透過 Augeas 來設定(如果 Augeas 有支援)。

很有趣的工具。我在 Ubuntu 14.04 編譯成功,如果我在 openSUSE LEAP 42.3 也成功,那我嘗試寫看看 tcl-augeas 的 RPM spec,如果成功我再嘗試放到 openSUSE build service 上。


tclopusfile: Tcl bindings for Opusfile library


Opus is a lossy audio coding format developed by the Xiph.Org Foundation and standardized by the Internet Engineering Task Force, designed to efficiently code speech and general audio in a single format, while remaining low-latency enough for real-time interactive communication and low-complexity enough for low-end embedded processors.

目前我的實作只有實作開啟檔案的部份,read 的部份使用 int16,float 的部份也省略(也就是我自己需要的部份,讀取 .opus 檔案然後播放音樂)。

目前只有幾個 sample 可以測試,不過聽起來是正常的,所以我把目前的實作放上去 github 了。

中間卡住的地方是 op_read,似乎跟 libopusfile 的實作有關,即使我加大我的 buffer,拿回來的 sample/channel 都是 960,一開始我以為他會計算 buffer 傳回來最大值,後來才發現…… 不是這樣。


OpenCL and Tcl


OpenCL(Open Computing Language,開放計算語言)是一個為異構平台編寫程式的框架,此異構平台可由 CPU、GPU、DSP、FPGA 或其他類型的處理器與硬體加速器所組成。OpenCL 由一門用於編寫 kernels(在 OpenCL 裝置上執行的函式)的語言(基於C99)和一組用於定義並控制平台的API組成。OpenCL 提供了基於任務分割和資料分割的平行計算機制。

OpenCL 一開始由蘋果所開發,並且擁有商標權。目前業界類似的是 NVIDIA 的 CUDA,而且 CUDA 目前看起來較為流行。OpenCL 交給 Khronos Group 之後,採用的主力是 AMD(AMD 曾經大力主推了一 陣子,不過大家也知道 AMD 前幾年狀況有點不好,所以……)

自由軟體的實作則是 pocl (Portable Computing Language),使用者可以在一般的 CPU 上使用 OpenCL API。再來是 pocl 最新的版本已經開始嘗試加入 NVIDIA GPU devices 的支援。

我看到 pocl 的時候,才想到確實 OpenCL 並沒有一定要在 GPU 上才行(只是大多數都是使用在 GPU 或者是 FPGA 上)。我已經稍微測試一下 pocl,發現 OpenCL 2.0 是可用的。

Tcl 有關的套件則是 TclOpenCL,嘗試 VecTcl 與 OpenCL 的整合。


Get Linux Distribution Name (Tcl exec 版)

在最後嘗試 Bash 的版本。在嘗試以後,寫好了 Bash 版,使用 cat, grep 和 sed 取得結果。

if [ -f '/etc/os-release' ]; then
    cat '/etc/os-release' | grep -w "^NAME" | sed -e 's/NAME=//g'

於是我就想,那我可以使用同樣的方式在 Tcl  嗎?答案是可以。
#!/usr/bin/env tclsh
# Execute external program to get result

package require Tcl 8.6

set myfunction {{} {
    return [exec cat /etc/os-release | grep -w "^NAME" | sed -e "s/NAME=//g"]

if {[file exists "/etc/os-release"]==1} {
    puts [apply $myfunction]



Tcl double quotes and string map

這只是 string map 的測試。

set doublequotes {"It is double quotes"}
string map [list {"} {\"}] $doublequotes
string map [list {\"} {"}] $doublequotes

進行雙引號的改寫,從 " 改寫為 \",以及改回來,從 \" 改成 "。


Use uname to get info

在 Unix-like 的系統可以使用 uname 來取得一些系統資料。下面是使用 uname -s 來取得目前的 OS 資訊。

proc getKernelName {} {
    set mychannel [open "|/usr/bin/uname -s"]
    set name [chan gets $mychannel]
    chan close $mychannel

    return $name

如果在 Linux 上,會得到的回傳值是 Linux。

我把其它的部份也寫成函式,結果就是下面的 tcl module:

Get Linux Distribution Name

使用 /etc/os-release 來取得 Linux Distribution 的相關資訊。應該可以適用於: CentOS, RedHat, Fedora, openSUSE, SUSE, Debian/Ubuntu, ArchLinux

proc getDistributionName {} {
    set OSName ""

    if {[file exists "/etc/os-release"]==1} {
        set release [open "/etc/os-release" r]
        while {1} {
            if {[chan gets $release line] > 0} {
                set info [split $line "="]
                if {[string compare [lindex $info 0] "NAME"]==0} {
                    set OSName [lindex $info 1]
            } else {
                if {[chan eof $release]} {
                } else {
                    puts "Something is wrong."

        chan close $release
    } else {
        return -code error "/etc/os-release not exist!!!" 
    return $OSName

os-release — Operating system identification


Tcllib and Tklib

開始準備 openSUSE Leap 15.0,雖然我會等一下才升級,但是放在 openSUSE build service 的部份可以開始準備。

Tcllib 和 Tklib 遇到同一個問題,就是 openSUSE build service 會將下面的寫法視為錯誤:
#!/usr/bin/env tclsh

所以我只好用 sed 改寫 examples 下的範例,改成 #!/usr/bin/tclsh。這似乎是 openSUSE Leap 15.0 才出現的檢查。

(* 有疑問的地方在於,其實 #!/usr/bin/env tclsh 是比較推薦的寫法,所以我搞不懂為什麼會加這個檢查)




在最近使用的過程中,我意外的發現我之前並沒有全部搞定。在最後編譯 md5c 的時候沒有成功,只是 openSUSE build service 還是成功的建立 RPM,所以可以使用部份的功能。

經過研究 critcl 的安裝過程,在 RPM spec 加入下列的修正:
+export TCLLIBPATH=" %{buildroot}/usr/lib64/tcl/critcl-app3.1.17 %{buildroot}/usr/lib64/tcl/critcl3.1.17 \
+%{buildroot}/usr/lib64/tcl/dict841 %{buildroot}/usr/lib64/tcl/lassign841.0.1 \

在安裝過程中,Critcl 會複製本身的套件到 Tcl library 目錄下,所以最後的步驟編譯 md5c 的時候,所有需要的套件都可以被 Tcl 找到。

而製造 RPM 的過程中,套件會被複製到 buildroot 目錄下,所以在這個時候 Tcl 會找不到需要的套件。因此只要讓 Tcl 可以找到套件,就可以完成編譯 md5c 的步驟。

在嘗試以後,既然我知道這些套件的安裝地點,就表示我可以使用設定 TCLLIBPATH 的方式解決,所以在安裝以前先設定環境變數,然後再執行安裝程式,這樣 Tcl 就會找到需要的套件完成編譯。

Tclqrencode v0.9


更新 libqrencode 的 code base,花了幾個版本整合,所以版本從 v0.7 開始一路修正 merge 所造成的問題,目前版本是 v0.9(希望我一切都搞定了)。

這個版本加入 XPM 的支援。然後我放一個 BSD 2-Clause License 的檔案在 source code package。


Get Linux kernel version

練習程式,讀取 /proc/version 並且輸出資料。/proc/version 記錄著 Linux kernel 的版本資訊。

#!/usr/bin/env tclsh

set version [open "/proc/version" r]
while {1} {
    if {[chan gets $version line] > 0} {
        puts $line
    } else {
        if {[chan eof $version]} {
        } else {
            puts "Something is wrong."

chan close $version


The proc filesystem (procfs) is a special filesystem in Unix-like operating systems that presents information about processes and other system information in a hierarchical file-like structure, providing a more convenient and standardized method for dynamically accessing process data held in the kernel than traditional tracing methods or direct access to kernel memory.

List current processes (Linux)

練習程式,讀取 /proc 下的目錄,如果存在 /proc/*/cmdline,就輸出資料。

#!/usr/bin/env tclsh

set dirs [glob -dir /proc * -type d *]
foreach dir $dirs {
    if {[file exists $dir/cmdline]} {
        set psinfo [open $dir/cmdline r]
        while {1} {
            if {[chan gets $psinfo line] > 0} {
                puts "$dir ---> $line"
            } else {
                if {[chan eof $psinfo]} {
                } else {
                    puts "Something is wrong."

        chan close $psinfo

而 /proc/PID/status 就是這個 process 目前的資訊,所以我們也可以查詢 /proc/self/status 取得自己的資料:
#!/usr/bin/env tclsh

set status [open "/proc/self/status" r]
while {1} {
    if {[chan gets $status line] > 0} {
        puts $line
    } else {
        if {[chan eof $status]} {
        } else {
            puts "Something is wrong."

chan close $status




tcl-promise implements the promise abstraction for asynchronous programming.

Promises are concurrency primitives that let you write asynchronous code in a sequential style. This Tcl based implementation is mostly modeled on the Javascript/ECMAScript standard.

* 而且 tcl-promise 在 1.0.3 還實作了 async/await。
* 因為 SourceForge 剛好出現問題,所以我複製了一份到 GitHub 上

我在嘗試使用 openSUSE zypper 作為 Tcl 套件管理的時候,就發現 Tcl module file (*.tm) 不是很好處理。這是因為如果放到 /usr/lib64/tcl,openSUSE build service 會檢查是否有包含 native 檔案,如果完全沒有會編譯失敗(至少我試的時候是這樣)。但是如果放到 /usr/share/tcl,則會需要建立一個 pkgIndex.tcl 才行。

所以大多數的 Tcl module file (*.tm) 我都用複製的(而不用 zypper 管理),只有少數要放上去 openSUSE build service 做編譯測試的套件我會做特別處理。tcl-promise 這個套件也使用了 work-around 的方式,在 /usr/share/tcl 目錄下建立套件目錄,並且建立 pkgIndex.tcl 檔案。

2018/03/07 更新
因為 SourceForge 恢復正常,所以刪除了複製的部份,build script 改用 wget 取得檔案的方式。




Tcl3D offers the 3D functionality of OpenGL and other 3D libraries at the Tcl scripting level.

因為 BAWT-Tcl 包含了 Tcl3D 套件,所以我有試著 build 看看。Tcl3D 在 SourceForge 上放的檔案是 0.5 版和 0.6 版,但是在 BAWT 網站上放的是 0.9.1 版,所以最後拿 BAWT 提供的版本來使用。

在寫 RPM spec 的時候加了二個 patch,CMakeLists.patch 用來設定我們要安裝的目錄,CMakeModules.patch 則是用來複製各個子目錄下的 pkgIndex.tcl,在 Windows 平台上使用 Visual C++ 編譯是有 Release/Debug 的分別,我不太確定 Linux 平台上有沒有,但是因為沒有正確複製,所以我加了一小段修改讓 pkgIndex.tcl 可以正確複製。



我之前有聽過,只是沒有實際安裝過,不過經過閱讀 Tcler's wiki 的資料,知道怎麼編譯了。先置條件是需要 Critcl 有安裝才行。

./sak.tcl critcl

或者是使用 configure/make 的組合:
make critcl

The packages using Tcllibc are:
  • base32
  • crc32
  • ip
  • md4
  • md5
  • md5crypt
  • rc4
  • ripemd128
  • ripemd160
  • sha1
  • struct::graph
  • struct::sets
  • struct::tree
  • sum
  • uuencode
  • uuid
  • yencode
這就是目前關於 Tcllibc 的簡單資料。

使用者不用特別指定使用,而是如果有使用的套件在有安裝的時候,就會使用 C 的版本,沒有的時候使用 pure Tcl 版本。

為了驗證,所以我寫了 openSUSE RPM spec,這樣就比較好裝:


tcl-archive: A Tcl wrapper for LibArchive


LibArchive project develops a portable, efficient C library that can read and write streaming archives in a variety of formats.

目前 tag 為 v0.1 版。這是在 Tcl 實作使用 libarchive 的 command,用來壓縮和解壓縮一些常見的壓縮檔案格式(主要是解壓縮)。

我只有寫簡單的 tcltest cases 和簡單的解壓縮測試,看起來沒問題,所以就先 tag 一個版本號。


Extended Tcl (TclX)

Extended Tcl (TclX)

這是 flightaware 修改的版本(for Tcl 8.6),更新了 build system,並且移除了 TkX extension。我在 ./configure 完以後順利編譯,但是使用的時候有點問題,會有 symbol 找不到。經過檢查以後,發現是 configure 需要正確加入新的檔案才行(如果一開始就先執行 autoconf 則不會有此問題)。我看了一下說明以後,發現沒有指定使用 autoconf,送出一個修正的 pull request,flightaware 也接受了這個小修正。

那麼為什麼我會需要 TclX 呢?因為我最近在試著簡化我自己的 Golang 環境(* 雖然我很少寫 Go,但是我需要測試某些東西的時候可能會需要,所以放一份),如果使用 openSUSE build service 提供的新版本,會一次安裝預料之外的版本數目,但是如果是使用穩定版本則沒有這個問題,只是要嘗試新版本就比較不方便(有可能是因為要使用 Go 來編譯 Go 本身所造成的影響)。

後來我想到,我可以寫一個 script 從官網下載以後解壓縮到指定目錄,如果檢查 .profile 不存在或者是沒有相關的環境變數,就建立檔案或者是寫入相關的環境變數,最後設定 .profile 的 owner 和 group。為了使用 chown,所以需要 TclX 才行。


tcllib 1.19

在剛才我在看資料的時候,發現 tcllib 1.19 已經釋出了。

Overview ======== 7 new packages in 6 modules 52 changed packages in 35 modules 15 internally changed packages in 10 modules 359 unchanged packages in 105 modules 443 packages, total in 130 modules, total



tdbchikaricp: Tcl DataBase Connectivity Driver for HikariCP library

HikariCP is a solid high-performance JDBC connection pool.

不過這裡比較像使用 HikariCP 提供的連線方式之一(讀取設定檔案)來連線,並沒有使用 JDBC connection pool 的能力。大多數的 code 來自 TDBCJDBC,因為連上資料庫以後的行為就跟 JDBC 很類似了。

2018/02/12 更新
後來我才想到,其實可以在外面生成 HikariDataSource 物件再傳進來,所以有做一點小更新。

2018/02/12 更新
然後我又想到,既然 tdbchikaricp 實作在外面生成 HikariDataSource 物件再傳進來的功能,如果我小改寫以後,也許只要有支援 DataSource interface 的 library 就可以套用這個方式。晚上我會使用 Alibaba Druid 來測試,然後在 Github README.md 上寫如何使用的說明。




這是我嘗試使用 .NET core 2.0 和 Tcl/TK 8.6 去編譯 TickleSharp v0.1 的結果。這是 C#  使用 P/Invoke 呼叫 Tcl/Tk functions 的套件,我發現…… 使用 .NET core 2.0 和 Tcl/TK 8.6 去編譯以後還是可以用。

只有在 openSUSE LEAP 42.3 上測試。


Dockerfile for openSUSE, Tcl and Java


本來是使用 -v 來對應 Docket Host 的目錄與 Docket image 裡的目錄,但是後來我發覺如果我只是要測試 tclBlend 在 Open JDK 9 的環境能不能編譯,我應該要做的是安裝 git 然後在這個環境下使用 git clone 取得 source code,再來測試是否可以編譯。

這樣就解決了我自己的困擾。因為一些使用的軟體還沒有遷移到 Java 9,所以我的主要使用環境還是 Open JDK 8。如果我需要測試 Open JDK 9 的部份,就可以使用 Docker 建立一個基本環境然後進行測試(而不會干擾到我自己的使用環境)。

Dockerfile for openSUSE, Tcl and Naviserver


How To Running Systemd on openSUSE Docker Container 的教學,照著建立以後,確認可以讓 systemd 執行。下一步就是嘗試加入 service,因為我之前嘗試建立並且拿到 openSUSE build service 編譯的 NaviServer RPM 有關於 systemd service 的部分,所以就使用 NaviServer 來測試了。

這還給我另外一個啟示,如果我可以在 docker image 和 docker host 之間同步或者是共用資料,就表示我可以將使用環境與開發環境作一定程度的分離。之前知道可以這樣弄,但是沒有嘗試過,也許該測試一下。


Tclunqlite v0.3.3




This is the UnQLite extension for Tcl using the Tcl Extension Architecture (TEA).

UnQLite is a in-process software library which implements a self-contained, serverless, zero-configuration, transactional NoSQL (Key/Value store and Document-store) database engine. This extension provides an easy to use interface for accessing UnQLite database files from Tcl.

Main Change

  1. Update UnQLite version to 1.1.8


這是一個小更新版本,更新 UnQLite 的版本。



SourceForge 很直接的將 CVS web 關掉,需要該計畫的管理者將 CVS 轉為 GIT 才行。

所以目前我建立的 RPM spec/build script 中,至少有三個會出現問題(因為只是快速檢查,所以我不確定是否有我沒檢查到的)。

三個的狀況都不相同。TclSOAP 1.6.7 使用 TclXML,而 CVS 的發展版本才是使用 tDom 的版本。如果 openSUSE  build service 我在上面有檔案,我會先暫時放檔案上去 github。

tktable 是比較新的版本 2.11 有可能會遺失,如果 openSUSE  build service 我在上面有檔案,我會先暫時放檔案上去 github。

TclVFS 已經遷移到 core.tcl.tk,所以需要使用 Fossil 來取得新版本並且更新內容。(無法順利 clone,要嘗試下載檔案以後處理的方式)

另外,tcl-dbus 也已經遷移(而且還刪除了所有的檔案),我暫時先把舊的檔案放到 github 上,有時間再處理。 (重建一次,嘗試下載檔案以後處理的方式)


ffidl: Ffidl allows you to define Tcl/Tk extensions with pure Tcl wrappers calling any shared library installed on your system, including the Tcl and Tk core libraries.


openSUSE 上的是舊的,所以我把目前比較有更新的 ffidl 寫成一個 RPM spec,試著製作  RPM 以後安裝。ffidl.patch 參考 openSUSE 之前 spec 的寫法,讓 ffidl 在編譯的時候使用系統的 libffi,而不是自己編譯的。

我有放上去 openSUSE build service 上我自己的 projects,有順利編譯。



使用 Tcl 寫的 JavaScript minifier,我發現的時候覺得很有趣(一方面也是有點意外),FlightAware 居然用 Tcl 實作了一個自己版本的 JSMin。


Apache Rivet 3.0 released

Apache Rivet 3.0 released

Nonetheless Rivet 3.0 ships with a major rewriting of mod_rivet, whose code has been redesigned into a modular architecture with the purpose to preserve the basic features of the 2.x series of modules but also to provide support for both non threaded Apache MPMs (Multi Process Module), such as mod_mpm_prefork, and threaded MPMs such as mod_mpm_worker, mod_mpm_event and mod_mpm_winnt. As a consequence mod_rivet now also can run with the Apache Web Server on the MS-Windows family of operative systems.

Threaded MPM integration was achieved by making mod_rivet multi-threaded and modular itself, introducing the MPM-module bridge concept. We developed a set of loadable modules which are supposed not only to overcome the issues related to threading but also the best possible MPM mod_rivet integration. As a side effect of this modular design mod_rivet is not only able to integrate with its environment but also to work as a framework for writing more MPM bridges designed along different multi-threading schemes and workload management models.

mod_rivet is built using the traditional autotools based toolchain but with version 3.0 we also are supporting a CMake based script that can be used on any OS where CMake is available. The CMake tool is necessary in order to build mod_rivet on MS-Windows

快速解釋一下狀況。如果只支援傳統的 perfork mode,那麼就無法與 HTTP/2 module 共用(因為 HTTP/2 module 與 perfork mode 並不相容)。

也就是如果要使用 Apache Httpd server + mod_http2 + Apache Rivet,Apache Rivet 必須支援其它的模式。這是個很重要的進展。


我在 local 端建立 RPM 的時候會在最後的 check test 階段失敗,所以 test 整段都被我拿掉了。經測試,在一個 Aapache Http Server + SSL + mod_http2 (使用 MPM_event)的環境下加入 Apache Rivet 3.0.0 跟之前的行為不同,是可以運作的,所以目前可以確定,使用 Apache mod_http2 與 Apache Rivet 搭配是可行的。

tclBlend, javah and JDK8

我在追蹤 Java 接下來的變化時,才發現 JDK 10 預計將會移除 javah

所以我對自己備份的 tclBlend 做了一些更動,主要是使用 javac -h 來產生 header file,然後移除掉 javah 的部份。不過這也表示,接下來的版本需要使用 JDK8 或者是更高的版本才行。

另外就是我在嘗試的過程中發現 file.toURL() 已經在 JDK6 被標記 deprecation,所以我也對這部份進行了小修改。



yajl-tcl: Tcl bindings for Yet Another JSON Library

Ubuntu 我很快的就安裝成功了,openSUSE 則遇到一點問題。目前 openSUSE Leap 42.3 的 libyajl-devel 沒有包含 pkgconfig 的部份,需要使用有包進去的版本

寫好的 openSUSE RPM spec:

會需要編譯是因為最新釋出的 TclWS (2.5.0) 有加這句話:yajl-tcl from flightaware github (only for rest-flavour requests)

所以更新完 tclws-spec 以後,我也建了一份 yajl-tcl spec(卡在 pkgconfig 的問題一段時間),只是如果有人需要的話,就可以直接建 RPM 然後使用。



DiffUtilTcl: A Tcl extension for diff utility functions, like Longest Common Substring

Magicsplat Tcl/Tk for WindowsBAWT 都新增加了這個套件,所以我也寫了一個 openSUSE RPM spec,這樣可以建立 RPM 來測試。




tcl-cmark: Tcl bindings to the cmark-gfm GitHub Flavoured CommonMark/Markdown library

會引起我的再次注意是因為 Magicsplat Tcl/Tk for Windows 在新版中加入了這個套件。

這個套件需要先安裝 libcmark-gfm 才行。經過搜尋,openSUSE LEAP 42.3 看起來有 cmark 套件,我會研究一下是不是可以先安裝 cmark 套件以後編譯成功。如果可以,我會試著建立一個 tcl-cmark 的 openSUSE RPM spec。

看起來不行,檢查以後,TCLCMARK_LOCATE_CMARK 會檢查是否有安裝 libcmark-gfm,而二者的 library 名稱不同。所以如果要使用 tcl-cmark,需要先有 libcmark-gfm RPM spec 才行。

cmark-gfm 使用 release 的 0.28.0 會有 header 問題,需要 git clone 使用較新的 code 才行。