--- /dev/null
+++ b/lib/recoll.tcl
@@ -0,0 +1,170 @@
+proc Recoll_Startup {} {
+    global recoll exwin
+
+    set w .recoll
+    if ![Exwin_Toplevel $w "Full Text Searching with Recoll" Recoll] {
+        return
+    }
+
+    # build the gui
+    wm minsize $w 200 200
+    wm iconname $w "exmh Recoll"
+
+    # add a link to the query expression manual
+    set f $w.but
+    Widget_AddBut $f manual "Manual" { URI_StartViewer "https://www.recoll.org/usermanual/usermanual.html#RCL.SEARCH.LANG" }
+    pack $f.quit -side right -padx 2 -fill y
+    Widget_Label $f info {top fillx} -textvariable recoll(info) -anchor w
+
+    set f [Widget_Frame $w g Menubar {top fillx}]
+    Widget_AddBut $f recoll "Search" Recoll_Search {left padx 2 filly}
+    set recoll(searchButton) $f.recoll
+    $f.recoll config -width 6
+
+    # start with the default: all folders
+    set recoll(searchrng) "all"
+
+    Widget_Entry $f e {left fillx expand padx 2}  -textvariable recoll(search)
+    Widget_BindEntryCmd $f.e <Key-Return> "$f.recoll invoke"
+    Widget_RadioBut $f all "all" recoll(searchrng)
+    Widget_RadioBut $f sub "subtree" recoll(searchrng)
+    Widget_RadioBut $f cur "current" recoll(searchrng)
+
+    # results list
+    Widget_Frame $w results
+    set t [Widget_Text $w.results 20 \
+      -relief raised -borderwidth 2]
+    # Set up tag for hyper link
+    if {[winfo depth .] > 4} {
+        # Colors as in Mosaic: blue3 and ?violetred3?
+        Preferences_Resource recoll(anchorColor) anchorColor blue
+        Preferences_Resource recoll(visitedAnchorColor) visitedAnchorColor "violet red"
+        set recoll(hyper) [list -underline 1 -foreground $recoll(anchorColor)]
+    } else {
+        set fg [option get $t foreground Foreground]
+        set bg [option get $t background Background]
+        set recoll(hyper) [list -foreground $bg -background $fg]
+    }
+    append recoll(hyper) " -lmargin2 1i"  ;# wrap indent
+    $t tag configure indent -lmargin2 10m -lmargin1 5m
+    eval {$t tag configure hyper} $recoll(hyper)
+    $t tag bind hyper <ButtonRelease-1> {
+        Recoll_Hyper [%W get "@%x,%y linestart" "@%x,%y lineend"]
+    }
+    $t tag bind hyper <Enter> {set recoll(cursor) [lindex [%W config -cursor] 4] ; %W config -cursor tcross}
+    $t tag bind hyper <Leave> {%W config -cursor $recoll(cursor)}
+
+    bind $t <Destroy> {catch unset recoll(results)}
+    set recoll(results) $t
+}
+
+proc Recoll_Search {} {
+    global recoll mhProfile flist exmh env
+
+    if [regexp -- "^\[  \]*\$" $recoll(search)] {
+        set recoll(info) "Empty search string specified"
+        bell
+        return
+    }
+
+    set t $recoll(results)
+    $t configure -state normal
+    $t delete 1.0 end
+    $t mark set insert 1.0
+    $t configure -state disabled
+
+    # mhprofile path is absolute, but recoll prefers relative for dir:xyz
+    set relprefix $mhProfile(path)
+    regsub "^$env(HOME)/" $mhProfile(path) "" relprefix
+
+    set opts "-S mtime -NF \"url mtime title abstract author recipient filename mtype\" "
+    if {$recoll(searchrng) == "all"} {
+        append opts "dir:$relprefix"
+    } elseif {$recoll(searchrng) == "subtree"} {
+        append opts " dir:$relprefix/$exmh(folder)"
+    } elseif {$recoll(searchrng) == "current"} {
+        append opts " dir:$relprefix/$exmh(folder) -dir:$relprefix/$exmh(folder)/*"
+    }
+    set recoll(info) "Searching..."
+
+    Exmh_Debug "recollq $opts $recoll(search)"
+    if [catch {
+        open "|$recoll(path)/recollq $opts $recoll(search)" r
+    } x] {
+        Exmh_Debug Recoll error $x
+        set result $x
+    } else {
+        set recoll(result) {}
+        fileevent $x readable [list RecollRead $x]
+        set recoll(channel) $x
+        tkwait variable recoll(eof)
+        set result $recoll(result)
+    }
+    $t configure -state normal
+
+    # recoll produces two lines of fixed headings; not sure where
+    # the two trailing blank lines come from...
+    set manylines [ lrange [split $result "\n"] 2 end-2 ]
+    set rcount [llength $manylines ]
+
+    if { $rcount > 0 } {
+        for { set ridx 0 } { $ridx < [ llength $manylines ] } { incr ridx } {
+            set oneres [split [string trim [ lindex $manylines $ridx ] ] " " ]
+            for {set tidx 1} {$tidx < [llength $oneres]} {incr tidx 2} {
+                lset oneres $tidx [string trim \
+                                       [ encoding convertfrom utf-8 \
+                                             [ binary decode base64 [ lindex $oneres $tidx ] ] ] ]
+            }
+            array set thisresult $oneres
+            # date, please...and don't get confused by leading zeros or ,123456789, inputs...
+            if { [ info exists thisresult(mtime) ] } {
+                set thisresult(mtime) [ regsub -all {[^[:digit:]]} $thisresult(mtime) "" ]
+                set thisresult(date) [clock format [ scan $thisresult(mtime) %d ]];
+            }
+            # report the filename only if it's not an email
+            if { [ info exists thisresult(mtype) ] \
+                     && ! [ string compare $thisresult(mtype) "message/rfc822" ] } {
+                array unset thisresult "filename"
+            }
+            # and display the goodies
+            regsub "^file://.*$mhProfile(path)/" $thisresult(url) "" justfolder
+            $t insert end $justfolder
+            $t tag add hyper "insert linestart" "insert lineend"
+            $t insert end "\n"
+            set showthese { date title abstract filename author recipient }
+            foreach item $showthese {
+                if [ info exists thisresult($item) ] {
+                    $t insert end [ format "%s\t%s\n" "$item:" $thisresult($item) ];
+                }
+            }
+            $t insert end "\n";
+        }
+    }
+    $t yview 1.0
+    $t configure -state disabled
+    set recoll(info) "$rcount results"
+}
+
+proc RecollRead {in} {
+    global recoll
+    if [eof $in] {
+        catch {close $in}
+        set recoll(eof) 1
+    } else {
+        append recoll(result) [gets $in]\n
+    }
+}
+
+
+proc Recoll_Hyper {hyper} {
+    global recoll exmh
+
+    if {![regexp {([^ ]+)/([0-9]+)} $hyper all folder msg]} return
+
+    # show message
+    if {[string compare $folder $exmh(folder)] != 0} {
+        Folder_Change $folder [list Msg_Change $msg]
+    } else {
+        Msg_Change $msg
+    }
+}
--- a/lib/app-defaults
+++ b/lib/app-defaults
@@ -338,7 +338,7 @@ exmh.iconic:		0
 
 
 *Fops.search.text: 	Search...
-*Fops.search.m.entrylist: help msg fast pick glimpse thread
+*Fops.search.m.entrylist: help msg fast pick glimpse recoll thread
 *Fops.search.m.uentrylist:
 
 *Fops.search.m.l_help: Search Help
@@ -351,9 +351,12 @@ exmh.iconic:		0
 *Fops.search.m.c_pick:	Pick
 *Fops.search.m.l_glimpse: All messages in all folders (glimpse)
 *Fops.search.m.c_glimpse: Glimpse_Startup
+*Fops.search.m.l_recoll: All messages in all folders (Recoll)
+*Fops.search.m.c_recoll: Recoll_Startup
 *Fops.search.m.l_thread: Display related messages
 *Fops.search.m.c_thread: Thread_Display
 
+
 *Fops.sequences.text:	Sequences...
 
 *Fops.buttonlist:	commit inc flist new
--- a/lib/extrasInit.tcl
+++ b/lib/extrasInit.tcl
@@ -714,6 +714,20 @@ The key id is substituted with %s (using
 
 }
 
+# Recoll_Init: adjusts the fopsmenu for recoll iff installed
+proc Recoll_Init {} {
+    global recoll
+
+    if { ! [ info exists recoll(path) ] || [string length $recoll(path)] == 0} {
+        global exwin
+        catch {destroy $exwin(fopButtons).recoll}
+        catch {$exwin(fopButtons).search.m entryconfigure Recoll* -state disabled}
+        return
+    }
+    if [info exists recoll(init)] { return }
+    set recoll(init) 1
+}
+
 # Glimpse_Init
 #
 #  glimpse options used in extrasInit.tcl	: version   : variable
@@ -729,9 +743,10 @@ The key id is substituted with %s (using
 #  -W : AND scope whole file			: since 2.0:glimpse(andScope) 
 
 proc Glimpse_Init {} {
-	global glimpse
+    global glimpse
 
-	if {[string length $glimpse(path)] == 0} {
+    # unlisted or blank means none, please
+    if { ! [ info exists glimpse(path) ] || [string length $glimpse(path)] == 0} {
 	    global exwin
 	    catch {destroy $exwin(fopButtons).glimpse}
 	    catch {$exwin(fopButtons).search.m entryconfigure Glimpse* -state disabled}
--- a/lib/main.tcl
+++ b/lib/main.tcl
@@ -97,6 +97,7 @@ set startup_code {
     Crypt_Init
     Pgp_Init
     Glimpse_Init
+    Recoll_Init
     Addr_Init
     Background_Init
     fileselect_Init
