// SPDX-License-Identifier: MIT // Copyright (c) 2026 Incoprea // Search a notecard for a word // Not compiled or tested in-world // Setup: Add a saved plain-text notecard named Notes. Edit FIND, then touch as owner. // Stop / restore: Remove or disable. Inventory changes cancel the search; touch again to restart. // Notes: Uses literal substring matching, so garden also matches gardening. No external search service or visitor data is involved. Limits output to the first sixty input lines. // License: see LICENSE in this distribution. // Original Incoprea example. MIT. string CARD = "Notes"; string FIND = "garden"; integer lineNumber; integer matches; key pending = NULL_KEY; finish(string reason) { pending = NULL_KEY; llSetTimerEvent(0.0); llOwnerSay(reason + " Matches: " + (string)matches); } requestLine() { pending = llGetNotecardLine(CARD, lineNumber); llSetTimerEvent(20.0); } default { touch_start(integer count) { if (llDetectedKey(0) != llGetOwner()) return; if (pending != NULL_KEY) return; if (FIND == "") { llOwnerSay("Set a nonempty search string."); return; } if (llGetInventoryType(CARD) != INVENTORY_NOTECARD) { llOwnerSay("Add the saved Notes notecard first."); return; } lineNumber = 0; matches = 0; requestLine(); } dataserver(key query, string data) { if (query != pending || pending == NULL_KEY) return; if (data == EOF) { finish("End of card."); return; } if (llSubStringIndex(llToLower(data), llToLower(FIND)) != -1) { ++matches; llOwnerSay("Line " + (string)(lineNumber + 1) + ": " + data); } ++lineNumber; if (lineNumber >= 60) { finish("Stopped at the 60-line limit."); return; } requestLine(); } timer() { finish("Search timed out; results are partial."); } changed(integer flags) { if (flags & (CHANGED_INVENTORY | CHANGED_OWNER)) llResetScript(); } }