-13
21

I lack the knowledge/skills fix my slop of an attempted shell script

2h 46m ago by piefed.social/u/iloveDigit in programming@programming.dev

I am not a coder. I have been staring at a screen the past two weeks trying to "vibe code" for the first time with unpaid chat bots, for a seemingly simple project. But I still haven't even gotten a single version to the real-world testing stage, keep getting failures on more basic self-tests.

The project is supposed to convert files to QR code videos and back. Right now, it can complete a round-trip self test (which please bewarned is meaningless to real world results) in the Debian environment I developed it in. However, every chat bot I've consulted tells me this script should be so "portable" it would run on everything from Linux to BSD to termux, and instead it's failed on the first additional Linux distro I tried it on: devuan.

I spent most of this morning figuring out where the issue probably is: in the extract_bin_folder logic, devuan seems to lose the final payload chunk every time. As a result, the file hits EOF exactly one chunk early when reassembled. Oddly, the extract_bin_folder does get the "footer" QR code (which comes after the missing payload chunk) from the encoder's default looped videos. If the user calls -l 0 in the encoder to disable the loop, then extract_bin_folder will miss both the footer and the final payload chunk. (I don't think the footer chunk is being used for anything regardless, just noting the odd behavior).

I've spent the rest of the day trying to fix it, but I simply lack the knowledge/skills to understand or fix the behavior difference between my Debian environment and my Devuan environment. I guess something to do with bash vs dash, but my chat bots and I just aren't making progress. Any human help would be appreciated.

Let me reiterate this is not ready for release at all, it isn't even passing these basic tests, hence the version number 0.0.0. I'll also mention a lot of the comments and echo statements from the chat bot are stupidly inaccurate, I can't clean up the phrasing as fast as it generates slop. I would consider the comments and echo statements just as important to fix as the actual functionality before calling this "ready to use."

Edit - made a copying/pasting mistake, fixed

#!/bin/sh  

# SPDX-License-Identifier: CC0-1.0  
# Public Domain  

# SnowyQR 0.0.0 by Digital Assistant  
# 56 years after UNIX epoch  

# Instruction to the shell to exit if any command fails.  
set -e  

# Default configuration for cleanup and temporary workspace.  
DO_CLEANUP=true  
USER_WORK_DIR=""  

# Help menu explaining the various operational modes and flags.  
usage() {  
    echo "Usage:"  
    echo "  Encode input file to video"  
    echo "  $0 -f <input_file> <output_file.mkv>"  
    echo ""  
    echo "  Decode file(s) from video"  
    echo "  $0 -v <input_vid.mkv> <output_dir>"  
    echo ""  
    echo "  Encode file to QR code directory (for test/debug)"  
    echo "  $0 -f <input_file> <output_dir>"  
    echo ""  
    echo "  Compile QR code dir to video (for test/debug)"  
    echo "  $0 -d <input_dir> <output_file.mkv>"  
    echo ""  
    echo "  Extract .bin folder from video (for test/debug)"  
    echo "  $0 -b <input_vid.mkv> <output_dir>"  
    echo ""  
    echo "  Reassemble .bin folder to file(s) (for test/debug)"  
    echo "  $0 -b <input_dir> <output_dir>"  
    echo ""  
    echo "Options:"  
    echo "  -l N Video mode: loop N times (default: 1, plays twice). Set 0 to disable."  
    echo "  -c FG BG  Set custom Hex colors for Foreground and Background"  
    echo "            Defaults: 000000 6B6B6B"  
    echo "            Use 000000 FFFFFF for standard black and white."  
    echo "  -w DIR    Specify a permanent work directory. Skips cleanup and uses"  
    echo "            numbered subfolders inside DIR."  
    echo "            (For debugging and systems without mktemp)"  
    exit 1  
}  

# --- Shared Utility Functions ---  

# Verifies the presence of 'zint', required for barcode/QR generation.  
check_zint() {  
    if ! command -v zint >/dev/null 2>&1; then  
        echo "Error: can't find tool 'zint'" >&2  
        exit 1  
    fi  
}  

# Verifies 'ffmpeg' is installed for video encoding and frame extraction tasks.  
check_ffmpeg() {  
    if ! command -v ffmpeg >/dev/null 2>&1; then  
        echo "Error: 'ffmpeg' not found for video processing." >&2  
        exit 1  
    fi  
}  

# Verifies 'zbarimg' is installed for reading data from QR and barcodes.  
check_zbarimg() {  
    if ! command -v zbarimg >/dev/null 2>&1; then  
        echo "Error: 'zbarimg' (zbar-tools) not found for QR scanning." >&2  
        exit 1  
    fi  
}  

# Ensures a temporary directory creator is available if no custom path is provided.  
check_mktemp() {  
    if [ -z "$USER_WORK_DIR" ] && ! command -v mktemp >/dev/null 2>&1; then  
        echo "Error: 'mktemp' not found. This system cannot create temporary directories automatically." >&2  
        echo "Please install 'mktemp' or use the '-w <DIR>' option to specify a work directory." >&2  
        exit 1  
    fi  
}  

# Creates or identifies a working directory, respecting user-defined paths or using mktemp.  
get_work_dir() {  
    if [ -n "$USER_WORK_DIR" ]; then  
        _i=1  
        while [ -d "$USER_WORK_DIR/$_i" ]; do _i=$((_i + 1)); done  
        _dir="$USER_WORK_DIR/$_i"  
        mkdir -p "$_dir"  
        echo "$_dir"  
    else  
        mktemp -d || { echo "Error: Failed to create a temporary directory." >&2; exit 1; }  
    fi  
}  

# --- Core Logic Sections ---  

# Logic from encoder script  
# encode_qr: Splits a file into binary chunks and generates a sequenced folder of QR code PNGs.  
# Includes a metadata header (index 0) and a footer.  
encode_qr() {  
    infile="$1"  
    outdir="$2"  
    fg_color="$3"  
    bg_color="$4"  

    if [ ! -f "$infile" ]; then  
        echo "Error: '$infile' does not exist?" >&2  
        exit 1  
    fi  

    # Prepare the output folder and convert paths to absolute for reliability.  
    echo "Checking output directory permissions..."  
    mkdir -p "$outdir"  
    outdir_abs="$(cd "$outdir" && pwd)"  

    # Capture file information.  
    filename=$(basename "$infile")  
    input_dir_path=$(dirname "$infile")  
    input_abs_path="$(cd "$input_dir_path" && pwd)/$(basename "$infile")"  

    echo "Creating temporary work folder for QR generation..."  
    work_dir=$(get_work_dir)  

    # Trap to ensure temporary files are removed if the process is interrupted.  
    cleanup_enc() {  
        if [ "$DO_CLEANUP" = true ] && [ -d "$work_dir" ]; then  
            echo "Cleaning up temporary files..."  
            rm -rf "$work_dir"  
        fi  
    }  
    trap cleanup_enc EXIT INT TERM  

    orig_pwd=$(pwd)  
    cd "$work_dir"  

    # Divide the source file into 362-byte segments (max capacity for this QR configuration).  
    echo "Splitting '$filename' into 362-byte chunks..."  
    mkdir -p 1  
    split -b 362 "$input_abs_path" 1/_  

    # Iterate through segments and generate high-density binary QR codes.  
    echo "Generating data QR codes..."  
    mkdir -p 1o  

    # Filename format: [index].[original_filename].qr.png  
    i=1  
    for f in 1/_*; do 
        [ -f "$f" ] || continue  
        out=$(printf "1o/%08d.%s.qr.png" "$i" "$filename")  
        zint -b 58 --binary --secure=2 --scale=2.5 --whitesp=4 --vwhitesp=4 --fg="$fg_color" --bg="$bg_color" -o "$out" -i "$f"  
        i=$((i + 1))  
    done  

    # Generate metadata frames: 00000000 as header and final index as footer.  
    echo "Generating header/footer QR code..."  
    header_filename=$(printf "00000000.%s.qr.png" "$filename")  
    footer_filename=$(printf "%08d.%s.qr.png" "$i" "$filename")  
    filesize=$(wc -c < "$input_abs_path" | tr -d ' ')  
    printf "%s\n%s\n%s bytes\n%s\n%s" "0" "$filename" "$filesize" "snowyQR 0.0.0 by Digital Assistant" "this is a metadata header/footer, payload data should be before or after this (may loop)" > header.tmp  
    zint -b 58 --secure=2 --scale=2.5 --whitesp=4 --vwhitesp=4 --fg="$fg_color" --bg="$bg_color" -i header.tmp -o "1o/$header_filename"  
    printf "%s\n%s\n%s bytes\n%s\n%s" "1" "$filename" "$filesize" "snowyQR 0.0.0 by Digital Assistant" "this is a metadata header/footer, payload data should be before or after this (may loop)" > footer.tmp  
    zint -b 58 --secure=2 --scale=2.5 --whitesp=4 --vwhitesp=4 --fg="$fg_color" --bg="$bg_color" -i footer.tmp -o "1o/$footer_filename"  

    # Transfer processed frames to the final destination.  
    echo "Moving generated QR codes to '$outdir'..."  
    cd "$orig_pwd"  
    mv "$work_dir"/1o/* "$outdir_abs"/  
    
    # Disarm trap and perform manual cleanup.  
    trap - EXIT INT TERM  
    if [ "$DO_CLEANUP" = true ] && [ -d "$work_dir" ]; then rm -rf "$work_dir"; fi  
}  

# Logic from encoder script  
# create_video: Stacks QR codes and linear barcodes side-by-side into a lossless video stream.  
create_video() {  
    indir="$1"  
    outfile="$2"  
    fg_color="$3"  
    bg_color="$4"  
    loop_count="$5"  

    if [ ! -d "$indir" ]; then  
        echo "Error: Input directory '$indir' does not exist." >&2  
        exit 1  
    fi  

    # Workspace for intermediate barcodes.  
    tmp_process_dir=$(get_work_dir)  
    barcode_dir="$tmp_process_dir/barcodes"  
    mkdir -p "$barcode_dir"  

    # Generate a matching Code-128 barcode for every QR frame to assist frame sequencing during decode.  
    echo "Generating linear barcodes for frames..."  
    for f in "$indir"/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].*.png; do  
        [ -f "$f" ] || continue  
        base_name=$(basename "$f")  
        seq_num=$(echo "$base_name" | cut -c1-8)  
        zint -b 20 --scale=2 --whitesp=4 --vwhitesp=4 --fg="$fg_color" --bg="$bg_color" -o "$barcode_dir/$base_name" -d "$seq_num"  
    done  

    # Add padding to the end of the barcode sequence to prevent video truncation.  
    highest_barcode=""  
    for f in "$barcode_dir"/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].*.png; do  
        [ -f "$f" ] && highest_barcode="$f"  
    done  

    if [ -n "$highest_barcode" ]; then  
        base_bar=$(basename "$highest_barcode")  
        bar_num=$(echo "$base_bar" | cut -c1-8)  
        bar_num_clean=$(echo "$bar_num" | sed 's/^0*//'); : "${bar_num_clean:=0}"  
        bar_suffix=$(echo "$base_bar" | cut -c9-)  
        for j in 1 2 3 4 5; do  
            next_bar_num=$((bar_num_clean + j))  
            next_bar_padded=$(printf "%08d" "$next_bar_num")  
            cp "$highest_barcode" "$barcode_dir/$next_bar_padded$bar_suffix"  
        done  

        # Add 4 copies of barcode #00000000 to complete 9 total padding frames.  
        zero_barcode="$barcode_dir/00000000$bar_suffix"  
        if [ -f "$zero_barcode" ]; then  
            for j in 6 7 8 9; do  
                next_bar_num=$((bar_num_clean + j))  
                next_bar_padded=$(printf "%08d" "$next_bar_num")  
                cp "$zero_barcode" "$barcode_dir/$next_bar_padded$bar_suffix"  
            done  
        fi  
    fi  

    # Add padding to the end of the QR sequence to prevent video truncation.  
    highest_file=""  
    for f in "$indir"/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].*.png; do  
        [ -f "$f" ] && highest_file="$f"  
    done  

    if [ -n "$highest_file" ]; then  
        base_file=$(basename "$highest_file")  
        highest_num=$(echo "$base_file" | cut -c1-8)  
        highest_num_clean=$(echo "$highest_num" | sed 's/^0*//'); : "${highest_num_clean:=0}"  
        file_suffix=$(echo "$base_file" | cut -c9-)  
        for j in 1 2 3 4 5 6 7 8 9; do  
            next_num=$((highest_num_clean + j))  
            next_num_padded=$(printf "%08d" "$next_num")  
            target_qr="$indir/$next_num_padded$file_suffix"  
            cp "$highest_file" "$target_qr"  
            
            # Log the created path cleanly to a tracking text file  
            echo "$target_qr" >> "$tmp_process_dir/qr_to_clean.txt"  
        done  
    fi  

    # Set ffmpeg loop arguments if specified.  
    LOOP_ARGS=""  
    if [ "$loop_count" -ne 0 ]; then  
        LOOP_ARGS="-stream_loop $loop_count"  
    fi  

    # FFmpeg assembly: stacks QR on the left and Barcode on the right.  
    echo "Combining QR codes and Barcodes into video (Loop: $loop_count)..."  
    
    # Use hstack and pad to create a combined canvas, encoded to lossless VP9.  
    if ffmpeg \  
      $LOOP_ARGS -f image2 -pattern_type glob -i "$indir/*.png" \  
      $LOOP_ARGS -f image2 -pattern_type glob -i "$barcode_dir/*.png" \  
      -filter_complex "[0:v]pad=420:420:0:420-ih:color=black@0[qr]; \  
                       [1:v]pad=420:420:420-iw:420-ih:color=black@0[bar]; \  
                       [qr][bar]hstack=inputs=2,format=yuva420p[out]" \  
      -map "[out]" \  
      -c:v libvpx-vp9 -lossless 1 \  
      "$outfile"; then  
        echo "Video output: $outfile"  
    fi  

    # Remove the temporary padding files created in the input directory.  
    if [ "$DO_CLEANUP" = true ] && [ -f "$tmp_process_dir/qr_to_clean.txt" ]; then  
        echo "Cleaning up temporary QR codes from input directory..."  
        while IFS= read -r file_to_remove; do  
            rm -f "$file_to_remove"  
        done < "$tmp_process_dir/qr_to_clean.txt"  
    fi  

    [ "$DO_CLEANUP" = true ] && rm -rf "$tmp_process_dir"  
}  

# Logic from extractor script  
# extract_bin_folder: Extracts frames from video and scans them back into raw binary chunks.  
# Detects sequence loops to organize data from repeated transmissions.  
extract_bin_folder() {  
    input_source="$1"  
    output_dir="$2"  
    
    video_input=""  
    input_dir=""  

    # Determine if the input is a file or a folder of existing images.  
    if [ -f "$input_source" ]; then  
        video_input="$input_source"  
    elif [ -d "$input_source" ]; then  
        input_dir="$input_source"  
    else  
        echo "Error: Extraction input source '$input_source' not found." >&2  
        exit 1  
    fi  

    # Create an internal work directory for the extraction process  
    # This respects -w and skips mktemp if requested.  
    _internal_work=$(get_work_dir)  
    
    cleanup_ext() { if [ "$DO_CLEANUP" = true ] && [ -d "$_internal_work" ]; then rm -rf "$_internal_work"; fi; }  
    trap cleanup_ext EXIT INT TERM  

    # Unpack video frames into images.  
    if [ -n "$video_input" ]; then  
        input_dir="$_internal_work/video_frames"  
        mkdir -p "$input_dir"  
        ffmpeg -i "$video_input" "$input_dir/snowyqr_%08d.png" || {  
            echo "Error: ffmpeg failed." >&2; exit 1;  
        }  
    fi  

    [ -d "$input_dir" ] || { echo "Error: Input directory does not exist." >&2; exit 1; }  

    # Setup output folder for extracted .bin files.  
    [ -z "$output_dir" ] && output_dir="snowyQR_$(date +%H%M%S)_$$"  
    mkdir -p "$output_dir"  

    # Initialize set folders for handling looped playback.  
    folder_index=1  
    current_temp_dir="$_internal_work/chunk_set_${folder_index}"  
    mkdir -p "$current_temp_dir"  

    echo "Extracting binary chunks. Please wait, may take hours. (Do not run without zbarimg and ffmpeg)"  
    # Scanning is intensive.  

    prev_barcode=""  

    for img in "$input_dir"/*; do  
        [ -f "$img" ] || continue  
        tmp_bin="$current_temp_dir/tmp.qr.bin"  
        
        # Scan frame for QR data.  
        if zbarimg -q --raw -S*.disable -Sqr.enable -Sbinary "$img" 2>/dev/null > "$tmp_bin" && [ -s "$tmp_bin" ]; then  
            dd if="$tmp_bin" of="$_internal_work/dd.tmp" bs=1 count=362 2>/dev/null  
            mv "$_internal_work/dd.tmp" "$tmp_bin"  
        else  
            rm -f "$tmp_bin"; continue  
        fi  

        # Scan frame for sequence index barcode.  
        barcode=$(zbarimg -q --raw -S*.disable -Scode128.enable "$img" 2>/dev/null | tr -d '\n\r')  
        # Skip if sequencing data is missing.  
        if [ -z "$barcode" ]; then  
            rm -f "$tmp_bin"  
        else  
            target_name="$current_temp_dir/${barcode}.qr.bin"  
            
            if [ "$barcode" = "$prev_barcode" ]; then  
                rm -f "$tmp_bin"  
                continue  
            fi  
            
            # Start a new folder if we detect a repeat index (indicating a loop start).  
            if [ -f "$target_name" ]; then  
                folder_index=$((folder_index + 1))  
                current_temp_dir="$_internal_work/chunk_set_${folder_index}"  
                mkdir -p "$current_temp_dir"  
                target_name="$current_temp_dir/${barcode}.qr.bin"  
                prev_barcode=""  
            else  
                prev_barcode="$barcode"  
            fi  
            
            mv "$tmp_bin" "$target_name"  
        fi  
    done  

    [ -n "$video_input" ] && [ "$DO_CLEANUP" = true ] && rm -rf "$input_dir"  

    # Final renaming: parse the header chunk (index 0) to recover the original filename.  
    i=1  
    while [ $i -le "$folder_index" ]; do  
        dir="$_internal_work/chunk_set_${i}"  
        if [ -d "$dir" ]; then  
            zero_file="$dir/00000000.qr.bin"  
            file_prefix=""  

            # Peek at metadata chunk for filename recovery.  
            if [ -f "$zero_file" ]; then  
                insert_name=$(sed -n '2p' "$zero_file" | tr -d '\n\r/')  
                [ -n "$insert_name" ] && file_prefix="${insert_name}."  
            fi  

            for bin_file in "$dir"/*; do  
                [ -f "$bin_file" ] || continue  
                base_name=${bin_file##*/}  
                [ "$base_name" = "tmp.qr.bin" ] && continue  
                
                barcode_part="${base_name%%.*}"  
                mv "$bin_file" "$output_dir/${barcode_part}.${file_prefix}qr.bin"  
            done  
            [ "$DO_CLEANUP" = true ] && rm -rf "$dir"  
        fi  
        i=$((i + 1))  
    done  

    echo "Extraction complete. Output folder available at: $output_dir"  

    trap - EXIT INT TERM  
    cleanup_ext  
}  

# Logic from reassembler script  
# reassemble_bin_folder: Concatenates binary chunks back into their original file format.  
reassemble_bin_folder() {  
    input_dir="$1"  
    output_dir="$2"  

    if [ ! -d "$input_dir" ]; then  
        echo "Error: Input directory '$input_dir' does not exist." >&2  
        exit 1  
    fi  

    mkdir -p "$output_dir"  

    _internal_work=$(get_work_dir)  
    processed_targets="$_internal_work/targets.$$"  

    cleanup_re() { [ "$DO_CLEANUP" = true ] && rm -f "$processed_targets"; }  
    trap cleanup_re EXIT INT TERM  

    # Catalog all unique target files based on chunk naming patterns.  
    for filepath in "$input_dir"/*.bin; do  
        [ -f "$filepath" ] || continue  
        
        filename=${filepath##*/}  
        length=$(printf "%s" "$filename" | wc -c)  
        # Convention check.  
        
        if [ "$length" -lt 16 ]; then  
            continue  
        fi  
        
        rest=$(printf "%s" "$filename" | cut -c 10-)  
        target_name=$(printf "%s" "$rest" | cut -c 1-$((length - 15)))  
        
        echo "$target_name" >> "$processed_targets"  
    done  

    # Deduplicate the list of targets to process.  
    if [ -s "$processed_targets" ]; then  
        unique_targets=$(sort -u "$processed_targets")  
    else  
        echo "No .bin files matching the requirements were found."  
        exit 0  
    fi  

    # Process each unique target by stitching its chunks together in order.  
    echo "$unique_targets" | while read -r target; do  
        [ -z "$target" ] && continue  
        
        echo "Processing $target..."  
        
        # Collate chunks for this specific target.  
        set --  
        for chunk in "$input_dir"/????????."$target"??????; do  
            [ -f "$chunk" ] && set -- "$@" "$chunk"  
        done  
        
        total_chunks=$#  
        
        # A valid sequence must contain at least Header, Footer, and one data chunk.  
        if [ "$total_chunks" -le 2 ]; then  
            echo "Skipping $target: Has $total_chunks chunk(s). Ignoring the boundaries leaves no data."  
            continue  
        fi  
        
        output_target=${target%.}  
        output_file="$output_dir/$output_target"  
        # Skip the metadata header chunk.  
        : > "$output_file"  
        
        shift 1  
        
        count=1  
        limit=$((total_chunks - 2))  
        # Iterate through the data chunks only.  
        
        while [ "$count" -le "$limit" ]; do  
            current_chunk="$1"  
            dd if="$current_chunk" bs=1 count=362 2>/dev/null >> "$output_file"  
            shift 1  
            count=$((count + 1))  
        done  
        
        echo "Reassembled file: $output_target (skipped boundaries, 362 bytes per chunk)."  
    done  

    echo "Reassembly complete. Output directory: '$output_dir'."  
    trap - EXIT INT TERM  
    cleanup_re  
}  

# --- CLI Routing Arguments Parsing ---  

MODE=""  
INPUT_ARG=""  
OUTPUT_ARG=""  
FG_COLOR="000000"  
BG_COLOR="6B6B6B"  
LOOP_COUNT=1  

# Argument parsing loop.  
while [ $# -gt 0 ]; do  
    case "$1" in  
        -l)  
            if [ -z "$2" ] || [ "${2#-}" != "$2" ]; then  
                echo "Error: -l requires a numeric argument." >&2  
                usage  
            fi  
            LOOP_COUNT="$2"  
            shift 2  
            ;;  
        -c)  
            if [ -z "$2" ] || [ "${2#-}" != "$2" ] || [ -z "$3" ] || [ "${3#-}" != "$3" ]; then  
                echo "Error: -c requires two color hex arguments (FG and BG)." >&2  
                usage  
            fi  
            if ! echo "$2" | grep -Eq '^[0-9a-fA-F]{6}$' || ! echo "$3" | grep -Eq '^[0-9a-fA-F]{6}$'; then  
                echo "Error: Colors must be valid 6-digit hex values (e.g., 000000)." >&2  
                usage  
            fi  
            FG_COLOR="$2"  
            BG_COLOR="$3"  
            shift 3  
            ;;  
        -w)  
            if [ -z "$2" ] || [ "${2#-}" != "$2" ]; then  
                echo "Error: -w requires a directory argument." >&2  
                usage  
            fi  
            USER_WORK_DIR="$2"  
            DO_CLEANUP=false  
            mkdir -p "$USER_WORK_DIR"  
            shift 2  
            ;;  
        -f|-v|-d|-b)  
            if [ -n "$MODE" ]; then  
                echo "Error: Multiple operation mode flags specified." >&2  
                usage  
            fi  
            MODE="$1"  
            if [ -z "$2" ] || [ "${2#-}" != "$2" ]; then  
                echo "Error: Mode option $1 requires a parameter path." >&2  
                usage  
            fi  
            INPUT_ARG="$2"  
            shift 2  
            ;;  
        -*)  
            echo "Unknown option: $1" >&2  
            usage  
            ;;  
        *)  
            if [ -n "$OUTPUT_ARG" ]; then  
                echo "Error: Too many trailing arguments arguments." >&2  
                usage  
            fi  
            OUTPUT_ARG="$1"  
            shift  
            ;;  
    esac  
done  

if [ -z "$MODE" ] || [ -z "$INPUT_ARG" ] || [ -z "$OUTPUT_ARG" ]; then  
    echo "Error: Missing required mode flags, input path, or output destination targets." >&2  
    usage  
fi  

# --- Dependency Validation ---  

# Validate required binaries are installed for the chosen operation mode.  
check_mktemp  

case "$MODE" in  
    -f)  
        check_zint  
        case "$OUTPUT_ARG" in  
            *.mkv|*.webm|*.mp4|*.avi|*.mov|*.flv) check_ffmpeg ;;  
        esac  
        ;;  
    -v)  
        check_ffmpeg  
        check_zbarimg  
        ;;  
    -d)  
        check_zint  
        check_ffmpeg  
        ;;  
    -b)  
        if [ -f "$INPUT_ARG" ]; then  
            check_ffmpeg  
            check_zbarimg  
        fi  
        ;;  
esac  

# --- Flow Execution Management ---  

case "$MODE" in  
    -f)  
        # Check if output specifies a common video format extension  
        IS_VIDEO=false  
        case "$OUTPUT_ARG" in  
            *.mkv|*.webm|*.mp4|*.avi|*.mov|*.flv) IS_VIDEO=true ;;  
        esac  

        if [ "$IS_VIDEO" = true ]; then  
            # Workflow 1: Encode input file directly to video  
            # Automated extension fix-ups and validation for video encoding.  
			case "$OUTPUT_ARG" in  
    		*.mkv|*.webm) ;;  
    		*)  
        	echo "Warning: Only .webm and .mkv video formats are officially supported." >&2  
        
      		  # Check if stdin is NOT connected to an interactive terminal  
      		  if [ ! -t 0 ]; then  
      	      echo "Error: Non-interactive shell detected. Cannot prompt for input. Aborting." >&2  
   	          exit 1  
     	      fi  

	 	        echo "Please choose an action:" >&2  
    	    	echo "  [1] Swap output extension to .mkv (Recommended)" >&2  
        		echo "  [2] Cancel render" >&2  
        		echo "  [3] Ignore and attempt video rendering anyway" >&2  
        		printf "Choice (1/2/3): " >&2  
        		read -r choice  
        		if [ "$choice" = "1" ]; then  
            	case "$OUTPUT_ARG" in  
                	*/*)  
                    	_dir="${OUTPUT_ARG%/*}"  
                    	_file="${OUTPUT_ARG##*/}"  
                    	case "$_file" in  
                        	*.*) _file="${_file%.*}.mkv" ;;  
                        	*)   _file="$_file.mkv" ;;  
                    	esac  
                    	OUTPUT_ARG="$_dir/$_file"  
                    	;;  
                	*.*) OUTPUT_ARG="${OUTPUT_ARG%.*}.mkv" ;;  
                	*)   OUTPUT_ARG="$OUTPUT_ARG.mkv" ;;  
            	esac  
            	echo "Updated output format target name to: $OUTPUT_ARG" >&2  
        	elif [ "$choice" = "2" ]; then  
            echo "Aborting operation."; exit 1  
        	fi  
        	;;  
			esac  

            # Create a sandboxed QR directory for the two-step video build.  
            TEMP_QR_DIR=$(get_work_dir)  
            cleanup_workflow() { if [ "$DO_CLEANUP" = true ] && [ -d "$TEMP_QR_DIR" ]; then rm -rf "$TEMP_QR_DIR"; fi; }  
            trap cleanup_workflow EXIT INT TERM  

            echo "Phase 1: Splitting and encoding data files to temporary raw QR configurations..."  
            encode_qr "$INPUT_ARG" "$TEMP_QR_DIR" "$FG_COLOR" "$BG_COLOR"  
            
            echo "Phase 2: Translating temporary raw frames into final targeted sequence compilation..."  
            create_video "$TEMP_QR_DIR" "$OUTPUT_ARG" "$FG_COLOR" "$BG_COLOR" "$LOOP_COUNT"  
            
            [ "$DO_CLEANUP" = true ] && rm -rf "$TEMP_QR_DIR"  
            trap - EXIT INT TERM  
        else  
            # Workflow 3: Encode file directly to standard folder directory (Debug mode)  
            encode_qr "$INPUT_ARG" "$OUTPUT_ARG" "$FG_COLOR" "$BG_COLOR"  
            echo "Raw storage configuration generated inside output directory: '$OUTPUT_ARG'."  
        fi  
        ;;  

    -v)  
        # Workflow 2: Complete direct sequential Decode file(s) right out of video package container  
        TEMP_EXTRACT_DIR=$(get_work_dir)  
        cleanup_dec() { if [ "$DO_CLEANUP" = true ] && [ -d "$TEMP_EXTRACT_DIR" ]; then rm -rf "$TEMP_EXTRACT_DIR"; fi; }  
        trap cleanup_dec EXIT INT TERM  

        # Execute the two-phase recovery process: extract frames then reassemble binary data.  
        echo "Phase 1: Running deep visual scanner to unpack assets into structural chunks..."  
        extract_bin_folder "$INPUT_ARG" "$TEMP_EXTRACT_DIR"  

        echo "Phase 2: Executing assembly rules to recover targeted file data boundaries..."  
        reassemble_bin_folder "$TEMP_EXTRACT_DIR" "$OUTPUT_ARG"  

        [ "$DO_CLEANUP" = true ] && rm -rf "$TEMP_EXTRACT_DIR"  
        trap - EXIT INT TERM  
        ;;  

    -d)  
        # Workflow 4: Compile existing raw directories into complete matrix video frame structures  
        create_video "$INPUT_ARG" "$OUTPUT_ARG" "$FG_COLOR" "$BG_COLOR" "$LOOP_COUNT"  
        ;;  

    -b)  
        # Handle overlap parsing based on filesystem layout configurations  
        if [ -f "$INPUT_ARG" ]; then  
            # Workflow 5: Extract .bin metadata directories right from underlying video container tracks  
            extract_bin_folder "$INPUT_ARG" "$OUTPUT_ARG"  
        else  
            # Workflow 6: Stitch pre-existing standalone binary component directory elements  
            reassemble_bin_folder "$INPUT_ARG" "$OUTPUT_ARG"  
        fi  
        ;;  
esac  

exit 0  

My friend. I am a professional programmer. I get paid to read code. I even enjoy debugging code, no matter who wrote it.

I will not slog through pages of AI slop that you can't explain or understand.

I say this not to put you down. If you want to go down this road, AI can bootstrap you. It can legitimate help you learn and grow if you are willing to engage and think and understand. But you simply can't dump a wall of script in a random sub, claim complete ignorance, and expect "the community" to swoop in and save you.

Break down your problem. Ask specific, targeted questions. Use AI? Sure. But understand what it writes, or you'll end up... Well, exactly where you are.

Ask again once you have a question. Good luck.

While it is technically possible to write shell scripts that do as much as what you're attempting to, it's not a good idea.

Shell scripts generally don't have the development environment necessary for debugging. You can't pause execution based on a breakpoint and inspect variables, or step through the code line by line to watch the execution flow as it happens. Well, bashdb exists, but it's quite hard to use.

Without proper debugging tooling you're limited to printing stuff out on the screen and trying to figure out what that means. That's ok for short scripts (< 50 lines?) but yours is 700+ lines.

I've written code for decades and almost never use a debugger... printf gets you a long way.

Thank you for the reply. Python keeps being suggested, what would you suggest if I end up changing rails here?

I was going to suggest Python too but you'll need to be careful to just use functionality that is part of the standard library because once you start pulling in 3rd party packages deployment is much harder and portability seems to be a requirement here. That should be doable with your case.

The free edition of PyCharm is great for debugging.

Keeping python in mind then. For now I think I've gotta keep trying with a shell script because it can fit the whole decoder in 2 QR codes (1 for each step, extracting+reassembling) with zbarimg+ffmpeg as the only dependencies

Maybe I should use python for the main version but keep a shell script mini decoder option available

For being not a coder, this is fairly ambitious. I've been coding software for thirty years and I'm not sure I can offer anything technical. Personally, I'd probably try to do this in Python rather than shell script. Shell script is fine if you have tools you're just trying to stitch together, but this is far beyond that.

That being said, I get it. The first coding I ever did was making a character sheet for roleplaying by sending raw commands to my dot matrix printer from my Commodore 64. You have a vision in your head and a computer at your fingertips and you have to bring it all together.

I respect that and if part of your goal is to achieve this using only bash, then god speed. Otherwise I might look at a language that gives you library support and modularity and unit testing. I write Python scripts all the time, and the truth is I don't know Python at all, so I know it can be done. Define a unit of functionality and build that piece. When that piece works build another component. Then build a parent program that calls those two things. Then build more components. Define functions that can be reused when you have code that needs to do similar things.

The key is to build standalone functionality — what is the smallest useful thing you can do? Build it. Now you can invoke that every time you need to do the thing. Construct your program out of components you create.

This is kinda like if I told AI to build me a web service but keep everything in a single Java class. It would be a wild and unreadable, untestable mess.

Thank you. For the record, the main goal of using a shell script instead of a python script is maximum portability via the QR code itself. Instead of telling someone to scan a QR code and download hundreds of megabytes of python environment, all most users will have to download for my decoder is zbarimg (because everyone already has ffmpeg)

If I can get it working, anyway

There's a language that is extremely portable - go. It's compiled though and would need a bit more development environment than a script. But it would be able to run on just about any Linux, Windows, MacOS easily.

But also - not all AI models are created equal. Your free ones may not be great at a script of this level of complexity.

You asked AI to do a thing, it didn't work, so now you ask us to fix it.
How about you start writing something yourself which you're able to understand, and go from there?

Fuck off, I didn't ask for a working script, I asked for a difference between how Debian and Devuan (or bash and dash) would handle the functions here. Blocking you now

The project is supposed to convert files to QR code videos and back.

I don't really understand what you're trying to do.

I don't think that you're trying to just encode an URL, from a glance.

If you want to encode a video file as a QR code, you're going to face the pretty fundamental limitation of information content.

A QR code can store a maximum of (checks) under 3 KiB. Virtually any video file cannot fit in those space constraints.

I am not a coder. I have been staring at a screen the past two weeks trying to "vibe code" for the first time with unpaid chat bots, for a seemingly simple project. But I still haven't even gotten a single version to the real-world testing stage, keep getting failures on more basic self-tests.

So, chatbots in 2026 are not really a drop-in replacement for a computer programmer. My guess is that while we'll probably get there, it's probably going to require something more elaborate than just an plain old LLM. Like, it's not going to be a minor refinement of what's currently there, but a pretty fundamentally different system (though that system might potentially incorporate an LLM).

EDIT: Ah, it's a series of QR codes. I mean, that's going to be a pretty huge number for any real-world video.

EDIT2: Oh, you want a video of QR codes from a file. I think that a problem that you're gonna hit there is the transport, because your video is gonna be playing at a constant rate and real-time QR code recognition isn't perfectly reliable, and you're probably not going to reliably capture every frame. Maybe someone could make something with FEC so that dropping a certain percentage of frames is viable, like, use PAR2 or something.

Also, assuming that you're trying to make something that runs automatically, the generated code has stuff that will bail out if used other than if run interactively.

EDIT3: Also, it sounds like you want something portable. The script itself may (or may not be) portable, but it is trying to run external tools that are not going to be guaranteed to be present wherever.

EDIT4: I don't know why you'd be running dash on denuvan, as it sounds like its default shell is bash, but if /bin/sh is a symlink to dash on that system, what you could try doing is generating a bash-specific version. It...might work if you just switch the shebang line to bash at the top of the file, but bash in sh compatibility mode is not identical to bash in bash mode.

If you want some sort of generic portability debugging tool, I'd add set -x as the second line of the file. That will enable trace mode. Every line that runs will be printed to stderr. Then run it like so:

$ yourscript.sh 2>debug1.log

That'll output every line being run. Run it on both systems, and you'll get a log on each. Then you can run:

$ diff -u debug1.log debug2.log|less

That'll show you where the behavior starts to differ on the two systems.

But...I'm going to be blunt. I think that you're asking more of current AI chatbots than is reasonable given their current state, and that it's going to wind up being pretty frustrating if you're aiming to do it with zero programming.

Didn't see edit 4 before replying originally - might try a bash script instead of generic posix shell, thank you

I have been surprised how much progress has been possible by guiding the chat bot through 1 step at a time (and occasionally humanly digging through a problem it refuses to handle)

If successfully implemented, my design would rely on users screen recording or downloading the video with the files in it (or recording a video with a camera pointed at the screen if even more successful)

Because you're right that decoding the QR codes definitely does seem too slow to do it in real time

I'd suggest replacing the shell script with a Python script as a first step. If nothing else, it will probably be a hell of a lot easier to read... Your bots ought to be able to do a first pass at that. If it's actually tripping up on some particular bash/dash-ism like you think, that might even just bypass the issue entirely; if not it should be easier to debug with better readability.

Also put it on gitlab or codeberg. If I'm gonna read slop at least give me line numbers

This seems too purely proof of concept and far from passing real world tests tbh, I'd have to see proof to believe it works. I am surprised nobody has made something like my idea sooner though, keep thinking maybe I'll find it

unlike your slop code? lmao

Is this intended to be actually usable by people, or is it just a proof of concept?

Any real videos will need to split over so many QR codes nobody would bother.

Intended to be usable, but it's for putting small files in the corner of a video