Skip to main content

Overview

Part 1 of the PNG assignment requires implementing a command-line argument processing system for the PNG manipulation tool. Your program must use a two-pass approach to validate and process arguments.

Program usage

The PNG tool supports the following command-line syntax:
bin/png -f png_file [options]

Required arguments

-f
string
required
Input PNG file path. Required for all operations except -h.

Optional arguments

-h
flag
Print help message and exit successfully. Ignores all other arguments.
-s
flag
Print chunk summary showing all PNG chunks with type, length, and CRC status.
-p
flag
Print palette summary (only valid for color type 3 images).
-i
flag
Print IHDR header fields (width, height, bit depth, color type, etc.).
-e
string
Encode a message into the image. Requires -o flag.Format: -e "message" -o output.png
-d
flag
Decode and print the hidden message from the image.
-m
string
Overlay another PNG file onto the input. Requires -o flag.Format: -m file2.png -o output.png [-w x] [-g y]
-o
string
Output file path. Required when using -e or -m.
-w
number
X offset (column) for overlay operation. Defaults to 0.
-g
number
Y offset (row) for overlay operation. Defaults to 0.

Two-pass processing

First pass: Validation

The first pass ensures the command-line arguments are structurally valid:
1

Check for help flag

If -h is present anywhere in arguments:
  • Use PRINT_USAGE(argv[0]) macro from global.h
  • Exit with EXIT_SUCCESS
  • Ignore all other arguments
2

Locate -f flag

Search argv for the -f flag:
  • If not found: print error and exit with EXIT_FAILURE
  • If found but no filename follows: print error and exit
  • Extract the filename for later use
3

Basic validation

Verify that:
  • -f has a filename argument
  • No unknown flags are present
  • Required combinations are present (e.g., -e requires -o)

Second pass: Execution

After validation, process operations in the order they appear:
1

Iterate through argv

Loop through command-line arguments from start to end
2

Execute each operation

When you encounter an operation flag:
  • Call the appropriate function
  • Print the specified output
  • Handle any errors
3

Maintain order

Operations execute in the order specified on the command lineExample: bin/png -f file.png -s -i -p prints chunk summary, then IHDR, then palette

Implementation guide

Using the provided macros

The global.h header provides macros for all output. You must use these macros to ensure output format matches specifications.
#include "global.h"

// Print usage
PRINT_USAGE(argv[0]);

// Error messages
PRINT_ERROR_MISSING_F_FLAG();
PRINT_ERROR_F_REQUIRES_FILENAME();
PRINT_ERROR_OPEN_FILE(filename);
PRINT_ERROR_UNKNOWN_OPTION(argv[i]);

// Operation output
PRINT_CHUNK_SUMMARY_HEADER(filename);
PRINT_CHUNK_INFO(i, chunk);
PRINT_IHDR(filename, ihdr);
PRINT_PALETTE_HEADER(filename);
PRINT_PALETTE_COUNT(count);
PRINT_PALETTE_COLOR(i, r, g, b);
PRINT_ENCODE_SUCCESS(output_file);
PRINT_HIDDEN_MESSAGE(buffer);
PRINT_OVERLAY_SUCCESS(output_file);

Argument parsing skeleton

int main(int argc, char **argv) {
    // First pass: Validation
    
    // Check for -h
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-h") == 0) {
            PRINT_USAGE(argv[0]);
            return EXIT_SUCCESS;
        }
    }
    
    // Check for required -f flag
    char *input_file = NULL;
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-f") == 0) {
            if (i + 1 >= argc) {
                PRINT_ERROR_F_REQUIRES_FILENAME();
                return EXIT_FAILURE;
            }
            input_file = argv[i + 1];
            break;
        }
    }
    
    if (input_file == NULL) {
        PRINT_ERROR_MISSING_F_FLAG();
        return EXIT_FAILURE;
    }
    
    // Second pass: Execute operations
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-s") == 0) {
            // Handle chunk summary
        }
        else if (strcmp(argv[i], "-i") == 0) {
            // Handle IHDR printing
        }
        else if (strcmp(argv[i], "-p") == 0) {
            // Handle palette printing
        }
        // ... other operations
    }
    
    return EXIT_SUCCESS;
}

Operation implementations

Chunk summary (-s)

if (strcmp(argv[i], "-s") == 0) {
    png_chunk_t *chunks = NULL;
    int count = png_summary(input_file, &chunks);
    
    if (count < 0) {
        PRINT_ERROR_READ_CHUNKS();
        return EXIT_FAILURE;
    }
    
    PRINT_CHUNK_SUMMARY_HEADER(input_file);
    for (int j = 0; j < count; j++) {
        PRINT_CHUNK_INFO(j, chunks[j]);
    }
    
    free(chunks);
}

IHDR fields (-i)

if (strcmp(argv[i], "-i") == 0) {
    FILE *fp = png_open(input_file);
    if (!fp) {
        PRINT_ERROR_OPEN_FILE(input_file);
        return EXIT_FAILURE;
    }
    
    png_ihdr_t ihdr;
    if (png_extract_ihdr(fp, &ihdr) < 0) {
        PRINT_ERROR_READ_IHDR();
        fclose(fp);
        return EXIT_FAILURE;
    }
    
    PRINT_IHDR(input_file, ihdr);
    fclose(fp);
}

Palette summary (-p)

if (strcmp(argv[i], "-p") == 0) {
    FILE *fp = png_open(input_file);
    if (!fp) {
        PRINT_ERROR_OPEN_FILE(input_file);
        return EXIT_FAILURE;
    }
    
    png_color_t *colors = NULL;
    size_t count = 0;
    
    if (png_extract_plte(fp, &colors, &count) < 0) {
        PRINT_ERROR_PLTE_NOT_FOUND();
        fclose(fp);
        return EXIT_FAILURE;
    }
    
    PRINT_PALETTE_HEADER(input_file);
    PRINT_PALETTE_COUNT(count);
    for (size_t j = 0; j < count; j++) {
        PRINT_PALETTE_COLOR(j, colors[j].r, colors[j].g, colors[j].b);
    }
    
    free(colors);
    fclose(fp);
}

Encode message (-e)

The -e flag requires both a message argument and -o output flag.
if (strcmp(argv[i], "-e") == 0) {
    // Validate arguments
    if (i + 1 >= argc) {
        PRINT_ERROR_ENCODE_REQUIRES();
        return EXIT_FAILURE;
    }
    
    char *message = argv[i + 1];
    i++; // Skip message argument
    
    // Find -o flag
    char *output = NULL;
    for (int j = i + 1; j < argc; j++) {
        if (strcmp(argv[j], "-o") == 0 && j + 1 < argc) {
            output = argv[j + 1];
            break;
        }
    }
    
    if (!output) {
        PRINT_ERROR_ENCODE_REQUIRES();
        return EXIT_FAILURE;
    }
    
    // Call steganography function
    if (png_encode_lsb(input_file, output, message) < 0) {
        PRINT_ERROR_ENCODE_FAILED();
        return EXIT_FAILURE;
    }
    
    PRINT_ENCODE_SUCCESS(output);
}

Decode message (-d)

if (strcmp(argv[i], "-d") == 0) {
    char buffer[1024];
    
    if (png_extract_lsb(input_file, buffer, sizeof(buffer)) < 0) {
        PRINT_ERROR_EXTRACT_FAILED();
        return EXIT_FAILURE;
    }
    
    PRINT_HIDDEN_MESSAGE(buffer);
}

Overlay images (-m)

The -w and -g offset flags are optional and may appear in any order after -o.
if (strcmp(argv[i], "-m") == 0) {
    if (i + 1 >= argc) {
        PRINT_ERROR_OVERLAY_REQUIRES();
        return EXIT_FAILURE;
    }
    
    char *file2 = argv[i + 1];
    i++;
    
    // Find -o flag  
    char *output = NULL;
    int x_offset = 0, y_offset = 0;
    
    for (int j = i + 1; j < argc; j++) {
        if (strcmp(argv[j], "-o") == 0 && j + 1 < argc) {
            output = argv[j + 1];
        }
        else if (strcmp(argv[j], "-w") == 0 && j + 1 < argc) {
            x_offset = atoi(argv[j + 1]);
        }
        else if (strcmp(argv[j], "-g") == 0 && j + 1 < argc) {
            y_offset = atoi(argv[j + 1]);
        }
    }
    
    if (!output) {
        PRINT_ERROR_OVERLAY_REQUIRES();
        return EXIT_FAILURE;
    }
    
    if (png_overlay_paste(input_file, file2, output, x_offset, y_offset) < 0) {
        PRINT_ERROR_OVERLAY_FAILED();
        return EXIT_FAILURE;
    }
    
    PRINT_OVERLAY_SUCCESS(output);
}

Output requirements

Your output must match the specifications EXACTLY. Use only the provided macros from global.h.

Standard output vs. error output

  • stdout: Normal operation results (chunk info, IHDR, palette, success messages)
  • stderr: Error messages only

Exit codes

  • EXIT_SUCCESS (0): Normal completion or help flag
  • EXIT_FAILURE (1): Any error condition

No extraneous output

// WRONG - adds debugging output
printf("Reading file: %s\n", filename);
FILE *fp = fopen(filename, "rb");

// CORRECT - use debug macro (only shown in debug builds)
debug("Reading file: %s\n", filename);
FILE *fp = fopen(filename, "rb");

Example invocations

Help

$ bin/png -h
Usage: bin/png -f png_file [options]
Options:
  -f png_file           Input PNG file (required)
  -h                    Print this help message
  -s                    Print chunk summary
  ...

Chunk summary

$ bin/png -f image.png -s
Chunk Summary for image.png:
  Chunk 0: Type=IHDR, Length=13, CRC=valid
  Chunk 1: Type=PLTE, Length=504, CRC=valid
  Chunk 2: Type=IDAT, Length=8192, CRC=valid
  Chunk 3: Type=IEND, Length=0, CRC=valid

Multiple operations

$ bin/png -f image.png -i -p
IHDR Fields for image.png:
  Width: 320
  Height: 320
  Bit Depth: 8
  Color Type: 3 (Palette)
  Compression: 0
  Filter: 0
  Interlace: 0 (None)
Palette Summary for image.png:
  Number of colors: 168
  Color 0: RGB(1, 0, 5)
  Color 1: RGB(4, 0, 6)
  ...

Error handling

$ bin/png image.png
Error: -f flag with filename is required

$ bin/png -f
Error: -f requires a filename

$ bin/png -f missing.png -s
Error: Failed to open file missing.png

Common pitfalls

After processing a flag that takes a value, increment i to skip the value:
if (strcmp(argv[i], "-e") == 0) {
    char *message = argv[i + 1];
    i++; // CRITICAL: Skip the message argument
}
Always check that enough arguments remain:
if (strcmp(argv[i], "-f") == 0) {
    if (i + 1 >= argc) {
        // No filename after -f!
        PRINT_ERROR_F_REQUIRES_FILENAME();
        return EXIT_FAILURE;
    }
    input_file = argv[i + 1];
}
Both -e and -m require -o. Check for it:
if (!output) {
    PRINT_ERROR_ENCODE_REQUIRES();
    return EXIT_FAILURE;
}

Testing your implementation

Test various argument combinations:
# Valid operations
bin/png -h
bin/png -f image.png -s
bin/png -f image.png -i -p -s
bin/png -f image.png -e "Hello" -o out.png
bin/png -f image.png -m small.png -o out.png -w 10 -g 20

# Error conditions
bin/png                          # Missing -f
bin/png -f                       # -f missing filename
bin/png -f missing.png -s        # File doesn't exist
bin/png -f image.png -e "Hi"     # -e missing -o
bin/png -f image.png -x          # Unknown flag

Next steps

PNG format

Learn about PNG file structure

Chunk reading

Implement PNG chunk reading

Build docs developers (and LLMs) love