diff options
28 files changed, 701 insertions, 35 deletions
@@ -1,3 +1,13 @@ bundle exec jekyll serve JEKYLL_ENV=production bundle exec jekyll build + +CGI scripts: + +# chown -R www:www cgi-bin/ +# chmod 554 cgi-bin/find.cgi + +Checking CGI script errors in chroot: + +# chroot /var/www/ htdocs/www.asciimx.com/cgi-bin/find.cgi + diff --git a/_includes/nav.html b/_includes/nav.html index dc2cc5d..0b7b631 100644 --- a/_includes/nav.html +++ b/_includes/nav.html @@ -13,7 +13,12 @@ <li {% if page.url == "/about/" %}class="active"{% endif %}> <a href="{{ site.baseurl }}/about/" class="link-decor-none">abt</a> </li> - <li><a href="{{ site.baseurl }}/feed.xml" class="link-decor-none">rss</a></li> + <li> + <a href="{{ site.baseurl }}/cgi-bin/find.cgi" class="link-decor-none">sws</a> + </li> + <li> + <a href="{{ site.baseurl }}/feed.xml" class="link-decor-none">rss</a> + </li> </ul> </div> diff --git a/_log/matrix-digital-rain.md b/_log/matrix-digital-rain.md index f1553e1..05aaf14 100644 --- a/_log/matrix-digital-rain.md +++ b/_log/matrix-digital-rain.md @@ -54,8 +54,7 @@ blend() is still good. Leaving it alone. Tossed license and automake cruft. Just `cc -O3 main.c -o matrix` now. Don't need the ceremony. -Runs at 2-3% CPU on OpenBSD (T490). No cause to measure performance more -precisely. No regressions. Fans are quiet. +Runs at 2-3% CPU on OpenBSD (T490). No regressions. Fans are quiet. Commit: [69a888a](https://git.asciimx.com/matrix-digital-rain/commit/main.c?id=69a888a5b0bc4ef4bce4f86c1556a06f0f131fda) diff --git a/_log/search-with-cgi.md b/_log/search-with-cgi.md new file mode 100644 index 0000000..2578878 --- /dev/null +++ b/_log/search-with-cgi.md @@ -0,0 +1,95 @@ +--- +title: Site search using Perl + CGI +date: 2025-12-29 +layout: post +--- + +Need a way to search site--number of articles are growing. + +Searching site client-side using the RSS feed and JavaScript is not an option-- +bloats the feed and breaks the site for Lynx and other text browsers. + +Perl's great for text processing--especially regex work. Few lines of Perl +could do a regex search and send the result back via CGI. OpenBSD httpd speaks +CGI, Perl and slowcgi are in the base systems. No dependencies. Works on every +conceivable browser. + +Perl: traverse the directory with File::Find recursively. If search text is +found grab the file name, title and up to 50 chars of the first paragraph to +include in the search result. + +``` +find(sub { + if (open my $fh, '<', $_) { + my $content = do { local $/; <$fh> }; + close $fh; + + if ($content =~ /\Q$search_text\E/i) { + my ($title) = $content =~ /<title>(.*?)<\/title>/is; + $title ||= $File::Find::name; + my ($p_content) = $content =~ /<p[^>]*>(.*?)<\/p>/is; + my $snippet = $p_content || ""; + $snippet =~ s/<[^>]*>//g; + $snippet =~ s/\s+/ /g; + $snippet = substr($snippet, 0, 50); + $snippet .= "..." if length($p_content || "") > 50; + + push @results, { + path => $File::Find::name, + title => $title, + snippet => $snippet + }; + } + } +}, $dir); +``` + +Don't need the Perl CGI module, httpd sets QUERY_STRING for the slowcgi script: + +``` +my %params; +if ($ENV{QUERY_STRING}) { + foreach my $pair (split /&/, $ENV{QUERY_STRING}) { + my ($key, $value) = split /=/, $pair; + $value =~ tr/+/ /; + $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; + $params{$key} = $value; + } +} +``` + +Run the script as www user. Permissions: 554 (read + execute). + +Running in OpenBSD chroot: Check Perl's dynamic object dependencies: + +``` +$ ldd $(which perl) +/usr/bin/perl: + Start End Type Open Ref GrpRef Name + 000008797e8e6000 000008797e8eb000 exe 1 0 0 /usr/bin/perl + 0000087c1ffe5000 0000087c20396000 rlib 0 1 0 /usr/lib/libperl.so.26.0 + 0000087bf4508000 0000087bf4539000 rlib 0 2 0 /usr/lib/libm.so.10.1 + 0000087b9e801000 0000087b9e907000 rlib 0 2 0 /usr/lib/libc.so.102.0 + 0000087bba182000 0000087bba182000 ld.so 0 1 0 /usr/libexec/ld.so +``` + +Copy them over to chroot. Now should have /var/www/usr/bin/perl, +/usr/lib/libperl.so.26.0, and so on. + +Troubleshooting: look for issues in logs or try executing the script in chroot: + +``` +$ cat /var/log/messages | grep slowcgi +# chroot /var/www/ htdocs/path/to/script/script.cgi +``` +The last command exposes any missing Perl modules in chroot and where to find +them. Copy them over as well. + +``` +location "/cgi-bin/*" { + fastcgi socket "/run/slowcgi.sock" +} +``` + +in httpd.conf routes queries to slowcgi. + diff --git a/_site/404.html b/_site/404.html index 00df26d..6edae67 100644 --- a/_site/404.html +++ b/_site/404.html @@ -28,7 +28,12 @@ <li > <a href="/about/" class="link-decor-none">abt</a> </li> - <li><a href="/feed.xml" class="link-decor-none">rss</a></li> + <li> + <a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a> + </li> + <li> + <a href="/feed.xml" class="link-decor-none">rss</a> + </li> </ul> </div> diff --git a/_site/about/index.html b/_site/about/index.html index 804e2d8..bd7e241 100644 --- a/_site/about/index.html +++ b/_site/about/index.html @@ -28,7 +28,12 @@ <li class="active"> <a href="/about/" class="link-decor-none">abt</a> </li> - <li><a href="/feed.xml" class="link-decor-none">rss</a></li> + <li> + <a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a> + </li> + <li> + <a href="/feed.xml" class="link-decor-none">rss</a> + </li> </ul> </div> @@ -53,6 +58,7 @@ <li>log: Technical log.</li> <li>poc: Projects.</li> <li>abt: Site information.</li> + <li>sws: Search within site.</li> <li>rss: RSS (Atom) feed.</li> </ul> diff --git a/_site/assets/css/main.css b/_site/assets/css/main.css index 910ddd6..091baf3 100644 --- a/_site/assets/css/main.css +++ b/_site/assets/css/main.css @@ -237,3 +237,18 @@ pre::-webkit-scrollbar { 100% { visibility: visible; } } +#search-box { + border-radius: 0; + border-color: var(--main-fg-color); + color: var(--main-fg-color) !important; + background-color: var(--main-bg-color); + text-shadow: 0 0 1px var(--main-fg-color), 0 0 6px var(--main-fg-color); +} + +#search-btn { + border-radius: 0; + border-color: var(--main-fg-color); + color: var(--main-fg-color); + background-color: var(--main-bg-color); + text-shadow: 0 0 1px var(--main-fg-color), 0 0 6px var(--main-fg-color); +} diff --git a/_site/cgi-bin/find.cgi b/_site/cgi-bin/find.cgi new file mode 100644 index 0000000..bad12e7 --- /dev/null +++ b/_site/cgi-bin/find.cgi @@ -0,0 +1,141 @@ +#!/usr/bin/perl + +use File::Find; + +sub escape_html { + my $str = shift; + $str =~ s/&/&/g; + $str =~ s/</</g; + $str =~ s/>/>/g; + $str =~ s/"/"/g; + $str =~ s/'/'/g; + return $str; +} + +my %params; +if ($ENV{QUERY_STRING}) { + foreach my $pair (split /&/, $ENV{QUERY_STRING}) { + my ($key, $value) = split /=/, $pair; + $value =~ tr/+/ /; + $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; + $params{$key} = $value; + } +} + +my $search_text = $params{'q'} || ''; +$search_text = substr($search_text, 0, 64); +$search_text =~ s/[^a-zA-Z0-9 ]//g; + +my $directory = '../log/'; +my @results; + +my %excluded_files = ( + 'index.html' => 1, # /log/index.html +); + +if ($search_text =~ /\S/) { + find({ + wanted => sub { + # Ignore directories and only process index.html + return unless -f $_ && $_ eq 'index.html'; + + # Calculate the relative path for the URL (prevents leaking server file structure) + my $rel_path = $File::Find::name; + $rel_path =~ s|^\Q$directory\E/?||; + return if $excluded_files{$rel_path}; + + if (open my $fh, '<', $_) { + my $content = do { local $/; <$fh> }; + close $fh; + + if ($content =~ /\Q$search_text\E/i) { + + # Extract Title + my ($title) = $content =~ /<title>(.*?)<\/title>/is; + $title = $title ? escape_html($title) : $rel_path; + + # Extract the first <p> tag content + my ($p_content) = $content =~ /<p[^>]*>(.*?)<\/p>/is; + + # Process the snippet + my $snippet = $p_content || ""; + $snippet =~ s/<[^>]*>//g; # Remove internal tags + $snippet =~ s/\s+/ /g; # Collapse whitespace + + # Escape HTML entities AFTER stripping tags + # but BEFORE sending to the user to prevent XSS. + $snippet = escape_html(substr($snippet, 0, 50)); + $snippet .= "..." if length($p_content || "") > 50; + + push @results, { + path => $File::Find::name, + title => $title, + snippet => $snippet + }; + } + } + }, + no_chdir => 0, + follow => 0, + }, $directory); +} + +print "Content-Type: text/html\n\n"; + +my $list; +if ($search_text eq '') { + $list = "<p>Please enter a search term above.</p>"; +} elsif (@results == 0) { + $list = "<p>No results found for \"<b>$search_text</b>\".</p>"; +} else { + $list = "<ul>"; + foreach my $res (@results) { + my $url = $res->{path}; + $list .= "<li><a href=\"/$url\">$res->{title}</a><br><small>$res->{snippet}</small></li>"; + } + $list .= "</ul>"; +} + +my $safe_search_text = escape_html($search_text); + +print <<"HTML"; +<!DOCTYPE html> +<html lang="en-us"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>Search</title> + <link rel="stylesheet" href="/assets/css/main.css"> + <link rel="stylesheet" href="/assets/css/skeleton.css"> +</head> +<body> + <div id="nav-container" class="container"> + <ul id="navlist" class="left"> + <li><a href="/" class="link-decor-none">hme</a></li> + <li><a href="/log/" class="link-decor-none">log</a></li> + <li><a href="/projects/" class="link-decor-none">poc</a></li> + <li><a href="/about/" class="link-decor-none">abt</a></li> + <li class="active"><a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a></li> + <li><a href="/feed.xml" class="link-decor-none">rss</a></li> + </ul> + </div> + <main class="container" id="main"> + <div class="container"> + <h2>Search</h2> + <form action="" method="GET"> + <input id="search-box" type="text" name="q" value="$safe_search_text"> + <input id="search-btn" type="submit" value="Search"> + </form> + $list + </div> + </main> + <div class="footer"> + <div class="container"> + <div class="twelve columns right container-2"> + <p id="footer-text">© ASCIIMX - 2025</p> + </div> + </div> + </div> +</body> +</html> +HTML diff --git a/_site/feed.xml b/_site/feed.xml index f4bdc86..bc8a117 100644 --- a/_site/feed.xml +++ b/_site/feed.xml @@ -1 +1 @@ -<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2025-12-28T15:18:50+08:00</updated><id>/feed.xml</id><title type="html">ASCIIMX | Log</title><author><name>W. D. Sadeep Madurange</name></author><entry><title type="html">Matrix Rain: 2025 refactor</title><link href="/log/matrix-digital-rain/" rel="alternate" type="text/html" title="Matrix Rain: 2025 refactor" /><published>2025-12-21T00:00:00+08:00</published><updated>2025-12-21T00:00:00+08:00</updated><id>/log/matrix-digital-rain</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[The 2022 version worked but had some loose ends. Unicode support was inflexible–couldn’t mix ASCII with Katakana; Phosphor decay was stored in a separate array when it should’ve been packed with RGB; Code was harder to read than it needed to be.]]></summary></entry><entry><title type="html">Fingerprint door lock (LP)</title><link href="/log/fpm-door-lock-lp/" rel="alternate" type="text/html" title="Fingerprint door lock (LP)" /><published>2025-08-18T00:00:00+08:00</published><updated>2025-08-18T00:00:00+08:00</updated><id>/log/fpm-door-lock-lp</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Second iteration of the RF door lock. Old version worked but drew too much quiescent current. Sensor and servo pulled 13.8mA and 4.6mA idle. Linear regulators were a disaster. Battery didn’t last 24 hours.]]></summary></entry><entry><title type="html">High-side MOSFET switching</title><link href="/log/mosfet-switches/" rel="alternate" type="text/html" title="High-side MOSFET switching" /><published>2025-06-22T00:00:00+08:00</published><updated>2025-06-22T00:00:00+08:00</updated><id>/log/mosfet-switches</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Needed low-power switching for the fingerprint door lock. Servo and FPM draw high quiescent current–had to cut power electronically during sleep. MOSFETs can do this.]]></summary></entry><entry><title type="html">ATmega328P at 3.3V and 5V</title><link href="/log/arduino-uno/" rel="alternate" type="text/html" title="ATmega328P at 3.3V and 5V" /><published>2025-06-10T00:00:00+08:00</published><updated>2025-06-10T00:00:00+08:00</updated><id>/log/arduino-uno</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Quick reference for wiring ATmega328P ICs at 5V and 3.3V. 5V uses 16MHz crystal, 3.3V uses 8MHz.]]></summary></entry><entry><title type="html">Fingerprint door lock (RF)</title><link href="/log/fpm-door-lock-rf/" rel="alternate" type="text/html" title="Fingerprint door lock (RF)" /><published>2025-06-05T00:00:00+08:00</published><updated>2025-06-05T00:00:00+08:00</updated><id>/log/fpm-door-lock-rf</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Wanted to unlock door with fingerprint, wirelessly to avoid drilling.]]></summary></entry><entry><title type="html">Bumblebee: browser automation</title><link href="/log/bumblebee/" rel="alternate" type="text/html" title="Bumblebee: browser automation" /><published>2025-04-02T00:00:00+08:00</published><updated>2025-04-02T00:00:00+08:00</updated><id>/log/bumblebee</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Built with Andy Zhang for an employer. Tool to automate web scraping script generation.]]></summary></entry><entry><title type="html">ATSAM3X8E bare-metal programming</title><link href="/log/arduino-due/" rel="alternate" type="text/html" title="ATSAM3X8E bare-metal programming" /><published>2024-09-16T00:00:00+08:00</published><updated>2024-09-16T00:00:00+08:00</updated><id>/log/arduino-due</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Notes on programming ATSAM3X8E chips (Arduino Due) without bootloader. Tested on OpenBSD.]]></summary></entry><entry><title type="html">Etlas: e-paper dashboard</title><link href="/log/etlas/" rel="alternate" type="text/html" title="Etlas: e-paper dashboard" /><published>2024-09-05T00:00:00+08:00</published><updated>2024-09-05T00:00:00+08:00</updated><id>/log/etlas</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Repurposed e-reader prototype into something for regular use. News, stocks, weather dashboard. ESP32 NodeMCU D1 + 7.5” Waveshare e-paper + DHT22 sensor.]]></summary></entry><entry><title type="html">ESP32 e-reader prototype</title><link href="/log/e-reader/" rel="alternate" type="text/html" title="ESP32 e-reader prototype" /><published>2023-10-24T00:00:00+08:00</published><updated>2023-10-24T00:00:00+08:00</updated><id>/log/e-reader</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[First project with e-paper displays and ESP32.]]></summary></entry><entry><title type="html">Neo4j shortest path optimization</title><link href="/log/neo4j-a-star-search/" rel="alternate" type="text/html" title="Neo4j shortest path optimization" /><published>2018-03-06T00:00:00+08:00</published><updated>2018-03-06T00:00:00+08:00</updated><id>/log/neo4j-a-star-search</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Replaced Dijkstra’s for vessel route tracking in Neo4J.]]></summary></entry></feed>
\ No newline at end of file +<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2025-12-29T21:56:36+08:00</updated><id>/feed.xml</id><title type="html">ASCIIMX | Log</title><author><name>W. D. Sadeep Madurange</name></author><entry><title type="html">Site search using Perl + CGI</title><link href="/log/search-with-cgi/" rel="alternate" type="text/html" title="Site search using Perl + CGI" /><published>2025-12-29T00:00:00+08:00</published><updated>2025-12-29T00:00:00+08:00</updated><id>/log/search-with-cgi</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Need a way to search site–number of articles are growing.]]></summary></entry><entry><title type="html">Matrix Rain: 2025 refactor</title><link href="/log/matrix-digital-rain/" rel="alternate" type="text/html" title="Matrix Rain: 2025 refactor" /><published>2025-12-21T00:00:00+08:00</published><updated>2025-12-21T00:00:00+08:00</updated><id>/log/matrix-digital-rain</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[The 2022 version worked but had some loose ends. Unicode support was inflexible–couldn’t mix ASCII with Katakana; Phosphor decay was stored in a separate array when it should’ve been packed with RGB; Code was harder to read than it needed to be.]]></summary></entry><entry><title type="html">Fingerprint door lock (LP)</title><link href="/log/fpm-door-lock-lp/" rel="alternate" type="text/html" title="Fingerprint door lock (LP)" /><published>2025-08-18T00:00:00+08:00</published><updated>2025-08-18T00:00:00+08:00</updated><id>/log/fpm-door-lock-lp</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Second iteration of the RF door lock. Old version worked but drew too much quiescent current. Sensor and servo pulled 13.8mA and 4.6mA idle. Linear regulators were a disaster. Battery didn’t last 24 hours.]]></summary></entry><entry><title type="html">High-side MOSFET switching</title><link href="/log/mosfet-switches/" rel="alternate" type="text/html" title="High-side MOSFET switching" /><published>2025-06-22T00:00:00+08:00</published><updated>2025-06-22T00:00:00+08:00</updated><id>/log/mosfet-switches</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Needed low-power switching for the fingerprint door lock. Servo and FPM draw high quiescent current–had to cut power electronically during sleep. MOSFETs can do this.]]></summary></entry><entry><title type="html">ATmega328P at 3.3V and 5V</title><link href="/log/arduino-uno/" rel="alternate" type="text/html" title="ATmega328P at 3.3V and 5V" /><published>2025-06-10T00:00:00+08:00</published><updated>2025-06-10T00:00:00+08:00</updated><id>/log/arduino-uno</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Quick reference for wiring ATmega328P ICs at 5V and 3.3V. 5V uses 16MHz crystal, 3.3V uses 8MHz.]]></summary></entry><entry><title type="html">Fingerprint door lock (RF)</title><link href="/log/fpm-door-lock-rf/" rel="alternate" type="text/html" title="Fingerprint door lock (RF)" /><published>2025-06-05T00:00:00+08:00</published><updated>2025-06-05T00:00:00+08:00</updated><id>/log/fpm-door-lock-rf</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Wanted to unlock door with fingerprint, wirelessly to avoid drilling.]]></summary></entry><entry><title type="html">Bumblebee: browser automation</title><link href="/log/bumblebee/" rel="alternate" type="text/html" title="Bumblebee: browser automation" /><published>2025-04-02T00:00:00+08:00</published><updated>2025-04-02T00:00:00+08:00</updated><id>/log/bumblebee</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Built with Andy Zhang for an employer. Tool to automate web scraping script generation.]]></summary></entry><entry><title type="html">ATSAM3X8E bare-metal programming</title><link href="/log/arduino-due/" rel="alternate" type="text/html" title="ATSAM3X8E bare-metal programming" /><published>2024-09-16T00:00:00+08:00</published><updated>2024-09-16T00:00:00+08:00</updated><id>/log/arduino-due</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Notes on programming ATSAM3X8E chips (Arduino Due) without bootloader. Tested on OpenBSD.]]></summary></entry><entry><title type="html">Etlas: e-paper dashboard</title><link href="/log/etlas/" rel="alternate" type="text/html" title="Etlas: e-paper dashboard" /><published>2024-09-05T00:00:00+08:00</published><updated>2024-09-05T00:00:00+08:00</updated><id>/log/etlas</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[Repurposed e-reader prototype into something for regular use. News, stocks, weather dashboard. ESP32 NodeMCU D1 + 7.5” Waveshare e-paper + DHT22 sensor.]]></summary></entry><entry><title type="html">ESP32 e-reader prototype</title><link href="/log/e-reader/" rel="alternate" type="text/html" title="ESP32 e-reader prototype" /><published>2023-10-24T00:00:00+08:00</published><updated>2023-10-24T00:00:00+08:00</updated><id>/log/e-reader</id><author><name>W. D. Sadeep Madurange</name></author><summary type="html"><![CDATA[First project with e-paper displays and ESP32.]]></summary></entry></feed>
\ No newline at end of file diff --git a/_site/index.html b/_site/index.html index f7b6c70..79b0549 100644 --- a/_site/index.html +++ b/_site/index.html @@ -28,7 +28,12 @@ <li > <a href="/about/" class="link-decor-none">abt</a> </li> - <li><a href="/feed.xml" class="link-decor-none">rss</a></li> + <li> + <a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a> + </li> + <li> + <a href="/feed.xml" class="link-decor-none">rss</a> + </li> </ul> </div> @@ -56,6 +61,19 @@ <tr> <td class="posts-td posts-td-link"> + <a href="/log/search-with-cgi/" class="link-decor-none">Site search using Perl + CGI</a> + </td> + <td class="posts-td posts-td-time"> + <span class="post-meta"> + <time datetime="2025-12-29 00:00:00 +0800">2025-12-29</time> + </span> + </td> + </tr> + + + + <tr> + <td class="posts-td posts-td-link"> <a href="/log/matrix-digital-rain/" class="link-decor-none">Matrix Rain: 2025 refactor</a> </td> <td class="posts-td posts-td-time"> @@ -171,19 +189,6 @@ - <tr> - <td class="posts-td posts-td-link"> - <a href="/log/neo4j-a-star-search/" class="link-decor-none">Neo4j shortest path optimization</a> - </td> - <td class="posts-td posts-td-time"> - <span class="post-meta"> - <time datetime="2018-03-06 00:00:00 +0800">2018-03-06</time> - </span> - </td> - </tr> - - - </table> </div> diff --git a/_site/log/arduino-due/index.html b/_site/log/arduino-due/index.html index 0916c6f..a2135cb 100644 --- a/_site/log/arduino-due/index.html +++ b/_site/log/arduino-due/index.html @@ -32,7 +32,12 @@ <li > <a href="/about/" class="link-decor-none">abt</a> </li> - <li><a href="/feed.xml" class="link-decor-none">rss</a></li> + <li> + <a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a> + </li> + <li> + <a href="/feed.xml" class="link-decor-none">rss</a> + </li> </ul> </div> diff --git a/_site/log/arduino-uno/index.html b/_site/log/arduino-uno/index.html index 95681a4..66fff37 100644 --- a/_site/log/arduino-uno/index.html +++ b/_site/log/arduino-uno/index.html @@ -32,7 +32,12 @@ <li > <a href="/about/" class="link-decor-none">abt</a> </li> - <li><a href="/feed.xml" class="link-decor-none">rss</a></li> + <li> + <a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a> + </li> + <li> + <a href="/feed.xml" class="link-decor-none">rss</a> + </li> </ul> </div> diff --git a/_site/log/bumblebee/index.html b/_site/log/bumblebee/index.html index 0962a27..b4c9c25 100644 --- a/_site/log/bumblebee/index.html +++ b/_site/log/bumblebee/index.html @@ -32,7 +32,12 @@ <li > <a href="/about/" class="link-decor-none">abt</a> </li> - <li><a href="/feed.xml" class="link-decor-none">rss</a></li> + <li> + <a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a> + </li> + <li> + <a href="/feed.xml" class="link-decor-none">rss</a> + </li> </ul> </div> diff --git a/_site/log/e-reader/index.html b/_site/log/e-reader/index.html index 3eef05d..44ae1e6 100644 --- a/_site/log/e-reader/index.html +++ b/_site/log/e-reader/index.html @@ -32,7 +32,12 @@ <li > <a href="/about/" class="link-decor-none">abt</a> </li> - <li><a href="/feed.xml" class="link-decor-none">rss</a></li> + <li> + <a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a> + </li> + <li> + <a href="/feed.xml" class="link-decor-none">rss</a> + </li> </ul> </div> diff --git a/_site/log/etlas/index.html b/_site/log/etlas/index.html index c1b5975..8149618 100644 --- a/_site/log/etlas/index.html +++ b/_site/log/etlas/index.html @@ -32,7 +32,12 @@ <li > <a href="/about/" class="link-decor-none">abt</a> </li> - <li><a href="/feed.xml" class="link-decor-none">rss</a></li> + <li> + <a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a> + </li> + <li> + <a href="/feed.xml" class="link-decor-none">rss</a> + </li> </ul> </div> diff --git a/_site/log/fpm-door-lock-lp/index.html b/_site/log/fpm-door-lock-lp/index.html index 76715d7..a5d237c 100644 --- a/_site/log/fpm-door-lock-lp/index.html +++ b/_site/log/fpm-door-lock-lp/index.html @@ -32,7 +32,12 @@ <li > <a href="/about/" class="link-decor-none">abt</a> </li> - <li><a href="/feed.xml" class="link-decor-none">rss</a></li> + <li> + <a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a> + </li> + <li> + <a href="/feed.xml" class="link-decor-none">rss</a> + </li> </ul> </div> diff --git a/_site/log/fpm-door-lock-rf/index.html b/_site/log/fpm-door-lock-rf/index.html index 0c5037b..04c84de 100644 --- a/_site/log/fpm-door-lock-rf/index.html +++ b/_site/log/fpm-door-lock-rf/index.html @@ -32,7 +32,12 @@ <li > <a href="/about/" class="link-decor-none">abt</a> </li> - <li><a href="/feed.xml" class="link-decor-none">rss</a></li> + <li> + <a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a> + </li> + <li> + <a href="/feed.xml" class="link-decor-none">rss</a> + </li> </ul> </div> diff --git a/_site/log/index.html b/_site/log/index.html index de9c470..defcfed 100644 --- a/_site/log/index.html +++ b/_site/log/index.html @@ -28,7 +28,12 @@ <li > <a href="/about/" class="link-decor-none">abt</a> </li> - <li><a href="/feed.xml" class="link-decor-none">rss</a></li> + <li> + <a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a> + </li> + <li> + <a href="/feed.xml" class="link-decor-none">rss</a> + </li> </ul> </div> @@ -46,6 +51,19 @@ <tr> <td class="posts-td posts-td-link"> + <a href="/log/search-with-cgi/" class="link-decor-none">Site search using Perl + CGI</a> + </td> + <td class="posts-td posts-td-time"> + <span class="post-meta"> + <time datetime="2025-12-29 00:00:00 +0800">2025-12-29</time> + </span> + </td> + </tr> + + + + <tr> + <td class="posts-td posts-td-link"> <a href="/log/matrix-digital-rain/" class="link-decor-none">Matrix Rain: 2025 refactor</a> </td> <td class="posts-td posts-td-time"> diff --git a/_site/log/matrix-digital-rain/index.html b/_site/log/matrix-digital-rain/index.html index 8383776..a570230 100644 --- a/_site/log/matrix-digital-rain/index.html +++ b/_site/log/matrix-digital-rain/index.html @@ -32,7 +32,12 @@ <li > <a href="/about/" class="link-decor-none">abt</a> </li> - <li><a href="/feed.xml" class="link-decor-none">rss</a></li> + <li> + <a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a> + </li> + <li> + <a href="/feed.xml" class="link-decor-none">rss</a> + </li> </ul> </div> @@ -91,8 +96,7 @@ static inline void insert_code(matrix *mat, <p>Tossed license and automake cruft. Just <code class="language-plaintext highlighter-rouge">cc -O3 main.c -o matrix</code> now. Don’t need the ceremony.</p> -<p>Runs at 2-3% CPU on OpenBSD (T490). No cause to measure performance more -precisely. No regressions. Fans are quiet.</p> +<p>Runs at 2-3% CPU on OpenBSD (T490). No regressions. Fans are quiet.</p> <p>Commit: <a href="https://git.asciimx.com/matrix-digital-rain/commit/main.c?id=69a888a5b0bc4ef4bce4f86c1556a06f0f131fda">69a888a</a></p> diff --git a/_site/log/mosfet-switches/index.html b/_site/log/mosfet-switches/index.html index 97bba07..37d6707 100644 --- a/_site/log/mosfet-switches/index.html +++ b/_site/log/mosfet-switches/index.html @@ -32,7 +32,12 @@ <li > <a href="/about/" class="link-decor-none">abt</a> </li> - <li><a href="/feed.xml" class="link-decor-none">rss</a></li> + <li> + <a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a> + </li> + <li> + <a href="/feed.xml" class="link-decor-none">rss</a> + </li> </ul> </div> diff --git a/_site/log/neo4j-a-star-search/index.html b/_site/log/neo4j-a-star-search/index.html index a00dd6c..013cd95 100644 --- a/_site/log/neo4j-a-star-search/index.html +++ b/_site/log/neo4j-a-star-search/index.html @@ -32,7 +32,12 @@ <li > <a href="/about/" class="link-decor-none">abt</a> </li> - <li><a href="/feed.xml" class="link-decor-none">rss</a></li> + <li> + <a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a> + </li> + <li> + <a href="/feed.xml" class="link-decor-none">rss</a> + </li> </ul> </div> diff --git a/_site/log/search-with-cgi/index.html b/_site/log/search-with-cgi/index.html new file mode 100644 index 0000000..060a282 --- /dev/null +++ b/_site/log/search-with-cgi/index.html @@ -0,0 +1,152 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>Site search using Perl + CGI</title> + + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>Site search using Perl + CGI</title> + <link rel="stylesheet" href="/assets/css/main.css"> + <link rel="stylesheet" href="/assets/css/skeleton.css"> +</head> + + + + </head> + <body> + + <div id="nav-container" class="container"> + <ul id="navlist" class="left"> + + <li > + <a href="/" class="link-decor-none">hme</a> + </li> + <li class="active"> + <a href="/log/" class="link-decor-none">log</a> + </li> + <li > + <a href="/projects/" class="link-decor-none">poc</a> + </li> + <li > + <a href="/about/" class="link-decor-none">abt</a> + </li> + <li> + <a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a> + </li> + <li> + <a href="/feed.xml" class="link-decor-none">rss</a> + </li> + </ul> +</div> + + + + <main> + <div class="container"> + <div class="container-2"> + <h2 class="center" id="title">SITE SEARCH USING PERL + CGI</h2> + <h6 class="center">29 DECEMBER 2025</h5> + <br> + <div class="twocol justify"><p>Need a way to search site–number of articles are growing.</p> + +<p>Searching site client-side using the RSS feed and JavaScript is not an option– +bloats the feed and breaks the site for Lynx and other text browsers.</p> + +<p>Perl’s great for text processing–especially regex work. Few lines of Perl +could do a regex search and send the result back via CGI. OpenBSD httpd speaks +CGI, Perl and slowcgi are in the base systems. No dependencies. Works on every +conceivable browser.</p> + +<p>Perl: traverse the directory with File::Find recursively. If search text is +found grab the file name, title and up to 50 chars of the first paragraph to +include in the search result.</p> + +<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find(sub { + if (open my $fh, '<', $_) { + my $content = do { local $/; <$fh> }; + close $fh; + + if ($content =~ /\Q$search_text\E/i) { + my ($title) = $content =~ /<title>(.*?)<\/title>/is; + $title ||= $File::Find::name; + my ($p_content) = $content =~ /<p[^>]*>(.*?)<\/p>/is; + my $snippet = $p_content || ""; + $snippet =~ s/<[^>]*>//g; + $snippet =~ s/\s+/ /g; + $snippet = substr($snippet, 0, 50); + $snippet .= "..." if length($p_content || "") > 50; + + push @results, { + path => $File::Find::name, + title => $title, + snippet => $snippet + }; + } + } +}, $dir); +</code></pre></div></div> + +<p>Don’t need the Perl CGI module, httpd sets QUERY_STRING for the slowcgi script:</p> + +<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>my %params; +if ($ENV{QUERY_STRING}) { + foreach my $pair (split /&/, $ENV{QUERY_STRING}) { + my ($key, $value) = split /=/, $pair; + $value =~ tr/+/ /; + $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; + $params{$key} = $value; + } +} +</code></pre></div></div> + +<p>Run the script as www user. Permissions: 554 (read + execute).</p> + +<p>Running in OpenBSD chroot: Check Perl’s dynamic object dependencies:</p> + +<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ldd $(which perl) +/usr/bin/perl: + Start End Type Open Ref GrpRef Name + 000008797e8e6000 000008797e8eb000 exe 1 0 0 /usr/bin/perl + 0000087c1ffe5000 0000087c20396000 rlib 0 1 0 /usr/lib/libperl.so.26.0 + 0000087bf4508000 0000087bf4539000 rlib 0 2 0 /usr/lib/libm.so.10.1 + 0000087b9e801000 0000087b9e907000 rlib 0 2 0 /usr/lib/libc.so.102.0 + 0000087bba182000 0000087bba182000 ld.so 0 1 0 /usr/libexec/ld.so +</code></pre></div></div> + +<p>Copy them over to chroot. Now should have /var/www/usr/bin/perl, +/usr/lib/libperl.so.26.0, and so on.</p> + +<p>Troubleshooting: look for issues in logs or try executing the script in chroot:</p> + +<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cat /var/log/messages | grep slowcgi +# chroot /var/www/ htdocs/path/to/script/script.cgi +</code></pre></div></div> +<p>The last command exposes any missing Perl modules in chroot and where to find +them. Copy them over as well.</p> + +<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>location "/cgi-bin/*" { + fastcgi socket "/run/slowcgi.sock" +} +</code></pre></div></div> + +<p>in httpd.conf routes queries to slowcgi.</p> + +</div> + <p class="post-author right">by W. D. Sadeep Madurange</p> + </div> + </div> + </main> + + <div class="footer"> + <div class="container"> + <div class="twelve columns right container-2"> + <p id="footer-text">© ASCIIMX - 2025</p> + </div> + </div> +</div> + + + </body> +</html> diff --git a/_site/posts.xml b/_site/posts.xml index 8f7399b..66974e6 100644 --- a/_site/posts.xml +++ b/_site/posts.xml @@ -1 +1 @@ -<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="/posts.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2025-12-28T15:18:50+08:00</updated><id>/posts.xml</id><title type="html">ASCIIMX</title><author><name>W. D. Sadeep Madurange</name></author></feed>
\ No newline at end of file +<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="/posts.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2025-12-29T21:56:36+08:00</updated><id>/posts.xml</id><title type="html">ASCIIMX</title><author><name>W. D. Sadeep Madurange</name></author></feed>
\ No newline at end of file diff --git a/_site/projects/index.html b/_site/projects/index.html index 71a41e6..2357793 100644 --- a/_site/projects/index.html +++ b/_site/projects/index.html @@ -28,7 +28,12 @@ <li > <a href="/about/" class="link-decor-none">abt</a> </li> - <li><a href="/feed.xml" class="link-decor-none">rss</a></li> + <li> + <a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a> + </li> + <li> + <a href="/feed.xml" class="link-decor-none">rss</a> + </li> </ul> </div> diff --git a/_site/sitemap.xml b/_site/sitemap.xml index b799547..93b39c0 100644 --- a/_site/sitemap.xml +++ b/_site/sitemap.xml @@ -41,6 +41,10 @@ <lastmod>2025-12-21T00:00:00+08:00</lastmod> </url> <url> +<loc>/log/search-with-cgi/</loc> +<lastmod>2025-12-29T00:00:00+08:00</lastmod> +</url> +<url> <loc>/about/</loc> </url> <url> @@ -22,6 +22,7 @@ title: About <li>log: Technical log.</li> <li>poc: Projects.</li> <li>abt: Site information.</li> + <li>sws: Search within site.</li> <li>rss: RSS (Atom) feed.</li> </ul> diff --git a/assets/css/main.css b/assets/css/main.css index 910ddd6..091baf3 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -237,3 +237,18 @@ pre::-webkit-scrollbar { 100% { visibility: visible; } } +#search-box { + border-radius: 0; + border-color: var(--main-fg-color); + color: var(--main-fg-color) !important; + background-color: var(--main-bg-color); + text-shadow: 0 0 1px var(--main-fg-color), 0 0 6px var(--main-fg-color); +} + +#search-btn { + border-radius: 0; + border-color: var(--main-fg-color); + color: var(--main-fg-color); + background-color: var(--main-bg-color); + text-shadow: 0 0 1px var(--main-fg-color), 0 0 6px var(--main-fg-color); +} diff --git a/cgi-bin/find.cgi b/cgi-bin/find.cgi new file mode 100644 index 0000000..bad12e7 --- /dev/null +++ b/cgi-bin/find.cgi @@ -0,0 +1,141 @@ +#!/usr/bin/perl + +use File::Find; + +sub escape_html { + my $str = shift; + $str =~ s/&/&/g; + $str =~ s/</</g; + $str =~ s/>/>/g; + $str =~ s/"/"/g; + $str =~ s/'/'/g; + return $str; +} + +my %params; +if ($ENV{QUERY_STRING}) { + foreach my $pair (split /&/, $ENV{QUERY_STRING}) { + my ($key, $value) = split /=/, $pair; + $value =~ tr/+/ /; + $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; + $params{$key} = $value; + } +} + +my $search_text = $params{'q'} || ''; +$search_text = substr($search_text, 0, 64); +$search_text =~ s/[^a-zA-Z0-9 ]//g; + +my $directory = '../log/'; +my @results; + +my %excluded_files = ( + 'index.html' => 1, # /log/index.html +); + +if ($search_text =~ /\S/) { + find({ + wanted => sub { + # Ignore directories and only process index.html + return unless -f $_ && $_ eq 'index.html'; + + # Calculate the relative path for the URL (prevents leaking server file structure) + my $rel_path = $File::Find::name; + $rel_path =~ s|^\Q$directory\E/?||; + return if $excluded_files{$rel_path}; + + if (open my $fh, '<', $_) { + my $content = do { local $/; <$fh> }; + close $fh; + + if ($content =~ /\Q$search_text\E/i) { + + # Extract Title + my ($title) = $content =~ /<title>(.*?)<\/title>/is; + $title = $title ? escape_html($title) : $rel_path; + + # Extract the first <p> tag content + my ($p_content) = $content =~ /<p[^>]*>(.*?)<\/p>/is; + + # Process the snippet + my $snippet = $p_content || ""; + $snippet =~ s/<[^>]*>//g; # Remove internal tags + $snippet =~ s/\s+/ /g; # Collapse whitespace + + # Escape HTML entities AFTER stripping tags + # but BEFORE sending to the user to prevent XSS. + $snippet = escape_html(substr($snippet, 0, 50)); + $snippet .= "..." if length($p_content || "") > 50; + + push @results, { + path => $File::Find::name, + title => $title, + snippet => $snippet + }; + } + } + }, + no_chdir => 0, + follow => 0, + }, $directory); +} + +print "Content-Type: text/html\n\n"; + +my $list; +if ($search_text eq '') { + $list = "<p>Please enter a search term above.</p>"; +} elsif (@results == 0) { + $list = "<p>No results found for \"<b>$search_text</b>\".</p>"; +} else { + $list = "<ul>"; + foreach my $res (@results) { + my $url = $res->{path}; + $list .= "<li><a href=\"/$url\">$res->{title}</a><br><small>$res->{snippet}</small></li>"; + } + $list .= "</ul>"; +} + +my $safe_search_text = escape_html($search_text); + +print <<"HTML"; +<!DOCTYPE html> +<html lang="en-us"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>Search</title> + <link rel="stylesheet" href="/assets/css/main.css"> + <link rel="stylesheet" href="/assets/css/skeleton.css"> +</head> +<body> + <div id="nav-container" class="container"> + <ul id="navlist" class="left"> + <li><a href="/" class="link-decor-none">hme</a></li> + <li><a href="/log/" class="link-decor-none">log</a></li> + <li><a href="/projects/" class="link-decor-none">poc</a></li> + <li><a href="/about/" class="link-decor-none">abt</a></li> + <li class="active"><a href="/cgi-bin/find.cgi" class="link-decor-none">sws</a></li> + <li><a href="/feed.xml" class="link-decor-none">rss</a></li> + </ul> + </div> + <main class="container" id="main"> + <div class="container"> + <h2>Search</h2> + <form action="" method="GET"> + <input id="search-box" type="text" name="q" value="$safe_search_text"> + <input id="search-btn" type="submit" value="Search"> + </form> + $list + </div> + </main> + <div class="footer"> + <div class="container"> + <div class="twelve columns right container-2"> + <p id="footer-text">© ASCIIMX - 2025</p> + </div> + </div> + </div> +</body> +</html> +HTML |
