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
Input PNG file path. Required for all operations except -h.
Optional arguments
Print help message and exit successfully. Ignores all other arguments.
Print chunk summary showing all PNG chunks with type, length, and CRC status.
Print palette summary (only valid for color type 3 images).
Print IHDR header fields (width, height, bit depth, color type, etc.).
Encode a message into the image. Requires -o flag. Format: -e "message" -o output.png
Decode and print the hidden message from the image.
Overlay another PNG file onto the input. Requires -o flag. Format: -m file2.png -o output.png [-w x] [-g y]
Output file path. Required when using -e or -m.
X offset (column) for overlay operation. Defaults to 0.
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:
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
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
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:
Iterate through argv
Loop through command-line arguments from start to end
Execute each operation
When you encounter an operation flag:
Call the appropriate function
Print the specified output
Handle any errors
Maintain order
Operations execute in the order specified on the command line Example: 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
// 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
Not skipping argument values
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
}
Not handling missing arguments
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