#!/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"
EOF
cat <<'EOF' >"$TMP_DIR/styles.xml"
EOF
cat <<'EOF' >"$TMP_DIR/meta.xml"
POSIX Shell ODT Generator
EOF
cat <<'EOF' >"$TMP_DIR/settings.xml"
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 "";
print "";
print " ";
print " ";
print " ";
print " ";
print " ";
print " ";
print " ";
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 " ";
is_first_p = 0;
} else {
printf " ";
}
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 "";
}
}
print " ";
print " ";
print "";
}
' "$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