Simple Search

Website Keyword Search Engine Script

Simple Search allows you to integrate a keyword and boolean search program into your website. Users can search specified text and HTML documents, and the script returns a list of matching files displayed by their title tags. Perfect for small to medium-sized sites without database-driven search.

Free Download
Version 1.0
Perl CGI

Modern Search Solutions (2024)

Site search has evolved dramatically. Today's solutions offer typo tolerance, instant results, faceting, and AI-powered relevance.

Open Source (Self-Hosted Free)

Meilisearch

Lightning fast (<50ms), written in Rust. Typo-tolerant, instant search. Great documentation. Inspired by Algolia but open source.

Open Source Self-Hosted Free Cloud from $0
Typesense

Written in C++, blazing fast. Typo correction, tunable ranking, faceting. Battle-tested since 2015. Great Algolia alternative.

Open Source Self-Hosted Free Cloud from $19/mo
OpenSearch

Fork of Elasticsearch (Apache 2.0). Full-text search, analytics, dashboards. Enterprise-grade, AWS-backed.

Open Source Apache 2.0

Client-Side (Static Sites)

Pagefind

Fully static search for static sites. Auto-indexes at build time. 10KB JS, no server needed. Perfect for Hugo, Jekyll, Astro.

Free Static Sites 10KB
Lunr.js

Client-side search with pre-built index. Stemming, boosting, field search. No server required, works offline.

Free MIT ~8KB
Fuse.js

Lightweight fuzzy search. No dependencies, works in browser and Node. Perfect for small datasets (<10K items).

Free Apache 2.0 ~4KB

SaaS / Managed Solutions

Algolia

Industry leader. Used by Stripe, Twitch, Slack. Instant, typo-tolerant. Enterprise pricing.

Free 10K searches/mo Paid from $1/1K
Elastic Cloud

Managed Elasticsearch. Full-text, analytics, APM. Enterprise features, global infrastructure.

From $95/mo
Algolia DocSearch

Free for open source documentation. Auto-crawls your docs. Used by React, Vue, Bootstrap docs.

Free for OSS

Search Solutions Comparison

Solution Type Best For Typo Tolerance Self-Hosted Cost
Simple Search (1990s)CGIHistorical interestNoYesFree
MeilisearchServerApps, e-commerceYesYesFree / Cloud
TypesenseServerApps, high trafficYesYesFree / $19+
PagefindStaticStatic sites, docsBasicYesFree
Lunr.jsClient-sideSmall static sitesBasicYesFree
AlgoliaSaaSEnterprise, e-commerceYesNo$$-$$$

Overview

Simple Search is a lightweight Perl CGI script that provides search functionality for static HTML and text files. It reads through your specified documents, searches for keywords, and returns results with links to matching pages.

How It Works

The script scans through files in specified directories, looking for keyword matches. When a match is found, it extracts the page title from the HTML <title> tag and creates a clickable link in the results. Boolean operators (AND, OR) can combine multiple keywords.

Package Contents
File Description
search.pl Main Perl script that performs the search and displays results
search.html HTML form template for the search interface
README Installation instructions and configuration guide
Search Demo

Features

Keyword Search

Search for single keywords or phrases across all specified documents on your website.

Boolean Operators

Support for AND/OR operators to combine multiple keywords for refined search results.

Title Extraction

Automatically extracts page titles from HTML documents to display meaningful result links.

Multiple Directories

Configure multiple directories to search, allowing you to cover your entire site structure.

Text & HTML Support

Search both plain text (.txt) and HTML files (.html, .htm) for maximum coverage.

Customizable Output

Modify the results page template to match your website's design and branding.

Search Options

Option Description Example
Single Keyword Search for a single word in all documents perl
Multiple Keywords (AND) Find documents containing ALL specified keywords perl cgi script
Multiple Keywords (OR) Find documents containing ANY of the keywords perl OR php OR python
Case Insensitive Searches are case insensitive by default PERL = perl = Perl

Installation

  1. Upload the Script
    Upload search.pl to your cgi-bin directory.
  2. Configure Perl Path
    Edit the first line to point to your Perl interpreter (usually #!/usr/bin/perl).
  3. Set Search Directories
    Configure the @directories array with the paths to directories you want to search.
  4. Set Base URL
    Configure the $baseurl variable to match your website's URL structure.
  5. Set Permissions
    Set the script permissions to 755: chmod 755 search.pl
  6. Create Search Form
    Upload or customize search.html to create your search interface.

Code Examples

Search Form HTML
<!DOCTYPE html>
<html>
<head>
    <title>Search Our Site</title>
</head>
<body>
    <h1>Search</h1>

    <form action="/cgi-bin/search.pl" method="GET">
        <p>
            <label for="keywords">Enter Keywords:</label><br>
            <input type="text" name="keywords" id="keywords" size="40">
        </p>

        <p>
            Search Type:<br>
            <input type="radio" name="boolean" value="AND" id="and" checked>
            <label for="and">Match ALL keywords (AND)</label><br>

            <input type="radio" name="boolean" value="OR" id="or">
            <label for="or">Match ANY keyword (OR)</label>
        </p>

        <p>
            <input type="submit" value="Search">
            <input type="reset" value="Clear">
        </p>
    </form>

    <!-- Bootstrap 5 version -->
    <form action="/cgi-bin/search.pl" method="GET" class="needs-validation">
        <div class="mb-3">
            <label for="keywords" class="form-label">Enter Keywords</label>
            <input type="text" class="form-control" name="keywords" id="keywords"
                   placeholder="Search..." required>
        </div>

        <div class="mb-3">
            <label class="form-label">Search Type</label>
            <div class="form-check">
                <input class="form-check-input" type="radio" name="boolean"
                       value="AND" id="and" checked>
                <label class="form-check-label" for="and">
                    Match ALL keywords (AND)
                </label>
            </div>
            <div class="form-check">
                <input class="form-check-input" type="radio" name="boolean"
                       value="OR" id="or">
                <label class="form-check-label" for="or">
                    Match ANY keyword (OR)
                </label>
            </div>
        </div>

        <button type="submit" class="btn btn-primary">
            <i class="bi bi-search"></i> Search
        </button>
    </form>
</body>
</html>
Perl Implementation
#!/usr/bin/perl
use strict;
use warnings;
use CGI;
use File::Find;

my $cgi = CGI->new;

# Configuration
my @directories = ('/var/www/html/docs', '/var/www/html/pages');
my $baseurl = 'http://example.com';
my @extensions = qw(html htm txt);

# Get search parameters
my $keywords = $cgi->param('keywords') || '';
my $boolean = $cgi->param('boolean') || 'AND';

# Security: sanitize input
$keywords =~ s/[^\w\s]//g;
my @terms = split(/\s+/, lc($keywords));

# Output HTML header
print $cgi->header('text/html');
print $cgi->start_html('Search Results');
print "

Search Results

\n"; if (!@terms) { print "

Please enter search keywords.

\n"; print $cgi->end_html; exit; } print "

Searching for: $keywords ($boolean)

\n"; # Search files my @results; find(sub { return unless -f; my $file = $File::Find::name; # Check extension my ($ext) = $file =~ /\.(\w+)$/; return unless $ext && grep { $_ eq lc($ext) } @extensions; # Read file content open(my $fh, '<', $file) or return; my $content = do { local $/; <$fh> }; close($fh); $content = lc($content); # Check for matches my $match = 0; if ($boolean eq 'AND') { $match = 1; for my $term (@terms) { unless ($content =~ /\b\Q$term\E\b/i) { $match = 0; last; } } } else { # OR for my $term (@terms) { if ($content =~ /\b\Q$term\E\b/i) { $match = 1; last; } } } if ($match) { # Extract title my ($title) = $content =~ /([^<]+)<\/title>/i; $title ||= $file; # Convert path to URL my $url = $file; $url =~ s{^/var/www/html}{$baseurl}; push @results, { title => $title, url => $url }; } }, @directories); # Display results if (@results) { print "<p>Found " . scalar(@results) . " result(s):</p>\n"; print "<ul>\n"; for my $result (@results) { print qq{<li><a href="$result->{url}">$result->{title}</a></li>\n}; } print "</ul>\n"; } else { print "<p>No results found.</p>\n"; } print $cgi->end_html; exit 0;</code></pre> </div> <div class="tab-pane fade" id="php" role="tabpanel"> <h5>PHP Implementation</h5> <pre><code class="language-php"><?php /** * Simple Search - PHP Version */ // Configuration $config = [ 'directories' => [ '/var/www/html/docs', '/var/www/html/pages' ], 'baseurl' => 'https://example.com', 'extensions' => ['html', 'htm', 'txt', 'php'], 'max_results' => 100 ]; // Get search parameters $keywords = $_GET['keywords'] ?? ''; $boolean = $_GET['boolean'] ?? 'AND'; // Security: sanitize input $keywords = preg_replace('/[^\w\s]/u', '', $keywords); $terms = array_filter(explode(' ', strtolower($keywords))); function searchFiles($directories, $extensions, $baseurl) { $files = []; foreach ($directories as $dir) { if (!is_dir($dir)) continue; $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($dir) ); foreach ($iterator as $file) { if (!$file->isFile()) continue; $ext = strtolower($file->getExtension()); if (!in_array($ext, $extensions)) continue; $files[] = [ 'path' => $file->getPathname(), 'url' => str_replace('/var/www/html', $baseurl, $file->getPathname()) ]; } } return $files; } function extractTitle($content, $fallback) { if (preg_match('/<title>([^<]+)<\/title>/i', $content, $matches)) { return htmlspecialchars($matches[1]); } return htmlspecialchars(basename($fallback)); } function matchesSearch($content, $terms, $boolean) { $content = strtolower($content); if ($boolean === 'AND') { foreach ($terms as $term) { if (stripos($content, $term) === false) { return false; } } return true; } else { // OR foreach ($terms as $term) { if (stripos($content, $term) !== false) { return true; } } return false; } } // Perform search $results = []; if (!empty($terms)) { $files = searchFiles( $config['directories'], $config['extensions'], $config['baseurl'] ); foreach ($files as $file) { $content = @file_get_contents($file['path']); if ($content === false) continue; if (matchesSearch($content, $terms, $boolean)) { $results[] = [ 'title' => extractTitle($content, $file['path']), 'url' => $file['url'] ]; if (count($results) >= $config['max_results']) { break; } } } } ?> <!DOCTYPE html> <html> <head> <title>Search Results</title> </head> <body> <h1>Search Results</h1> <?php if (empty($terms)): ?> <p>Please enter search keywords.</p> <?php else: ?> <p>Searching for: <strong><?= htmlspecialchars($keywords) ?></strong> (<?= $boolean ?>)</p> <?php if (!empty($results)): ?> <p>Found <?= count($results) ?> result(s):</p> <ul> <?php foreach ($results as $result): ?> <li> <a href="<?= htmlspecialchars($result['url']) ?>"> <?= $result['title'] ?> </a> </li> <?php endforeach; ?> </ul> <?php else: ?> <p>No results found.</p> <?php endif; ?> <?php endif; ?> </body> </html></code></pre> </div> <div class="tab-pane fade" id="js" role="tabpanel"> <h5>JavaScript Client-Side Search</h5> <pre><code class="language-javascript">/** * Simple Search - JavaScript Version * Client-side search for static sites (requires pre-built index) */ class SimpleSearch { constructor(options = {}) { this.options = { indexUrl: '/search-index.json', inputSelector: '#search-input', resultsSelector: '#search-results', minChars: 2, maxResults: 20, highlightMatches: true, ...options }; this.index = []; this.init(); } async init() { await this.loadIndex(); this.bindEvents(); } async loadIndex() { try { const response = await fetch(this.options.indexUrl); this.index = await response.json(); } catch (error) { console.error('Failed to load search index:', error); } } bindEvents() { const input = document.querySelector(this.options.inputSelector); if (input) { input.addEventListener('input', (e) => this.handleSearch(e.target.value)); // Handle form submission input.closest('form')?.addEventListener('submit', (e) => { e.preventDefault(); this.handleSearch(input.value); }); } } handleSearch(query) { if (query.length < this.options.minChars) { this.displayResults([]); return; } const results = this.search(query); this.displayResults(results, query); } search(query) { const terms = query.toLowerCase().split(/\s+/).filter(t => t.length > 0); if (terms.length === 0) return []; return this.index .map(item => ({ ...item, score: this.calculateScore(item, terms) })) .filter(item => item.score > 0) .sort((a, b) => b.score - a.score) .slice(0, this.options.maxResults); } calculateScore(item, terms) { let score = 0; const titleLower = item.title.toLowerCase(); const contentLower = (item.content || '').toLowerCase(); for (const term of terms) { // Title matches are worth more if (titleLower.includes(term)) { score += 10; // Exact word match in title if (new RegExp(`\\b${term}\\b`).test(titleLower)) { score += 5; } } // Content matches const contentMatches = (contentLower.match(new RegExp(term, 'g')) || []).length; score += Math.min(contentMatches, 5); // Cap at 5 points per term } return score; } displayResults(results, query = '') { const container = document.querySelector(this.options.resultsSelector); if (!container) return; if (results.length === 0) { container.innerHTML = query.length >= this.options.minChars ? '<p class="text-muted">No results found.</p>' : ''; return; } const html = results.map(result => { let title = result.title; let snippet = result.snippet || ''; if (this.options.highlightMatches && query) { const terms = query.split(/\s+/); terms.forEach(term => { const regex = new RegExp(`(${term})`, 'gi'); title = title.replace(regex, '<mark>$1</mark>'); snippet = snippet.replace(regex, '<mark>$1</mark>'); }); } return ` <div class="search-result mb-3"> <h5><a href="${result.url}">${title}</a></h5> <p class="text-muted small mb-1">${result.url}</p> <p class="mb-0">${snippet}</p> </div> `; }).join(''); container.innerHTML = ` <p class="text-muted">Found ${results.length} result(s):</p> ${html} `; } } // Usage const search = new SimpleSearch({ indexUrl: '/search-index.json', inputSelector: '#search-input', resultsSelector: '#search-results' }); // Building a search index (Node.js build script example) /* const fs = require('fs'); const path = require('path'); const cheerio = require('cheerio'); function buildIndex(directory) { const index = []; const files = walkDir(directory); files.forEach(file => { if (!file.endsWith('.html')) return; const content = fs.readFileSync(file, 'utf-8'); const $ = cheerio.load(content); index.push({ url: file.replace(directory, ''), title: $('title').text() || path.basename(file), content: $('body').text().replace(/\s+/g, ' ').slice(0, 500), snippet: $('meta[name="description"]').attr('content') || '' }); }); return index; } fs.writeFileSync('search-index.json', JSON.stringify(buildIndex('./public'))); */</code></pre> </div> </div> </section> <!-- Download Section --> <section id="download" class="mb-5"> <h2 class="border-bottom pb-2 mb-4"><i class="bi bi-download"></i> Download</h2> <div class="row g-4"> <div class="col-md-6"> <div class="card h-100"> <div class="card-header bg-primary text-white"> <i class="bi bi-file-earmark-zip"></i> Compressed Archives </div> <div class="card-body"> <ul class="list-group list-group-flush"> <li class="list-group-item d-flex justify-content-between align-items-center"> <span><i class="bi bi-file-earmark-zip"></i> search.tar.gz</span> <span class="badge bg-secondary">3.9 KB</span> </li> <li class="list-group-item d-flex justify-content-between align-items-center"> <span><i class="bi bi-file-earmark-zip"></i> search.zip</span> <span class="badge bg-secondary">4.3 KB</span> </li> <li class="list-group-item d-flex justify-content-between align-items-center"> <span><i class="bi bi-file-earmark-zip"></i> search.tar.Z</span> <span class="badge bg-secondary">5.8 KB</span> </li> <li class="list-group-item d-flex justify-content-between align-items-center"> <span><i class="bi bi-file-earmark"></i> search.tar</span> <span class="badge bg-secondary">20.5 KB</span> </li> </ul> </div> </div> </div> <div class="col-md-6"> <div class="card h-100"> <div class="card-header bg-secondary text-white"> <i class="bi bi-file-code"></i> Individual Files </div> <div class="card-body"> <ul class="list-group list-group-flush"> <li class="list-group-item"> <strong><i class="bi bi-file-code"></i> search.pl</strong> <p class="mb-0 small text-muted">Main Perl script that performs keyword searches</p> </li> <li class="list-group-item"> <strong><i class="bi bi-filetype-html"></i> search.html</strong> <p class="mb-0 small text-muted">HTML form template for the search interface</p> </li> <li class="list-group-item"> <strong><i class="bi bi-file-text"></i> README</strong> <p class="mb-0 small text-muted">Installation instructions and configuration guide</p> </li> </ul> </div> </div> </div> </div> <div class="mt-4"> <a href="/scripts/downloads/search/" class="btn btn-primary btn-lg"><i class="bi bi-download"></i> Go to Downloads</a> </div> </section> <!-- Extras Section --> <section id="extras" class="mb-5"> <h2 class="border-bottom pb-2 mb-4"><i class="bi bi-plus-circle"></i> Extras</h2> <p>Community-contributed enhancements and localizations:</p> <div class="row g-4"> <div class="col-md-6"> <div class="card h-100"> <div class="card-body"> <h5 class="card-title"><i class="bi bi-bug text-warning"></i> Simple Search with Debug</h5> <p class="card-text">A modified version with built-in debugging options to help troubleshoot search issues. Created by the MSA help list community.</p> <span class="badge bg-secondary">Community Contribution</span> </div> </div> </div> <div class="col-md-6"> <div class="card h-100"> <div class="card-body"> <h5 class="card-title"><i class="bi bi-translate text-info"></i> Simple Search in Japanese</h5> <p class="card-text">A localized version modified to search Japanese character sets (Shift-JIS, EUC-JP). Demonstrates internationalization of the script.</p> <span class="badge bg-secondary">Localization</span> </div> </div> </div> </div> </section> <!-- FAQ Section --> <section id="faq" class="mb-5"> <h2 class="border-bottom pb-2 mb-4"><i class="bi bi-question-circle"></i> Frequently Asked Questions</h2> <div class="accordion" id="faqAccordion"> <div class="accordion-item"> <h2 class="accordion-header"> <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#faq1"> How can I search recursively down directories? </button> </h2> <div id="faq1" class="accordion-collapse collapse show" data-bs-parent="#faqAccordion"> <div class="accordion-body"> The original script searches only the directories you specify. To enable recursive searching, you can modify the script to use Perl's File::Find module (as shown in the code examples), or list all subdirectories explicitly in the configuration. The File::Find approach automatically traverses all nested directories. </div> </div> </div> <div class="accordion-item"> <h2 class="accordion-header"> <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#faq2"> How many files can this program search through? </button> </h2> <div id="faq2" class="accordion-collapse collapse" data-bs-parent="#faqAccordion"> <div class="accordion-body"> Simple Search reads files on each request, so performance depends on your server and the number/size of files. For small sites (under 100 files), it works well. For larger sites, consider: (1) Building a search index file that's updated periodically, (2) Using a dedicated search solution like Elasticsearch or Algolia, (3) Limiting the directories searched. </div> </div> </div> <div class="accordion-item"> <h2 class="accordion-header"> <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#faq3"> Can I exclude certain files or directories from search? </button> </h2> <div id="faq3" class="accordion-collapse collapse" data-bs-parent="#faqAccordion"> <div class="accordion-body"> Yes, you can add exclusion patterns to the script. Create an array of patterns to skip (like <code>@exclude = ('admin', 'private', '*.bak');</code>) and check each file against these patterns before searching. This is useful for excluding administrative pages, backup files, or development directories. </div> </div> </div> <div class="accordion-item"> <h2 class="accordion-header"> <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#faq4"> How do I display snippets with highlighted keywords? </button> </h2> <div id="faq4" class="accordion-collapse collapse" data-bs-parent="#faqAccordion"> <div class="accordion-body"> Modify the script to extract a portion of text around the matched keyword and wrap the keyword in <code><mark></code> or <code><strong></code> tags. You'll need to find the position of the keyword in the content, extract surrounding text (50-100 characters before and after), and apply the highlighting. </div> </div> </div> <div class="accordion-item"> <h2 class="accordion-header"> <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#faq5"> Can I search PDF or Word documents? </button> </h2> <div id="faq5" class="accordion-collapse collapse" data-bs-parent="#faqAccordion"> <div class="accordion-body"> The original script only searches plain text and HTML. To search PDFs, you'd need to extract text using tools like pdftotext. For Word documents, you'd need to convert them or use specialized libraries. Consider using a dedicated document search solution for sites with many binary formats. </div> </div> </div> <div class="accordion-item"> <h2 class="accordion-header"> <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#faq6"> How can I make the search case-sensitive? </button> </h2> <div id="faq6" class="accordion-collapse collapse" data-bs-parent="#faqAccordion"> <div class="accordion-body"> By default, Simple Search is case-insensitive. To make it case-sensitive, remove the <code>lc()</code> functions that convert text to lowercase before comparison. You can also add a checkbox to the search form letting users choose case sensitivity, then check that parameter in the script. </div> </div> </div> <div class="accordion-item"> <h2 class="accordion-header"> <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#faq7"> Is there a modern alternative to this script? </button> </h2> <div id="faq7" class="accordion-collapse collapse" data-bs-parent="#faqAccordion"> <div class="accordion-body"> Yes, many modern alternatives exist: (1) Static site generators often include search plugins (Hugo, Jekyll), (2) JavaScript libraries like Lunr.js or Fuse.js for client-side search, (3) Cloud services like Algolia or Elasticsearch for larger sites, (4) CMS built-in search (WordPress, Drupal). For simple sites, client-side JavaScript search with a pre-built index often works well. </div> </div> </div> <div class="accordion-item"> <h2 class="accordion-header"> <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#faq8"> How do I add pagination to search results? </button> </h2> <div id="faq8" class="accordion-collapse collapse" data-bs-parent="#faqAccordion"> <div class="accordion-body"> Add a <code>page</code> parameter to the query string and modify the script to: (1) Count total results, (2) Calculate offset based on page number and results per page, (3) Slice the results array to show only the current page, (4) Generate pagination links with the search query and page numbers. Example: <code>?keywords=perl&page=2</code>. </div> </div> </div> </div> </section> <!-- Related Scripts Section --> <section id="related" class="mb-5"> <h2 class="border-bottom pb-2 mb-4"><i class="bi bi-collection"></i> Related Scripts</h2> <div class="row g-4"> <div class="col-md-4"> <div class="card h-100"> <div class="card-body"> <h5 class="card-title"><i class="bi bi-envelope text-primary"></i> FormMail</h5> <p class="card-text">Process web form submissions and send results via email.</p> <a href="/scripts/formmail.shtml" class="btn btn-outline-primary btn-sm">View Script</a> </div> </div> </div> <div class="col-md-4"> <div class="card h-100"> <div class="card-body"> <h5 class="card-title"><i class="bi bi-book text-primary"></i> Guestbook</h5> <p class="card-text">Allow visitors to leave messages and comments on your site.</p> <a href="/scripts/guestbook.shtml" class="btn btn-outline-primary btn-sm">View Script</a> </div> </div> </div> <div class="col-md-4"> <div class="card h-100"> <div class="card-body"> <h5 class="card-title"><i class="bi bi-chat-dots text-primary"></i> WWWBoard</h5> <p class="card-text">Threaded discussion forum for community interaction.</p> <a href="/scripts/wwwboard.shtml" class="btn btn-outline-primary btn-sm">View Script</a> </div> </div> </div> </div> </section> <!-- FAQPage Schema --> <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [ { "@type": "Question", "name": "How can I search recursively down directories?", "acceptedAnswer": { "@type": "Answer", "text": "Modify the script to use Perl's File::Find module for automatic recursive directory traversal, or list all subdirectories explicitly in the configuration." } }, { "@type": "Question", "name": "How many files can this program search through?", "acceptedAnswer": { "@type": "Answer", "text": "Simple Search reads files on each request. For small sites under 100 files it works well. For larger sites, consider building a search index or using dedicated solutions like Elasticsearch." } }, { "@type": "Question", "name": "Can I exclude certain files or directories from search?", "acceptedAnswer": { "@type": "Answer", "text": "Yes, add exclusion patterns to the script. Create an array of patterns to skip and check each file against these patterns before searching." } }, { "@type": "Question", "name": "How do I display snippets with highlighted keywords?", "acceptedAnswer": { "@type": "Answer", "text": "Modify the script to extract text around matched keywords and wrap them in mark or strong tags for highlighting." } }, { "@type": "Question", "name": "Can I search PDF or Word documents?", "acceptedAnswer": { "@type": "Answer", "text": "The original script only searches plain text and HTML. To search PDFs or Word documents, you need additional tools or libraries for text extraction." } }, { "@type": "Question", "name": "How can I make the search case-sensitive?", "acceptedAnswer": { "@type": "Answer", "text": "Remove the lc() functions that convert text to lowercase before comparison, or add a user checkbox to toggle case sensitivity." } }, { "@type": "Question", "name": "Is there a modern alternative to this script?", "acceptedAnswer": { "@type": "Answer", "text": "Yes, modern alternatives include JavaScript libraries like Lunr.js or Fuse.js, cloud services like Algolia, or CMS built-in search functionality." } }, { "@type": "Question", "name": "How do I add pagination to search results?", "acceptedAnswer": { "@type": "Answer", "text": "Add a page parameter to the query string and modify the script to calculate offset, slice results, and generate pagination links." } } ] } </script> </div><!-- /.container --> <!-- Footer for worldwidemart.com - Bootstrap 5 --> <footer class="py-5 bg-dark text-light"> <div class="container"> <div class="row gy-4"> <!-- About Section --> <div class="col-lg-3 col-md-6 col-12"> <div class="d-flex flex-column gap-3"> <h5 class="fw-bold text-white mb-0">Script Archive</h5> <div class="fs-5 d-flex flex-row gap-3"> <a href="mailto:webarchive@gmail.com" class="text-secondary" title="Email"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-envelope" viewBox="0 0 16 16"> <path d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4Zm2-1a1 1 0 0 0-1 1v.217l7 4.2 7-4.2V4a1 1 0 0 0-1-1H2Zm13 2.383-4.708 2.825L15 11.105V5.383Zm-.034 6.876-5.64-3.471L8 9.583l-1.326-.795-5.64 3.47A1 1 0 0 0 2 13h12a1 1 0 0 0 .966-.741ZM1 11.105l4.708-2.897L1 5.383v5.722Z"/> </svg> </a> <a href="https://github.com" class="text-secondary" title="GitHub"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-github" viewBox="0 0 16 16"> <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/> </svg> </a> </div> </div> </div> <!-- CGI Scripts - Main Category --> <div class="col-lg-3 col-md-6 col-6"> <div class="d-flex flex-column gap-2"> <h6 class="fw-bold text-white text-uppercase mb-1">CGI Scripts</h6> <ul class="list-unstyled mb-0 small"> <li class="mb-1"><a href="/scripts/" class="text-secondary text-decoration-none">All Scripts</a></li> <li class="mb-1"><a href="/scripts/formmail.shtml" class="text-secondary text-decoration-none">FormMail</a></li> <li class="mb-1"><a href="/scripts/wwwboard.shtml" class="text-secondary text-decoration-none">WWWBoard</a></li> <li class="mb-1"><a href="/scripts/guestbook.shtml" class="text-secondary text-decoration-none">Guestbook</a></li> <li class="mb-1"><a href="/scripts/search.shtml" class="text-secondary text-decoration-none">Simple Search</a></li> <li class="mb-1"><a href="/scripts/counter.shtml" class="text-secondary text-decoration-none">Counter</a></li> <li class="mb-1"><a href="/scripts/textcounter.shtml" class="text-secondary text-decoration-none">TextCounter</a></li> <li class="mb-1"><a href="/scripts/cookielib.shtml" class="text-secondary text-decoration-none">HTTP Cookie Library</a></li> <li class="mb-1"><a href="/scripts/links.shtml" class="text-secondary text-decoration-none">Free For All Links</a></li> <li class="mb-1"><a href="/scripts/C++/textcounter.shtml" class="text-secondary text-decoration-none">C++ TextCounter</a></li> </ul> </div> </div> <!-- Help & Documentation --> <div class="col-lg-2 col-md-4 col-6"> <div class="d-flex flex-column gap-2"> <h6 class="fw-bold text-white text-uppercase mb-1">Help & Docs</h6> <ul class="list-unstyled mb-0 small"> <li class="mb-1"><a href="/scripts/faq/" class="text-secondary text-decoration-none">FAQ</a></li> <li class="mb-1"><a href="/scripts/readme/" class="text-secondary text-decoration-none">Readme Files</a></li> <li class="mb-1"><a href="/scripts/help/" class="text-secondary text-decoration-none">Help Center</a></li> <li class="mb-1"><a href="/scripts/examples/" class="text-secondary text-decoration-none">Examples</a></li> <li class="mb-1"><a href="/scripts/demos/" class="text-secondary text-decoration-none">Demos</a></li> <li class="mb-1"><a href="/scripts/get_all.shtml" class="text-secondary text-decoration-none">Download Scripts</a></li> </ul> </div> </div> </div> <!-- Divider --> <hr class="my-4 border-secondary"> <!-- Bottom Row --> <div class="row align-items-center"> <div class="col-md-6 text-center text-md-start"> <p class="mb-0 small text-secondary"> This is a fan archive preserving the legacy of classic CGI scripts from the early web era.<br> All code implementations are newly written and provided free for educational and practical use.<br> © 1995 - <span id="currentYear"></span> Script Archive. Original concepts inspired by various early web developers. </p> </div> <div class="col-md-6 text-center text-md-end mt-2 mt-md-0"> <ul class="list-inline mb-0 small"> <li class="list-inline-item"><a href="/about/" class="text-secondary text-decoration-none">About</a></li> <li class="list-inline-item text-secondary">|</li> <li class="list-inline-item"><a href="/contact/" class="text-secondary text-decoration-none">Contact</a></li> <li class="list-inline-item text-secondary">|</li> <li class="list-inline-item"><a href="/hosting/" class="text-secondary text-decoration-none">Web Hosting</a></li> </ul> </div> </div> </div> </footer> <script> document.getElementById('currentYear').textContent = new Date().getFullYear(); </script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script> </body> </html>