diff options
Diffstat (limited to 'csprdg.sh')
| -rwxr-xr-x | csprdg.sh | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/csprdg.sh b/csprdg.sh new file mode 100755 index 0000000..81f880e --- /dev/null +++ b/csprdg.sh @@ -0,0 +1,228 @@ +#!/bin/sh +# +# Conjecturally Secure Pseudo-Random Document Generator +# +# Copyright (c) 2026 Doan Luu Duc Tai doan@luuduct.ai +# SPDX-License-Identifier: 0BSD OR Apache-2.0 +# +# This script is dual-licensed under the Zero-Clause BSD License (0BSD) +# OR the Apache License, Version 2.0 (Apache-2.0), at your option. +# See the LICENSE file for the full text of both licenses. + +OUTPUT_FILE="document.odt" + +SCRIPT_DIR=$(dirname "$0") +WORDLIST="$SCRIPT_DIR/words.txt" + +if [ ! -r "$WORDLIST" ]; then + printf "Error: The required word list was not found or is not readable at:\n" >&2 + printf " %s\n" "$WORDLIST" >&2 + printf "Please ensure 'words.txt' is placed in the same folder as this script.\n" >&2 + exit 1 +fi + +if ! command -v zip >/dev/null 2>&1; then + printf "Error: The 'zip' utility is required but was not found in PATH.\n" >&2 + exit 1 +fi + +TMP_DIR="./odt_tmp_$$" +mkdir -p "$TMP_DIR" + +cleanup() { + rm -rf "$TMP_DIR" +} +trap cleanup 0 INT TERM + +mkdir -p "$TMP_DIR/META-INF" + +printf "application/vnd.oasis.opendocument.text" >"$TMP_DIR/mimetype" + +cat <<'EOF' >"$TMP_DIR/META-INF/manifest.xml" +<?xml version="1.0" encoding="UTF-8"?> +<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" manifest:version="1.2"> + <manifest:file-entry manifest:full-path="/" manifest:version="1.2" manifest:media-type="application/vnd.oasis.opendocument.text"/> + <manifest:file-entry manifest:full-path="content.xml" manifest:media-type="text/xml"/> + <manifest:file-entry manifest:full-path="styles.xml" manifest:media-type="text/xml"/> + <manifest:file-entry manifest:full-path="meta.xml" manifest:media-type="text/xml"/> + <manifest:file-entry manifest:full-path="settings.xml" manifest:media-type="text/xml"/> +</manifest:manifest> +EOF + +cat <<'EOF' >"$TMP_DIR/styles.xml" +<?xml version="1.0" encoding="UTF-8"?> +<office:document-styles xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" office:version="1.2"> + <office:styles> + <style:default-style style:family="paragraph"/> + </office:styles> + <office:automatic-styles/> + <office:master-styles> + <style:master-page style:name="Standard"/> + </office:master-styles> +</office:document-styles> +EOF + +cat <<'EOF' >"$TMP_DIR/meta.xml" +<?xml version="1.0" encoding="UTF-8"?> +<office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" office:version="1.2"> + <office:meta> + <meta:generator>POSIX Shell ODT Generator</meta:generator> + </office:meta> +</office:document-meta> +EOF + +cat <<'EOF' >"$TMP_DIR/settings.xml" +<?xml version="1.0" encoding="UTF-8"?> +<office:document-settings xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" office:version="1.2"> + <office:settings/> +</office:document-settings> +EOF + +SEED=$( ( + date + times 2>/dev/null + ls -l /tmp 2>/dev/null + env 2>/dev/null +) | cksum | awk '{print $1}') + +case "$SEED" in +"" | *[!0-9]*) SEED=123456 ;; +esac + +awk -v seed="$SEED" -v stats_file="$TMP_DIR/stats" ' +BEGIN { + srand(seed); + word_count = 0; + + pages = int(50 + rand() * 31); + + min_words = pages * 200; + if (min_words < 10000) { + min_words = 10000; + } + total_words = min_words + int(rand() * 5001); +} + +/^[[:alpha:]]+$/ { + if (word_count < 50000) { + words[word_count] = $0; + word_count++; + } +} + +END { + if (word_count == 0) { + print "ERROR=\"No usable words found in the system wordlist.\"" > stats_file; + exit 1; + } + + print "PAGES=" pages > stats_file; + print "WORDS=" total_words > stats_file; + + print "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; + print "<office:document-content xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\" xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\" xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\" xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\" xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\" xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\" office:version=\"1.2\">"; + print " <office:automatic-styles>"; + print " <style:style style:name=\"PB\" style:family=\"paragraph\">"; + print " <style:paragraph-properties fo:break-before=\"page\"/>"; + print " </style:style>"; + print " </office:automatic-styles>"; + print " <office:body>"; + print " <office:text>"; + + w_per_page = int(total_words / pages); + words_left = total_words; + + for (p = 1; p <= pages; p++) { + if (p == pages) { + p_words = words_left; + } else { + variation = int(w_per_page * 0.2); + if (variation < 5) { + variation = 5; + } + p_words = w_per_page + int(rand() * (variation * 2 + 1)) - variation; + if (p_words > words_left) { + p_words = words_left; + } + if (p_words < 10) { + p_words = 10; + } + } + words_left -= p_words; + + if (p > 1) { + is_first_p = 1; + } else { + is_first_p = 0; + } + while (p_words > 0) { + p_len = int(25 + rand() * 36); + if (p_len > p_words) { + p_len = p_words; + } + p_words -= p_len; + + if (is_first_p) { + printf " <text:p text:style-name=\"PB\">"; + is_first_p = 0; + } else { + printf " <text:p>"; + } + + for (w = 0; w < p_len; w++) { + rnd_idx = int(rand() * word_count); + word = words[rnd_idx]; + + if (w == 0) { + word = toupper(substr(word, 1, 1)) substr(word, 2); + } else { + word = tolower(word); + } + + printf "%s", word; + if (w == p_len - 1) { + printf("."); + } else { + printf(" "); + } + } + print "</text:p>"; + } + } + + print " </office:text>"; + print " </office:body>"; + print "</office:document-content>"; +} +' "$WORDLIST" >"$TMP_DIR/content.xml" + +if [ -f "$TMP_DIR/stats" ]; then + . "$TMP_DIR/stats" +fi + +if [ -n "$ERROR" ]; then + printf "Error: %s\n" "$ERROR" >&2 + exit 1 +fi + +case "$OUTPUT_FILE" in +/*) ABS_OUTPUT="$OUTPUT_FILE" ;; +*) ABS_OUTPUT="$(pwd)/$OUTPUT_FILE" ;; +esac + +rm -f "$ABS_OUTPUT" + +( + cd "$TMP_DIR" || exit 1 + zip -X -r -n mimetype "$ABS_OUTPUT" mimetype META-INF content.xml styles.xml meta.xml settings.xml >/dev/null +) + +if [ $? -eq 0 ]; then + printf "Document successfully created: %s\n" "$OUTPUT_FILE" + printf "Generated Details:\n" + printf " - Pages: %s\n" "$PAGES" + printf " - Words: %s\n" "$WORDS" +else + printf "Error: Failed to package ODT using zip.\n" >&2 + exit 1 +fi |
