#!/bin/bash # Script to embed web files into C headers for the C-Relay admin interface # Converts HTML, CSS, and JS files from api/ directory into C byte arrays set -e echo "Embedding web files into C headers..." # Output directory for generated headers OUTPUT_DIR="src" mkdir -p "$OUTPUT_DIR" # Function to convert a file to C byte array file_to_c_array() { local input_file="$1" local array_name="$2" local output_file="$3" # Get file size local file_size=$(stat -c%s "$input_file" 2>/dev/null || stat -f%z "$input_file" 2>/dev/null || echo "0") echo "// Auto-generated from $input_file" >> "$output_file" echo "static const unsigned char ${array_name}_data[] = {" >> "$output_file" # Convert file to hex bytes hexdump -v -e '1/1 "0x%02x,"' "$input_file" >> "$output_file" echo "};" >> "$output_file" echo "static const size_t ${array_name}_size = $file_size;" >> "$output_file" echo "" >> "$output_file" } # Generate the header file HEADER_FILE="$OUTPUT_DIR/embedded_web_content.h" echo "// Auto-generated embedded web content header" > "$HEADER_FILE" echo "// Do not edit manually - generated by embed_web_files.sh" >> "$HEADER_FILE" echo "" >> "$HEADER_FILE" echo "#ifndef EMBEDDED_WEB_CONTENT_H" >> "$HEADER_FILE" echo "#define EMBEDDED_WEB_CONTENT_H" >> "$HEADER_FILE" echo "" >> "$HEADER_FILE" echo "#include " >> "$HEADER_FILE" echo "" >> "$HEADER_FILE" # Generate the C file SOURCE_FILE="$OUTPUT_DIR/embedded_web_content.c" echo "// Auto-generated embedded web content" > "$SOURCE_FILE" echo "// Do not edit manually - generated by embed_web_files.sh" >> "$SOURCE_FILE" echo "" >> "$SOURCE_FILE" echo "#include \"embedded_web_content.h\"" >> "$SOURCE_FILE" echo "#include " >> "$SOURCE_FILE" echo "" >> "$SOURCE_FILE" # Process each web file declare -A file_map # Find all web files for file in api/*.html api/*.css api/*.js; do if [ -f "$file" ]; then # Get filename without path basename=$(basename "$file") # Create C identifier from filename c_name=$(echo "$basename" | sed 's/[^a-zA-Z0-9_]/_/g' | sed 's/^_//') # Determine content type case "$file" in *.html) content_type="text/html" ;; *.css) content_type="text/css" ;; *.js) content_type="application/javascript" ;; *) content_type="text/plain" ;; esac echo "Processing $file -> ${c_name}" # No extern declarations needed - data is accessed through get_embedded_file() # Add to source file_to_c_array "$file" "$c_name" "$SOURCE_FILE" # Store mapping for lookup function file_map["/$basename"]="$c_name:$content_type" if [ "$basename" = "index.html" ]; then file_map["/"]="$c_name:$content_type" fi fi done # Generate lookup function echo "// Embedded file lookup function" >> "$HEADER_FILE" echo "typedef struct {" >> "$HEADER_FILE" echo " const char *path;" >> "$HEADER_FILE" echo " const unsigned char *data;" >> "$HEADER_FILE" echo " size_t size;" >> "$HEADER_FILE" echo " const char *content_type;" >> "$HEADER_FILE" echo "} embedded_file_t;" >> "$HEADER_FILE" echo "" >> "$HEADER_FILE" echo "embedded_file_t *get_embedded_file(const char *path);" >> "$HEADER_FILE" echo "" >> "$HEADER_FILE" echo "#endif // EMBEDDED_WEB_CONTENT_H" >> "$HEADER_FILE" # Generate lookup function implementation echo "// File mapping" >> "$SOURCE_FILE" echo "static embedded_file_t embedded_files[] = {" >> "$SOURCE_FILE" for path in "${!file_map[@]}"; do entry="${file_map[$path]}" c_name="${entry%:*}" content_type="${entry#*:}" echo " {\"$path\", ${c_name}_data, ${c_name}_size, \"$content_type\"}," >> "$SOURCE_FILE" done echo " {NULL, NULL, 0, NULL} // Sentinel" >> "$SOURCE_FILE" echo "};" >> "$SOURCE_FILE" echo "" >> "$SOURCE_FILE" echo "embedded_file_t *get_embedded_file(const char *path) {" >> "$SOURCE_FILE" echo " for (int i = 0; embedded_files[i].path != NULL; i++) {" >> "$SOURCE_FILE" echo " if (strcmp(path, embedded_files[i].path) == 0) {" >> "$SOURCE_FILE" echo " return &embedded_files[i];" >> "$SOURCE_FILE" echo " }" >> "$SOURCE_FILE" echo " }" >> "$SOURCE_FILE" echo " return NULL;" >> "$SOURCE_FILE" echo "}" >> "$SOURCE_FILE" echo "Web file embedding complete. Generated:" >&2 echo " $HEADER_FILE" >&2 echo " $SOURCE_FILE" >&2