#!/usr/bin/env bash set -u set -o pipefail ROOT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) TMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/cowsay-tour.XXXXXX") trap 'rm -rf "$TMP_DIR"' EXIT AUTO_RUN=0 COUNTDOWN=10 STOP_TOUR=0 COMPILER="" usage() { cat <<'EOF' Usage: ./tour_programmes.sh ./tour_programmes.sh --auto Options: --auto starts immediately without waiting -h, --help shows this help EOF } while (($# > 0)); do case "$1" in --auto) AUTO_RUN=1 ;; -h | --help) usage exit 0 ;; *) printf 'Unknown option: %s\n\n' "$1" >&2 usage >&2 exit 1 ;; esac shift done hr() { printf '\n============================================================\n' } show_intro() { hr printf 'Quick cowsay program tour\n' if ((AUTO_RUN == 1)); then printf 'Auto mode: demos start immediately.\n' else printf 'The first demo starts after %d seconds.\n' "$COUNTDOWN" printf 'After each demo, the tour waits %d seconds before the next one.\n' "$COUNTDOWN" printf 'Press Enter to continue faster, or type n then Enter to stop.\n' fi } need_commands() { local missing=0 local cmd for cmd in "$@"; do if ! command -v "$cmd" >/dev/null 2>&1; then printf 'Missing dependency: %s\n' "$cmd" >&2 missing=1 fi done return "$missing" } detect_compiler() { if [[ -n "$COMPILER" ]]; then return 0 fi local candidate for candidate in cc clang gcc; do if command -v "$candidate" >/dev/null 2>&1; then COMPILER="$candidate" return 0 fi done printf 'No C compiler found.\n' >&2 return 1 } compile_c() { local source="$1" local output="$2" detect_compiler || return 1 "$COMPILER" -std=c99 -Wall -Wextra -O2 -D_DEFAULT_SOURCE "$source" -o "$output" } run_command() { "$@" } run_command_with_input() { local input="$1" shift printf '%s' "$input" | "$@" } announce_demo() { local name="$1" local description="$2" hr printf '%s: %s\n' "$name" "$description" } drain_stdin() { if [[ ! -t 0 ]]; then return 0 fi while IFS= read -r -t 0; do : done } timed_continue() { local prompt="$1" if ((AUTO_RUN == 1)) || [[ ! -t 0 ]]; then printf 'Starting...\n' return 0 fi drain_stdin printf '%s' "$prompt" local answer="" if IFS= read -r -t "$COUNTDOWN" answer; then printf '\n' case "${answer,,}" in n) STOP_TOUR=1 return 1 ;; *) return 0 ;; esac fi printf '\n' return 0 } run_demo() { local name="$1" local function_name="$2" "$function_name" local status=$? if ((status != 0)); then printf '%s: failed (%d)\n' "$name" "$status" >&2 fi } demo_cow_kindergarten() { need_commands cowsay || return 1 run_command bash "$ROOT_DIR/src/bash_scripts/cow_kindergarten.sh" } demo_cow_primaryschool() { need_commands cowsay || return 1 run_command bash "$ROOT_DIR/src/bash_scripts/cow_primaryschool.sh" 3 } demo_cow_highschool() { need_commands cowsay || return 1 run_command bash "$ROOT_DIR/src/bash_scripts/cow_highschool.sh" 3 } demo_cow_college() { need_commands cowsay || return 1 run_command bash "$ROOT_DIR/src/bash_scripts/cow_college.sh" 5 } demo_cow_university() { need_commands cowsay || return 1 run_command bash "$ROOT_DIR/src/bash_scripts/cow_university.sh" 12 } demo_smart_cow() { need_commands cowsay bc || return 1 run_command bash "$ROOT_DIR/src/bash_scripts/smart_cow.sh" '4+13' } demo_crazy_cow() { need_commands cowsay || return 1 run_command_with_input $'5\n' bash "$ROOT_DIR/src/bash_scripts/crazy_cow.sh" } demo_newcow() { local bin="$TMP_DIR/newcow" compile_c "$ROOT_DIR/src/C/newcow.c" "$bin" || return 1 run_command "$bin" -e "^^" } demo_newcowt() { local bin="$TMP_DIR/newcowT" compile_c "$ROOT_DIR/src/C/newcows/newcowT.c" "$bin" || return 1 run_command "$bin" -T "U" } demo_newcowp() { local bin="$TMP_DIR/newcowp" compile_c "$ROOT_DIR/src/C/newcows/newcowp.c" "$bin" || return 1 run_command "$bin" -p "Hello" } demo_newcow2() { local bin="$TMP_DIR/newcow2" compile_c "$ROOT_DIR/src/C/newcow2.c" "$bin" || return 1 run_command "$bin" blink } demo_reading_cow() { local bin="$TMP_DIR/reading_cow" local sample="$TMP_DIR/reading_demo.txt" compile_c "$ROOT_DIR/src/C/reading_cow.c" "$bin" || return 1 printf 'Hello.\nI read.\n' >"$sample" run_command "$bin" --i 0.10 "$sample" } demo_tamagoshi_cow() { local bin="$TMP_DIR/tamagoshi_cow" if ((AUTO_RUN == 1)) || [[ ! -t 0 ]]; then printf 'Tamagoshi skipped in auto mode.\n' return 0 fi compile_c "$ROOT_DIR/src/C/tamagoshi_cow.c" "$bin" || return 1 run_command "$bin" } visit_programs() { local -a names=( 'cow_kindergarten.sh' 'cow_primaryschool.sh' 'cow_highschool.sh' 'cow_college.sh' 'cow_university.sh' 'smart_cow.sh' 'crazy_cow.sh' 'newcow.c' 'newcowT.c' 'newcowp.c' 'newcow2.c' 'reading_cow.c' 'tamagoshi_cow.c' ) local -a descriptions=( 'Counts from 1 to 10.' 'Counts up to n.' 'Shows square numbers.' 'Shows Fibonacci.' 'Shows prime numbers.' 'Calculates an expression.' 'Generates a weird sequence.' 'Basic cow.' 'Cow with a tongue option.' 'Cow with a speech bubble.' 'Compiles a .c file into .out.' 'Animates the cow.' 'The cow reads a file.' 'Small cow game.' ) local -a functions=( 'demo_cow_kindergarten' 'demo_cow_primaryschool' 'demo_cow_highschool' 'demo_cow_college' 'demo_cow_university' 'demo_smart_cow' 'demo_crazy_cow' 'demo_newcow' 'demo_newcowt' 'demo_newcowp' 'demo_newcow2' 'demo_reading_cow' 'demo_tamagoshi_cow' ) local total=${#names[@]} local i for ((i = 0; i < total; i++)); do announce_demo "${names[i]}" "${descriptions[i]}" if ((i == 0)); then timed_continue "Press Enter to start, type n then Enter to stop, auto-start in ${COUNTDOWN} seconds... " || return else timed_continue "Press Enter to start the next program, type n then Enter to stop, or wait ${COUNTDOWN} seconds... " || return fi run_demo "${names[i]}" "${functions[i]}" ((STOP_TOUR == 1)) && return done } show_outro() { hr if ((STOP_TOUR == 1)); then printf 'Tour stopped.\n' else printf 'End of tour.\n' fi } show_intro visit_programs show_outro