修炼者
修炼者
发布于 2026-05-20 / 0 阅读
0
0

永远的C语言

环境准备

更新源

# 更新系统源
sudo apt update && sudo apt upgrade -y
# 安装tree
sudo apt install tree

安装clang

# clang
sudo apt update
sudo apt install -y clang  lld

安装ninja

# ninja
sudo apt install -y ninja-build

安装musl

# musl
sudo apt install -y musl musl-dev musl-tools

静态编译

ubuntu下使用gcc、clang分别静态和动态编译,并分别结合musl库进行静态与动态编译。如下

helloworld.c

// cat helloworld.c
#include <stdio.h>

int main() {
    printf("Hello, World!\n");
    return 0;
}

CMakeList.txt

cmake_minimum_required(VERSION 3.10)
project(HelloWorldComparison LANGUAGES C)

# Find compilers
find_program(GCC gcc)
find_program(CLANG clang)
find_program(MUSL_GCC musl-gcc)

# Check for musl headers and libraries (for clang+musl support)
if(EXISTS "/usr/include/x86_64-linux-musl" AND EXISTS "/usr/lib/x86_64-linux-musl")
    set(MUSL_CLANG_AVAILABLE TRUE)
    set(MUSL_INCLUDE_DIR "/usr/include/x86_64-linux-musl")
    set(MUSL_LIB_DIR "/usr/lib/x86_64-linux-musl")
else()
    set(MUSL_CLANG_AVAILABLE FALSE)
endif()

message(STATUS "=== Compiler Detection ===")
message(STATUS "GCC: ${GCC}")
message(STATUS "Clang: ${CLANG}")
message(STATUS "musl-gcc: ${MUSL_GCC}")
message(STATUS "Clang+musl support: ${MUSL_CLANG_AVAILABLE}")
message(STATUS "========================")

# ============================================================================
# GCC builds (using default compiler which is typically gcc)
# ============================================================================

# 1. GCC + glibc dynamic
add_executable(hello_gcc_glibc_dynamic helloworld.c)

# 2. GCC + glibc static
add_executable(hello_gcc_glibc_static helloworld.c)
target_link_libraries(hello_gcc_glibc_static PRIVATE -static)

# ============================================================================
# Clang builds
# ============================================================================

if(CLANG)
    # 3. Clang + glibc dynamic
    add_custom_command(
        OUTPUT ${CMAKE_BINARY_DIR}/hello_clang_glibc_dynamic
        COMMAND ${CLANG} -o ${CMAKE_BINARY_DIR}/hello_clang_glibc_dynamic ${CMAKE_SOURCE_DIR}/helloworld.c
        DEPENDS ${CMAKE_SOURCE_DIR}/helloworld.c
        COMMENT "Building clang+glibc dynamic executable"
    )
    add_custom_target(build_clang_glibc_dynamic ALL DEPENDS ${CMAKE_BINARY_DIR}/hello_clang_glibc_dynamic)
    
    # 4. Clang + glibc static
    add_custom_command(
        OUTPUT ${CMAKE_BINARY_DIR}/hello_clang_glibc_static
        COMMAND ${CLANG} -static -o ${CMAKE_BINARY_DIR}/hello_clang_glibc_static ${CMAKE_SOURCE_DIR}/helloworld.c
        DEPENDS ${CMAKE_SOURCE_DIR}/helloworld.c
        COMMENT "Building clang+glibc static executable"
    )
    add_custom_target(build_clang_glibc_static ALL DEPENDS ${CMAKE_BINARY_DIR}/hello_clang_glibc_static)
else()
    message(WARNING "clang not found. Skipping clang builds.")
endif()

# ============================================================================
# musl-gcc builds
# ============================================================================

if(MUSL_GCC)
    # 5. musl-gcc + musl dynamic (musl prefers static, but we can try dynamic)
    add_custom_command(
        OUTPUT ${CMAKE_BINARY_DIR}/hello_muslgcc_musl_dynamic
        COMMAND ${MUSL_GCC} -o ${CMAKE_BINARY_DIR}/hello_muslgcc_musl_dynamic ${CMAKE_SOURCE_DIR}/helloworld.c
        DEPENDS ${CMAKE_SOURCE_DIR}/helloworld.c
        COMMENT "Building musl-gcc+musl dynamic executable"
    )
    add_custom_target(build_muslgcc_musl_dynamic ALL DEPENDS ${CMAKE_BINARY_DIR}/hello_muslgcc_musl_dynamic)
    
    # 6. musl-gcc + musl static
    add_custom_command(
        OUTPUT ${CMAKE_BINARY_DIR}/hello_muslgcc_musl_static
        COMMAND ${MUSL_GCC} -static -o ${CMAKE_BINARY_DIR}/hello_muslgcc_musl_static ${CMAKE_SOURCE_DIR}/helloworld.c
        DEPENDS ${CMAKE_SOURCE_DIR}/helloworld.c
        COMMENT "Building musl-gcc+musl static executable"
    )
    add_custom_target(build_muslgcc_musl_static ALL DEPENDS ${CMAKE_BINARY_DIR}/hello_muslgcc_musl_static)
else()
    message(WARNING "musl-gcc not found. Skipping musl-gcc builds.")
endif()

# ============================================================================
# Clang + musl builds
# ============================================================================

if(CLANG AND MUSL_CLANG_AVAILABLE)
    # 7. Clang + musl dynamic
    add_custom_command(
        OUTPUT ${CMAKE_BINARY_DIR}/hello_clang_musl_dynamic
        COMMAND ${CLANG} -target x86_64-linux-musl -nostdinc -isystem ${MUSL_INCLUDE_DIR} -L${MUSL_LIB_DIR} ${CMAKE_SOURCE_DIR}/helloworld.c -lc -o ${CMAKE_BINARY_DIR}/hello_clang_musl_dynamic
        DEPENDS ${CMAKE_SOURCE_DIR}/helloworld.c
        COMMENT "Building clang+musl dynamic executable"
    )
    add_custom_target(build_clang_musl_dynamic ALL DEPENDS ${CMAKE_BINARY_DIR}/hello_clang_musl_dynamic)
    
    # 8. Clang + musl static
    add_custom_command(
        OUTPUT ${CMAKE_BINARY_DIR}/hello_clang_musl_static
        COMMAND ${CLANG} -target x86_64-linux-musl -static -nostdinc -isystem ${MUSL_INCLUDE_DIR} -L${MUSL_LIB_DIR} ${CMAKE_SOURCE_DIR}/helloworld.c -lc -o ${CMAKE_BINARY_DIR}/hello_clang_musl_static
        DEPENDS ${CMAKE_SOURCE_DIR}/helloworld.c
        COMMENT "Building clang+musl static executable"
    )
    add_custom_target(build_clang_musl_static ALL DEPENDS ${CMAKE_BINARY_DIR}/hello_clang_musl_static)
elseif(CLANG AND NOT MUSL_CLANG_AVAILABLE)
    message(WARNING "musl headers/libraries not found. Skipping clang+musl builds.")
endif()

构建

# 使用make构建
cmake -B build 
cmake --build build

# 使用ninja构建
cmake -B build
cmake --build build

效果

$ tree -L 2
.
├── build
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   ├── cmake_install.cmake
│   ├── hello_clang_dynamic
│   ├── hello_clang_static
│   ├── hello_glibc_dynamic
│   ├── hello_glibc_static
│   ├── hello_musl_static
│   └── Makefile
├── CMakeLists.txt
├── helloworld.c
├── ninja
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   ├── cmake_install.cmake
│   ├── hello_clang_glibc_dynamic
│   ├── hello_clang_glibc_static
│   ├── hello_clang_musl_dynamic
│   ├── hello_clang_musl_static
│   ├── hello_gcc_glibc_dynamic
│   ├── hello_gcc_glibc_static
│   ├── hello_muslgcc_musl_dynamic
│   ├── hello_muslgcc_musl_static
│   └── Makefile
└── requiremetn.md

5 directories, 22 files

单文件发行

将所需要的配置文件、Web文件都打包进入目标文件。可以使用多种方式objcopy是不错的一种。还可以结合tar或者自定义的hash方式预处理形成目标文件.o,链接进入发行包。

加入配置文件

目录结构

$ tree
.
├── CMakeLists.txt
├── data
│   ├── a.html
│   ├── a.ini
│   └── a.json
├── main.c

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* 
 * Embedded data files - these will be linked into the executable
 * The data is embedded using objcopy or linker scripts
 */

// Declare external symbols for embedded data
// These symbols are created by objcopy when embedding binary files
// Format: _binary_<filename>_<start|end|size>
extern const char _binary_a_html_start[];
extern const char _binary_a_html_end[];
extern const char _binary_a_json_start[];
extern const char _binary_a_json_end[];
extern const char _binary_a_ini_start[];
extern const char _binary_a_ini_end[];

// Function to print usage
void print_usage(const char *program_name) {
    printf("Usage: %s [option]\n", program_name);
    printf("Options:\n");
    printf("  --html    Output embedded HTML file\n");
    printf("  --json    Output embedded JSON file\n");
    printf("  --ini     Output embedded INI file\n");
    printf("  --list    List all embedded files\n");
    printf("  --help    Show this help message\n");
}

// Function to output embedded file content
void output_embedded_file(const char *name, const char *start, const char *end) {
    size_t size = end - start;
    printf("=== %s (size: %zu bytes) ===\n", name, size);
    fwrite(start, 1, size, stdout);
    printf("\n");
}

// Function to list embedded files
void list_embedded_files() {
    printf("Embedded files:\n");
    printf("  1. a.html (%zu bytes)\n", _binary_a_html_end - _binary_a_html_start);
    printf("  2. a.json (%zu bytes)\n", _binary_a_json_end - _binary_a_json_start);
    printf("  3. a.ini  (%zu bytes)\n", _binary_a_ini_end - _binary_a_ini_start);
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        print_usage(argv[0]);
        return 1;
    }

    if (strcmp(argv[1], "--html") == 0) {
        output_embedded_file("a.html", _binary_a_html_start, _binary_a_html_end);
    } else if (strcmp(argv[1], "--json") == 0) {
        output_embedded_file("a.json", _binary_a_json_start, _binary_a_json_end);
    } else if (strcmp(argv[1], "--ini") == 0) {
        output_embedded_file("a.ini", _binary_a_ini_start, _binary_a_ini_end);
    } else if (strcmp(argv[1], "--list") == 0) {
        list_embedded_files();
    } else if (strcmp(argv[1], "--help") == 0) {
        print_usage(argv[0]);
    } else {
        fprintf(stderr, "Unknown option: %s\n", argv[1]);
        print_usage(argv[0]);
        return 1;
    }

    return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(DataInExe LANGUAGES C)

# Find compilers
find_program(MUSL_GCC musl-gcc)
find_program(CLANG clang)

# Check for musl support
if(EXISTS "/usr/include/x86_64-linux-musl" AND EXISTS "/usr/lib/x86_64-linux-musl")
    set(MUSL_CLANG_AVAILABLE TRUE)
    set(MUSL_INCLUDE_DIR "/usr/include/x86_64-linux-musl")
    set(MUSL_LIB_DIR "/usr/lib/x86_64-linux-musl")
endif()

message(STATUS "musl-gcc: ${MUSL_GCC}")
message(STATUS "clang: ${CLANG}")
message(STATUS "clang+musl: ${MUSL_CLANG_AVAILABLE}")

# Define data directory
set(DATA_DIR ${CMAKE_SOURCE_DIR}/data)

# Embed data files using objcopy
function(embed_data_file INPUT_FILE OUTPUT_OBJ SYMBOL_PREFIX)
    get_filename_component(FILENAME ${INPUT_FILE} NAME)
    set(SIMPLE_INPUT ${CMAKE_BINARY_DIR}/${FILENAME})
    
    add_custom_command(
        OUTPUT ${OUTPUT_OBJ}
        COMMAND ${CMAKE_COMMAND} -E copy ${INPUT_FILE} ${SIMPLE_INPUT}
        COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR} 
                objcopy --input binary --output elf64-x86-64 
                --rename-section .data=.rodata,alloc,load,readonly,data,contents 
                --redefine-sym _binary_${FILENAME}_start=${SYMBOL_PREFIX}_start
                --redefine-sym _binary_${FILENAME}_end=${SYMBOL_PREFIX}_end
                --redefine-sym _binary_${FILENAME}_size=${SYMBOL_PREFIX}_size
                --add-section .note.GNU-stack=/dev/null
                --set-section-flags .note.GNU-stack=readonly
                ${FILENAME} ${OUTPUT_OBJ}
        DEPENDS ${INPUT_FILE}
        COMMENT "Embedding ${FILENAME}"
    )
endfunction()

embed_data_file(${DATA_DIR}/a.html ${CMAKE_BINARY_DIR}/a_html.o data_a_html)
embed_data_file(${DATA_DIR}/a.json ${CMAKE_BINARY_DIR}/a_json.o data_a_json)
embed_data_file(${DATA_DIR}/a.ini ${CMAKE_BINARY_DIR}/a_ini.o data_a_ini)

# Build with musl-gcc
if(MUSL_GCC)
    add_custom_command(
        OUTPUT ${CMAKE_BINARY_DIR}/main_muslgcc.o
        COMMAND ${MUSL_GCC} -c ${CMAKE_SOURCE_DIR}/main.c -o ${CMAKE_BINARY_DIR}/main_muslgcc.o
        DEPENDS ${CMAKE_SOURCE_DIR}/main.c
        COMMENT "Compiling with musl-gcc"
    )
    
    add_custom_command(
        OUTPUT ${CMAKE_BINARY_DIR}/data_in_exe_muslgcc_static
        COMMAND ${MUSL_GCC} -static 
                ${CMAKE_BINARY_DIR}/main_muslgcc.o
                ${CMAKE_BINARY_DIR}/a_html.o
                ${CMAKE_BINARY_DIR}/a_json.o
                ${CMAKE_BINARY_DIR}/a_ini.o
                -o ${CMAKE_BINARY_DIR}/data_in_exe_muslgcc_static
        DEPENDS ${CMAKE_BINARY_DIR}/main_muslgcc.o
                ${CMAKE_BINARY_DIR}/a_html.o
                ${CMAKE_BINARY_DIR}/a_json.o
                ${CMAKE_BINARY_DIR}/a_ini.o
        COMMENT "Linking musl-gcc static"
    )
    add_custom_target(build_muslgcc_static ALL 
        DEPENDS ${CMAKE_BINARY_DIR}/data_in_exe_muslgcc_static
    )
endif()

# Build with clang+musl
if(CLANG AND MUSL_CLANG_AVAILABLE)
    add_custom_command(
        OUTPUT ${CMAKE_BINARY_DIR}/main_clang_musl.o
        COMMAND ${CLANG} -target x86_64-linux-musl -nostdinc -isystem ${MUSL_INCLUDE_DIR} 
                -c ${CMAKE_SOURCE_DIR}/main.c -o ${CMAKE_BINARY_DIR}/main_clang_musl.o
        DEPENDS ${CMAKE_SOURCE_DIR}/main.c
        COMMENT "Compiling with clang+musl"
    )
    
    add_custom_command(
        OUTPUT ${CMAKE_BINARY_DIR}/data_in_exe_clang_musl_static
        COMMAND ${CLANG} -target x86_64-linux-musl -static -nostdinc -isystem ${MUSL_INCLUDE_DIR} 
                -L${MUSL_LIB_DIR} 
                ${CMAKE_BINARY_DIR}/main_clang_musl.o
                ${CMAKE_BINARY_DIR}/a_html.o
                ${CMAKE_BINARY_DIR}/a_json.o
                ${CMAKE_BINARY_DIR}/a_ini.o
                -lc -o ${CMAKE_BINARY_DIR}/data_in_exe_clang_musl_static
        DEPENDS ${CMAKE_BINARY_DIR}/main_clang_musl.o
                ${CMAKE_BINARY_DIR}/a_html.o
                ${CMAKE_BINARY_DIR}/a_json.o
                ${CMAKE_BINARY_DIR}/a_ini.o
        COMMENT "Linking clang+musl static"
    )
    add_custom_target(build_clang_musl_static ALL 
        DEPENDS ${CMAKE_BINARY_DIR}/data_in_exe_clang_musl_static
    )
endif()



message(STATUS "")
message(STATUS "Build targets:")
if(MUSL_GCC)
    message(STATUS "  - data_in_exe_muslgcc_static")
endif()
if(CLANG AND MUSL_CLANG_AVAILABLE)
    message(STATUS "  - data_in_exe_clang_musl_static")
endif()
message(STATUS "")

加入tar组装的web目录

使用tar将web目录打包为一个整块,然后用objcopy将其构建成.o,链接进入发行包。

目录结构

$ tree
.
├── CMakeLists.txt
├── htdocs
│   ├── css
│   │   └── index.css
│   ├── img
│   │   └── image.png
│   ├── index.html
│   └── js
│       ├── index.js
│       └── test.js
├── main.c

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

/* 
 * Embedded tar archive - this will be linked into the executable
 * The tar file is created from the htdocs directory and embedded using objcopy
 */

// Declare external symbols for embedded tar data
extern const char _binary_htdocs_tar_start[];
extern const char _binary_htdocs_tar_end[];

// TAR header structure (POSIX ustar format)
typedef struct {
    char name[100];
    char mode[8];
    char uid[8];
    char gid[8];
    char size[12];
    char mtime[12];
    char checksum[8];
    char typeflag;
    char linkname[100];
    char magic[6];
    char version[2];
    char uname[32];
    char gname[32];
    char devmajor[8];
    char devminor[8];
    char prefix[155];
    char padding[12];
} tar_header_t;

// Function to convert octal string to integer
static int octal_to_int(const char *str, int len) {
    int result = 0;
    for (int i = 0; i < len && str[i] != '\0' && str[i] != ' '; i++) {
        if (str[i] >= '0' && str[i] <= '7') {
            result = result * 8 + (str[i] - '0');
        }
    }
    return result;
}

// Function to find a file in the tar archive
static const char* find_file_in_tar(const char *tar_data, size_t tar_size, 
                                     const char *filename, size_t *file_size) {
    const char *ptr = tar_data;
    const char *end = tar_data + tar_size;
    
    while (ptr < end) {
        tar_header_t *header = (tar_header_t *)ptr;
        
        // Check for end of archive (two consecutive zero blocks)
        if (header->name[0] == '\0') {
            break;
        }
        
        // Calculate file size from header
        size_t size = octal_to_int(header->size, sizeof(header->size));
        
        // Get filename from header
        char entry_name[256] = {0};
        if (header->prefix[0] != '\0') {
            strncpy(entry_name, header->prefix, sizeof(header->prefix));
            strncat(entry_name, "/", sizeof(entry_name) - strlen(entry_name) - 1);
        }
        strncat(entry_name, header->name, sizeof(entry_name) - strlen(entry_name) - 1);
        
        // Remove trailing slash for directories
        size_t name_len = strlen(entry_name);
        if (name_len > 0 && entry_name[name_len - 1] == '/') {
            entry_name[name_len - 1] = '\0';
        }
        
        // Check if this is the file we're looking for
        if (strcmp(entry_name, filename) == 0 && header->typeflag == '0') {
            // Regular file found
            *file_size = size;
            return ptr + 512; // Data starts after the 512-byte header
        }
        
        // Move to next entry (header + data rounded up to 512 bytes)
        size_t data_size = (size + 511) & ~511; // Round up to nearest 512
        ptr += 512 + data_size;
    }
    
    return NULL; // File not found
}

// Function to list all files in the tar archive
static void list_tar_files(const char *tar_data, size_t tar_size) {
    const char *ptr = tar_data;
    const char *end = tar_data + tar_size;
    int file_count = 0;
    
    printf("Files in embedded htdocs:\n");
    
    while (ptr < end) {
        tar_header_t *header = (tar_header_t *)ptr;
        
        // Check for end of archive
        if (header->name[0] == '\0') {
            break;
        }
        
        // Get filename from header
        char entry_name[256] = {0};
        if (header->prefix[0] != '\0') {
            strncpy(entry_name, header->prefix, sizeof(header->prefix));
            strncat(entry_name, "/", sizeof(entry_name) - strlen(entry_name) - 1);
        }
        strncat(entry_name, header->name, sizeof(entry_name) - strlen(entry_name) - 1);
        
        // Calculate file size
        size_t size = octal_to_int(header->size, sizeof(header->size));
        
        // Determine file type
        const char *type_str = "unknown";
        switch (header->typeflag) {
            case '0': type_str = "file"; break;
            case '5': type_str = "dir"; break;
            default: type_str = "other"; break;
        }
        
        printf("  [%s] %s (%zu bytes)\n", type_str, entry_name, size);
        file_count++;
        
        // Move to next entry
        size_t data_size = (size + 511) & ~511;
        ptr += 512 + data_size;
    }
    
    printf("\nTotal: %d entries\n", file_count);
}

// Function to print usage
static void print_usage(const char *program_name) {
    printf("Usage: %s [option] [filepath]\n", program_name);
    printf("\nOptions:\n");
    printf("  --list              List all files in embedded htdocs\n");
    printf("  --get <filepath>    Extract and display file content\n");
    printf("  --hmi               Start interactive HMI mode\n");
    printf("  --help              Show this help message\n");
    printf("\nExamples:\n");
    printf("  %s --list\n", program_name);
    printf("  %s --get index.html\n", program_name);
    printf("  %s --get css/index.css\n", program_name);
    printf("  %s --get js/index.js\n", program_name);
    printf("  %s --hmi\n", program_name);
}

// Function to check if a file is binary (contains non-text characters)
static int is_binary_file(const char *data, size_t size) {
    // Check first 512 bytes or entire file if smaller
    size_t check_size = size < 512 ? size : 512;
    for (size_t i = 0; i < check_size; i++) {
        unsigned char c = (unsigned char)data[i];
        // Common text characters: printable ASCII, tab, newline, carriage return
        if (c < 32 && c != '\t' && c != '\n' && c != '\r') {
            return 1; // Binary file
        }
    }
    return 0; // Text file
}

// Function to display file content in hex dump format (similar to xxd)
static void hex_dump(const char *data, size_t size) {
    size_t offset = 0;
    while (offset < size) {
        // Print offset
        printf("%08lx: ", (unsigned long)offset);
        
        // Print hex bytes
        for (int i = 0; i < 16; i++) {
            if (offset + i < size) {
                printf("%02x ", (unsigned char)data[offset + i]);
            } else {
                printf("   ");
            }
            if (i == 7) printf(" "); // Extra space in the middle
        }
        
        // Print ASCII representation
        printf(" |");
        for (int i = 0; i < 16; i++) {
            if (offset + i < size) {
                unsigned char c = (unsigned char)data[offset + i];
                if (c >= 32 && c <= 126) {
                    printf("%c", c);
                } else {
                    printf(".");
                }
            }
        }
        printf("|\n");
        
        offset += 16;
    }
    printf("Total: %zu bytes\n", size);
}

// Interactive HMI mode
static void interactive_mode(const char *tar_data, size_t tar_size) {
    char input[1024];
    char filepath[1100];  // Buffer for filepath with htdocs/ prefix
    
    printf("=== Htdocs Interactive Mode ===\n");
    printf("Enter file path to view content (e.g., index.html, css/style.css)\n");
    printf("Type '!exit' to quit\n");
    printf("Type '!list' to see available files\n");
    printf("================================\n\n");
    
    while (1) {
        printf("htdocs> ");
        fflush(stdout);
        
        // Read input
        if (fgets(input, sizeof(input), stdin) == NULL) {
            break;
        }
        
        // Remove trailing newline
        size_t len = strlen(input);
        if (len > 0 && input[len - 1] == '\n') {
            input[len - 1] = '\0';
            len--;
        }
        
        // Skip empty input
        if (len == 0) {
            continue;
        }
        
        // Check for exit command
        if (strcmp(input, "!exit") == 0) {
            printf("Exiting interactive mode. Goodbye!\n");
            break;
        }
        
        // Check for list command
        if (strcmp(input, "!list") == 0) {
            list_tar_files(tar_data, tar_size);
            printf("\n");
            continue;
        }
        
        // Add htdocs/ prefix if not already present
        if (strncmp(input, "htdocs/", 7) != 0) {
            snprintf(filepath, sizeof(filepath), "htdocs/%s", input);
        } else {
            strncpy(filepath, input, sizeof(filepath) - 1);
            filepath[sizeof(filepath) - 1] = '\0';
        }
        
        // Try to find and display the file
        size_t file_size = 0;
        const char *file_data = find_file_in_tar(tar_data, tar_size, filepath, &file_size);
        
        if (file_data == NULL) {
            fprintf(stderr, "Error: File '%s' not found in embedded htdocs\n", input);
            printf("\n");
            continue;
        }
        
        // Display file information
        printf("\n--- File: %s (%zu bytes) ---\n", input, file_size);
        
        // Check if binary or text
        if (is_binary_file(file_data, file_size)) {
            printf("Binary file detected. Displaying hex dump:\n\n");
            hex_dump(file_data, file_size);
        } else {
            printf("Text file content:\n\n");
            fwrite(file_data, 1, file_size, stdout);
            // Ensure output ends with newline
            if (file_size > 0 && file_data[file_size - 1] != '\n') {
                printf("\n");
            }
        }
        
        printf("\n--- End of file ---\n\n");
    }
}

int main(int argc, char *argv[]) {
    const char *tar_data = _binary_htdocs_tar_start;
    size_t tar_size = _binary_htdocs_tar_end - _binary_htdocs_tar_start;
    
    if (argc < 2) {
        print_usage(argv[0]);
        return 1;
    }
    
    if (strcmp(argv[1], "--help") == 0) {
        print_usage(argv[0]);
        return 0;
    }
    
    if (strcmp(argv[1], "--list") == 0) {
        list_tar_files(tar_data, tar_size);
        return 0;
    }
    
    if (strcmp(argv[1], "--hmi") == 0) {
        interactive_mode(tar_data, tar_size);
        return 0;
    }
    
    if (strcmp(argv[1], "--get") == 0) {
        if (argc < 3) {
            fprintf(stderr, "Error: --get requires a filepath argument\n");
            print_usage(argv[0]);
            return 1;
        }
        
        const char *input_path = argv[2];
        char filepath[1100];
        
        // Add htdocs/ prefix if not already present
        if (strncmp(input_path, "htdocs/", 7) != 0) {
            snprintf(filepath, sizeof(filepath), "htdocs/%s", input_path);
        } else {
            strncpy(filepath, input_path, sizeof(filepath) - 1);
            filepath[sizeof(filepath) - 1] = '\0';
        }
        
        size_t file_size = 0;
        const char *file_data = find_file_in_tar(tar_data, tar_size, filepath, &file_size);
        
        if (file_data == NULL) {
            fprintf(stderr, "Error: File '%s' not found in embedded htdocs\n", input_path);
            return 1;
        }
        
        // Output file content
        fwrite(file_data, 1, file_size, stdout);
        return 0;
    }
    
    fprintf(stderr, "Error: Unknown option '%s'\n", argv[1]);
    print_usage(argv[0]);
    return 1;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(HtdocsInExe LANGUAGES C)

# Find compilers
find_program(CLANG clang)

# Check for musl support
if(EXISTS "/usr/include/x86_64-linux-musl" AND EXISTS "/usr/lib/x86_64-linux-musl")
    set(MUSL_CLANG_AVAILABLE TRUE)
    set(MUSL_INCLUDE_DIR "/usr/include/x86_64-linux-musl")
    set(MUSL_LIB_DIR "/usr/lib/x86_64-linux-musl")
endif()

message(STATUS "clang: ${CLANG}")
message(STATUS "clang+musl: ${MUSL_CLANG_AVAILABLE}")

# Define htdocs directory
set(HTDOCS_DIR ${CMAKE_SOURCE_DIR}/htdocs)

# Automatically find all files in htdocs directory
file(GLOB_RECURSE HTDOCS_FILES RELATIVE ${HTDOCS_DIR} "${HTDOCS_DIR}/*")

# Create tar archive from htdocs directory
set(TAR_FILE ${CMAKE_BINARY_DIR}/htdocs.tar)

# Convert relative paths to absolute paths for dependencies
set(HTDOCS_FILES_ABS "")
foreach(FILE ${HTDOCS_FILES})
    list(APPEND HTDOCS_FILES_ABS "${HTDOCS_DIR}/${FILE}")
endforeach()

add_custom_command(
    OUTPUT ${TAR_FILE}
    COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_SOURCE_DIR} 
            tar cf ${TAR_FILE} htdocs
    DEPENDS ${HTDOCS_FILES_ABS}
    COMMENT "Creating tar archive of htdocs directory (${HTDOCS_FILES} files)"
)

# Embed tar file using objcopy
set(TAR_OBJ ${CMAKE_BINARY_DIR}/htdocs_tar.o)

add_custom_command(
    OUTPUT ${TAR_OBJ}
    COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR} 
            objcopy --input binary --output elf64-x86-64 
            --rename-section .data=.rodata,alloc,load,readonly,data,contents 
            --redefine-sym _binary_htdocs_tar_start=_binary_htdocs_tar_start
            --redefine-sym _binary_htdocs_tar_end=_binary_htdocs_tar_end
            --redefine-sym _binary_htdocs_tar_size=_binary_htdocs_tar_size
            --add-section .note.GNU-stack=/dev/null
            --set-section-flags .note.GNU-stack=readonly
            htdocs.tar ${TAR_OBJ}
    DEPENDS ${TAR_FILE}
    COMMENT "Embedding htdocs.tar into object file"
)

# Build with clang+musl
if(CLANG AND MUSL_CLANG_AVAILABLE)
    add_custom_command(
        OUTPUT ${CMAKE_BINARY_DIR}/main_clang_musl.o
        COMMAND ${CLANG} -target x86_64-linux-musl -nostdinc -isystem ${MUSL_INCLUDE_DIR} 
                -c ${CMAKE_SOURCE_DIR}/main.c -o ${CMAKE_BINARY_DIR}/main_clang_musl.o
        DEPENDS ${CMAKE_SOURCE_DIR}/main.c
        COMMENT "Compiling with clang+musl"
    )
    
    add_custom_command(
        OUTPUT ${CMAKE_BINARY_DIR}/htdocs_in_exe_clang_musl_static
        COMMAND ${CLANG} -target x86_64-linux-musl -static -nostdinc -isystem ${MUSL_INCLUDE_DIR} 
                -L${MUSL_LIB_DIR} 
                ${CMAKE_BINARY_DIR}/main_clang_musl.o
                ${TAR_OBJ}
                -lc -o ${CMAKE_BINARY_DIR}/htdocs_in_exe_clang_musl_static
        DEPENDS ${CMAKE_BINARY_DIR}/main_clang_musl.o
                ${TAR_OBJ}
        COMMENT "Linking clang+musl static executable"
    )
    
    add_custom_target(build_clang_musl_static ALL 
        DEPENDS ${CMAKE_BINARY_DIR}/htdocs_in_exe_clang_musl_static
    )
endif()

message(STATUS "")
message(STATUS "Build targets:")
if(CLANG AND MUSL_CLANG_AVAILABLE)
    message(STATUS "  - htdocs_in_exe_clang_musl_static")
endif()
message(STATUS "")


评论