Last Updated: 2026-02-08 Sun 15:50

CMSC216 Project 1: C Programming

CODE DISTRIBUTION: p1-code.zip

VIDEO OVERVIEW: https://umd.instructure.com/courses/1398391/pages/week-02-videos

CHANGELOG:

Sun Feb 8 03:17:58 PM EST 2026

The project specification has been amended to include a missing section on Work Disclosure that all students must complete for full credit. A completed WORK_DISCLOSURE.txt needs to be present in the project directory with information how what collaborators and resources were used to complete the project. The template for this file is now in the project codepack and those who have already started the project can get it by updating via

  >> cd p1-code
  >> make update

Students that have already started P1 should run make update to get the updated files and may need to resubmit if they have done so already.

Sun Feb 8 01:42:04 PM EST 2026

Post 48 reported a problem in glyph_init(), a provided function that is missing a line. This has been fixed in the codepack for students who have not yet downloaded p1-code.zip. Students who have already started the project can add the line marked MISSING in manually below or just delete the entire glyph_init() function and replace it with this updated version:

// PROVIDED: Initializes a glyph to mostly X's except for its
// codepoint on the first line.
void glyph_init(glyph_t *glyph, int codepoint){
  glyph->is_set = 0;              // MISSING in original posting
  glyph->codepoint = codepoint;
  glyph->width = 6;
  for(int i=0; i<MAX_HEIGHT; i++){
    for(int j=0; j<MAX_WIDTH; j++){
      if(j == glyph->width){
        glyph->data[i][j] = '\0'; // null terminate
      }
      else{
        glyph->data[i][j] = 'X';
      }
    }
  }
  int len = sprintf((char *)glyph->data, "%d",codepoint); // add codepoint # to glyph
  glyph->data[0][len] = 'X';                              // remove null termination char
}        

1 Introduction

Basic application programming in C is an essential step downward towards the lower levels of computing. This project explores fundamental aspects of getting work done in C:

  • Dynamic memory management with malloc()/free()
  • Reading data from files in text format
  • Displaying information to the screen
  • Building data structures with C structs and pointers

The assignment is divided into several problems utilizing many of the above techniques.

  • Problem 1 is a warm up with some basic string and printing routines to implement.
  • Problem 2 builds on the previous routines to complete a banner letter printing function as well as deal with loading fonts and text files
  • Problem 3 completes the application by coding a main() function to employ the preceding functions to display banner text in a terminal.

Problems build on each other and should be done in order to complete the project.

1.1 Grading Criteria

Credit for this assignment will be given based on two categories.

  • Manual Inspection Criteria (~60%): Each problem has a checklist of things that graders will look for. The checklist is in the spec and often contains hints on what to do. Make sure you have a look at these.
  • Automated Testing (~40%): Each problem has tests associated with it along with instructions on how to run those tests from the command line. Tests require that code compiles and runs according to the descriptions given so make sure you verify that these work.

1.2 Getting Started

Take the following steps to get started

  1. Download the code associated with the project linked at the top of the spec. Unzip it and examine some of the provided code.
  2. Examine the overview of the files provided listed in the Download and Setup section. This gives brief descriptions of files that already exist and those that you must create.
  3. Pick a problem and read. There is a lot of information and many examples provided for each problem. Reading this will help you write the correct code earlier rather than later.
  4. Ask questions: if its not clear how to proceed, put up a Piazza post or visit an office hour.
  5. Get coding: don't wait to start for too long as this will greatly increase your stress level an potentially result in late submissions.
  6. Familiarize yourself with the late submission policy for assignments so you are not caught off guard. No submissions will be accepted more than 48 hours after the deadline.

2 Download Code and Setup

Download the code pack linked at the top of the page. Unzip this which will create a project folder. Create new files in this folder. Ultimately you will re-zip this folder to submit it.

File State Notes
Makefile Provided Build file to compile all programs
banlet_funcs.c EDIT Problem 1&2 functions to write
banlet_main.c EDIT Problem 3 main function skeleton to edit
banlet.h Provided Header file
banlet_font_standard.c Provided Source file for "builtin" font
banlet_demo.c Provided Demo code to show some Banlet struct syntax / usage
cmdline_args.c Provided Demo of how to access command line arguments
     
test_banlet_funcs.c Testing Testing file for functions in Problem 1-2
     
data/font_banner.txt Data Font files for testing
data/font_mini.txt Data  
   
data/bass.txt Data Text files for testing
data/oo.txt Data  
   
TESTING    
testy Testing Test running script
test-results/ Testing Directory in which temporary testing files are written
test_banlet1.org Testing Problem 1 tests
test_banlet2.org Testing Problem 2 tests
test_banlet3.org Testing Problem 2 tests

2.1 Makefile

A Makefile is provided as part of this project. Building programs in C is a bit tedious and most folks use build systems of which make is the oldest. The instructions and dependencies to create programs are written in a Makefile which is then interpreted by the make program which will run gcc and other commands to create programs.

Use this Makefile by issuing commands like make prob1

>> make help
Typical usage is:
  > make                        # build all programs
  > make clean                  # remove all compiled items
  > make zip                    # create a zip file for submission
  > make prob1                  # built targets associated with problem 1
  > make test                   # run all tests
  > make test-prob2             # run test for problem 2
  > make test-prob2 testnum=5   # run problem 2 test #5 only

>> make prob2                   # build problem 2 demo program
gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c banlet_funcs.c

> make clean                    # remove all programs/binary object files
rm -f banlet_main banlet_demo test_banlet_funcs  *.o

>> make prob3                   # build problem 3 main program
gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c banlet_main.c
gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c banlet_funcs.c
gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c banlet_font_standard.c
gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -o banlet_main banlet_main.o banlet_funcs.o banlet_font_standard.o

>> make clean                   # remove all programs/binary object files
rm -f banlet_main banlet_demo test_banlet_funcs  *.o

>> make                         # build all programs/objects for the assignment
gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c banlet_main.c
gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c banlet_funcs.c
gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c banlet_font_standard.c
gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -o banlet_main banlet_main.o banlet_funcs.o banlet_font_standard.o
gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -o banlet_demo banlet_demo.c banlet_font_standard.o
gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -o test_banlet_funcs test_banlet_funcs.c banlet_funcs.o banlet_font_standard.o

You are not required to understand all that is in the Makefile (yet) but it is a very useful tool well worth your time to learn.

2.2 Automated Tests

Automated tests are included with the code distribution. These tests are known to work on grace.umd.edu only but in most cases they should run identically in Linux environments. They may work on the Windows Subsystem for Linux but no guarantees are made. They very unlikely to run on MacOS natively as Linux-specific tools are used.

The provided Makefile allows automated tests to be run via calls like make test-prob1 to test Problem 1 and make test-prob2 to test Problem 2. See the transcript below.

>> make test                    # run tests for problem 1, compiles required code first
gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c banlet_funcs.c
gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c banlet_font_standard.c
gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -o test_banlet_funcs test_banlet_funcs.c banlet_funcs.o banlet_font_standard.o
./testy test_banlet1.org 
============================================================
== test_banlet1.org : Problem 1 String / Printing Functions in banlet_funcs.c
== Running 15 / 15 tests
1)  string_replace_char_1          : ok
2)  string_replace_char_2          : ok
3)  string_replace_char_3          : ok
4)  string_replace_char_4          : ok
5)  count_linebreaks_1             : ok
6)  count_linebreaks_2             : ok
7)  count_linebreaks_3             : ok
8)  find_linebreaks_1              : ok
9)  find_linebreaks_2              : ok
10) find_linebreaks_3              : ok
11) find_linebreaks_4              : ok
12) print_fontified_oneline_1      : ok
13) print_fontified_oneline_2      : ok
14) print_fontified_oneline_3      : ok
15) print_fontified_oneline_length : ok
============================================================
RESULTS: 15 / 15 tests passed

>> make test-prob3              # run tests for problem 2
gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -c banlet_main.c
gcc -Wall -Wno-comment -Werror -g -Wno-unused-variable -o banlet_main banlet_main.o banlet_funcs.o banlet_font_standard.o
./testy test_banlet3.org 
============================================================
== test_banlet3.org : Problem 3 slurp_file() and banlet_main tests
== Running 15 / 15 tests
1)  banlet_main Hello World                         : ok
2)  banlet_main multiline                           : ok
...

>> make test                    # run tests for all problems
...

Each problem describes specifically how tests can be run and how credit will be assigned.

Note that one can run a single test with the following make invocation which sets testnum.

>> make test-prob2 testnum=5

This is useful when debugging to limit the output and time it takes to check program results.

3 Problem 1: Banlet Support

3.1 Overview

The overarching project goal is to recreate a simplified version of the figlet program program that is available on many Unix systems (though no Zaratan unfortunately). This project constructs a similar program called Banlet which prints messages as "banner letters" which are larger and more striking than standard terminal fonts. To set the goal, here is what banlet_main does in practice:

>> ./banlet_main 'Hi there!'                                                # print a simple "fontified" message"
._. ._.    ._.   _                       ._.
| | | |(`)  | |_.| |__    ___ ._ __.  ___ | |
| |_| ||'|  | __|| '_ \  /'_`\| `__| /'_`\| |
|  _  || |  | |_ | | | ||  __/| |   |  __/|_|
|_| |_||_|   \__||_| |_| \___||_|    \___|(_)

>> ./banlet_main $'How do\nyou do?' --fontfile data/font_banner.txt         # print a multiline message in a different font
#     #                                
#     #  ####  #    #    #####   ####  
#     # #    # #    #    #    # #    # 
####### #    # #    #    #    # #    # 
#     # #    # # ## #    #    # #    # 
#     # #    # ##  ##    #    # #    # 
#     #  ####  #    #    #####   ####  
                                       
                                      #####  
#   #  ####  #    #    #####   ####  #     # 
 # #  #    # #    #    #    # #    #       # 
  #   #    # #    #    #    # #    #    ###  
  #   #    # #    #    #    # #    #    #    
  #   #    # #    #    #    # #    #         
  #    ####   ####     #####   ####     #    


>> cat data/asimov.txt                                                      # show contents of a text file
I do not fear computers. I fear the lack of them. -Asimov

>> banlet_main --fontfile data/font_mini.txt --textfile data/asimov.txt     # print file as banner text using a different font
___                       _                                       ___    _                                    _                                      
 |    _|     ._    _|_  _|_ _  _.._   _   ._ _ ._    _|_ _ ._ _    |   _|_ _  _.._  _|_|_  _   | _. _|      _|_  _|_|_  _ ._ _    __ /\  _o._ _      
_|_  (_|(_)  | |(_) |_   | (/_(_||   (_(_)| | ||_)|_| |_(/_| _>o  _|_   | (/_(_||    |_| |(/_  |(_|(_|<  (_) |    |_| |(/_| | |o    /--\_>|| | |(_)\/

Problem 1 lays out a number of utility functions that operate on strings which gradually build towards the full functionality required.

3.2 Outline of banlet_funcs.c

The file banlet_funcs.c will contain most of the support functions for the Banlet program. An outline of these functions are presented below. Note that each function has the Problem # to which it belongs.

// banlet_funcs.c: support functions for the banlet_main program.

#include "banlet.h"

void glyph_init(glyph_t *glyph, int codepoint);
// PROVIDED: Initializes a glyph to mostly X's except for its
// codepoint on the first line.

void string_replace_char(char *str, char old, char new);
// PROBLEM 1: Replace instances of character 'old' with 'new' in the
// string 'str'.  May use the strlen() library function to determine
// string length or directly look for a '\0' null termination
// character to end the string.
// 
// EXAMPLES:
// char s1[]="A A B B A"; string_replace_char(s1, 'A', 'X'); // s1 is "X X B B X"
// char s2[]="A A B B A"; string_replace_char(s2, 'B', 'Y'); // s2 is "A A Y Y A"
// char s3[]="A A B B A"; string_replace_char(s3, ' ', '-'); // s3 is "A-A-B-B-A"

int count_linebreaks(char *msg);
// PROBLEM 1: Counts the number of newline characters '\n' in the
// string 'msg'; return this number +1 as the end of lines will always
// be a line break. May use the strlen() library function to determine
// string length or directly look for a '\0' null termination
// character to end the string.
// 
// EXAMPLES:
// count_linebreaks("Hi")        ->  1
// count_linebreaks("Hi There")  ->  1
// count_linebreaks("Hi\nThere") ->  2
// count_linebreaks("O\nM\nG")   ->  3

int *find_linebreaks(char *msg, int *nbreaks);
// PROBLEM 1: Counts the linebreaks (newline '\n' chars and end of
// string) and returns an array of integers with the position for each
// linebreak in string 'msg'.  Uses the count_linebreaks()
// function. The 'nbreaks' parameter is an OUTPUT integer that should
// be set to the number of breaks in 'msg' using the C dereference
// operator (*).
//
// EXAMPLES:
// int nbreaks = -1;
// int *breaks = find_linebreaks("Hello\nWorld", &nbreaks);
// //            index in string: 012345 67890
// // nbreaks is now 2
// // breask is now [5, 11]

void print_fontified_oneline(char *msg, font_t *font, int length);
// PROBLEM 1: Prints string 'msg' using 'font'. Only prints characters
// 0 to 'length-1'.  Iterates over each row in font->height and then
// scans across the charactes in 'msg' printing each "row" of the
// character. On reaching index 'length', prints a newline and then
// scans across 'msg' again printing characters from the next row.
// Each msg[i] character is used to as the index into fonts->glyphs[]
// to access the "glyph" that will be printed.
//
// NOTE: This function does NOT handle embedded newline '\n'
// characters. It is intended to be called repeatedly on each line in
// multiline text with '\n' characters found using the
// 'find_linebreaks()' function.
//
// EXAMPLE:
//
// print_fontified_oneline("Hello!", &font_standard, 6);
// // Prints the following on standard output:
// ._. ._.      ,-,,-,       ._.
// | | | |  ___ | || |  ___  | |
// | |_| | /'_`\| || | / _ \ | |
// |  _  ||  __/| || || (_) ||_|
// |_| |_| \___||_||_| \___/ (_)

void print_fontified(char *msg, font_t *font);
// PROBLEM 1: Uses print_fontified_oneline() with find_linebreaks() to
// correctly print 'msg' with 'font' even if there are linebreaks in
// 'msg'.  Uses find_linebreaks() to find the end of each line in
// 'msg' and then iterates over lines printing each.  Uses pointer
// arithmetic to pass the starting position of each line into calls of
// print_fontified_oneline(). Frees memory allocated (such as through
// use of count_linebreaks()) before returning.
//
// EXAMPLE:
//   print_fontified("apple\nB@N@N@\nCarr0^", &font_standard);
// Shows the following on standard output:
//                      ,-,      
//   __ _ ,_ __. ,_ __. | |  ___ 
//  / _` || '_ \ | '_ \ | | /'_`\
// | (_| || |_) || |_) || ||  __/
//  \__,_|| .__/ | .__/ |_| \___|
//        |_|    |_|             
// ,____    ____  ._  ._.   ____  ._  ._.   ____  
// | == )  / __ \ | \ | |  / __ \ | \ | |  / __ \ 
// |  _ \ / / _` ||  \| | / / _` ||  \| | / / _` |
// | |_)|| | (_| || |\  || | (_| || |\  || | (_| |
// |____/ \ \__,.||_| \_| \ \__,.||_| \_| \ \__,.|
//         \____/          \____/          \____/ 
//   ____.                     ___   /\ 
//  / ___|  __ _ ._ __.._ __. / _ \ |/\|
// | |     / _` || `__|| `__|| | | |    
// | |___.| (_| || |   | |   | |_| |    
//  \____| \__,_||_|   |_|    \___/     

font_t *font_load(char *filename);
// PROBLEM 2: Loads a banner font from 'filename'.  The format of font
// files is documented more thoroughly in the project specification
// but is generally comprised of a first line that indicate the height
// of each glyph in the font followed by a sequence of each glyph
// starting with its codepoint (ASCII index) and a grid of characters
// in it. To make parsing easier, the ? character is used to represent
// blank spaces in glyph shapes.
//
// EXAMPLE:
// height: 4
// 42
// ???
// \|/
// /|\
// ???
// 43
// ???
// _|_
// ?|?
// ???
//
// The two characters above are the codepoint 42 '*' and codepoint 43
// '+' with the ? symbols eventually being replaced by spaces during
// loading.
//
// If 'filename' cannot be opened for reading, returns NULL.
//
// Memory allocation happens in two steps: (1) allocates memory for a
// font_t struct then (2) allocates memory for an array of glyph_t
// structs of length NUM_ASCII_GLYPHS (a constant defined in
// banlet.h). Sets the font->glyphs field to be the allocated array of
// glyphs.  Reads the font->height field from teh first line of the
// file.  Iterates over each element of the glyphs array and calls the
// glyph_init() function on it to populate it with default data.  Then
// proceeds to read glyphs from the file. Glyphs are read by reading
// the integer codepoint first which determins which element of the
// glyph array to read into.  Then a loop over the height of the font
// is used to read each row of the glyph into the
// glyph[codepoint]->data[row]; fscanf() with '%s' specifier is used
// for this.  Finally, the string_replace_char() function is used to
// replace all '?' characters with ' ' (space) characters in the glyph
// data. Sets the width of each glyph using the strlen() function on
// any balid row of the glyph data. The width of each glyph is set
// based on the length of one of its 0th row and the .is_set field is
// changed to 1 to indicate the glyph is present.
//
// Glyphs are read from 'filename' until an attempt to read a glyph's
// codepoint with fscanf() returns EOF (end of file). This causes the
// routine to return the allocated font_t data for use elsewhere.

void font_free(font_t *font);
// PROBLEM 2: Frees the memory associated with 'font'.  First frees
// the glyph array, then frees the font itself. Hint: this funciton
// should be 2 lines long.

char *slurp_file_two_pass(char *filename);
// PROBLEM 2: Read all characters from the file 'filename' into a
// dynamcally allocated array. Terminates the array with the '\0'
// character to form a valid C string and return that string. If the
// file cannot be opened, return NULL. 
// 
// Uses a "2-Pass" solution. Counts the characters in file via calls
// to fscanf() or fgetc(), then returns to beginning of the file with
// rewind(), allocates an appropriately sized block of memory, then
// reads all characters into it in a second pass.
// 
// CONSTRAINT: Does not use any functions that determine the size of a
// file a priori such as fstat() or fseek() or their kin.

char *slurp_file_one_pass(char *filename);
// OPTIONAL MAKEUP CREDIT: Like surp_file_two_pass() but uses a single
// I/O pass and dynamically expands memory to handle larger
// inputs. For full makeup credit, this approach must abide by the
// following additional conditions:
// - Does not use functions that determine the size of the file
// - Does not allocate/copy memory each loop iteration, only
//   "occasionally"
// - Does not use the realloc() function, only malloc()/free()
// - Has amortized linear time and linear space; the allocated array may
//   not be any larger than 2x the number of characters in it
// - Uses a memory allocation scheme similar to thos used with
//   dynamic arrays to meet the above requirement; see
//   https://en.wikipedia.org/wiki/Dynamic_array
// - Include comments describing your makeup credit approach
// - Is used in the main() function and/or elsewhere in place of the
//   other version

3.3 Editing and Testing

The project code contains a skeleton version of banlet_funcs.c which you should fill in with definitions. With this skeleton version, you can immediately start testing your code by typing make test-prob1. Without changes, you will get failures for all tests as in

>> make test-prob1
./testy test_banlet_funcs.org 
============================================================
== test_banlet_funcs.org : Problem 1 First 5 Functions in banlet_funcs.c
== Running 15 / 15 tests
1)  string_replace_char_1     : FAIL -> results in file 'test-results/prob1-01-result.md'
2)  string_replace_char_2     : FAIL -> results in file 'test-results/prob1-02-result.md'
3)  string_replace_char_3     : FAIL -> results in file 'test-results/prob1-03-result.md'
4)  string_replace_char_4     : FAIL -> results in file 'test-results/prob1-04-result.md'
...
15) print_fontified_oneline_4 : FAIL -> results in file 'test-results/prob1-15-result.md'
============================================================
RESULTS: 0 / 15 tests passed

However, the ability to run tests on your code an see progress is extremely important. Your first goal when starting a new project should be to see some results or running the program which is much easier if some benevolent dictator has provided a bunch of tests.

Keep in mind that as you fail tests, you can see the results files in any text editor or in the terminal. The cat command is helpful for this:

>> cat test-results/prob1-01-result.md
(TEST 1) string_replace_char_1 : FAIL
=====================================

COMMENTS
--------


PROGRAM: ./test_banlet_funcs string_replace_char_1
--------------------------------------------------
To run this individual test in GDB use the command:
  gdb --args ./test_banlet_funcs string_replace_char_1
but any input to the program must be typed within the debugger

FAILURE MESSAGES
----------------
- Output Differenes: Expected/Actual do not match, check Diff Sections for details

SIDE-BY-SIDE DIFF of Expected vs Actual
---------------------------------------
. lines match; | lines differ; < expected line missing; > extra line in actual

```sdiff
===EXPECT===                                    ===ACTUAL===
IF_TEST("string_replace_char_1") {            . IF_TEST("string_replace_char_1") { 
    // Tests replacing characters in a string .     // Tests replacing characters in a string
    char string[]="A A B B A";                .     char string[]="A A B B A";
    string_replace_char(string, 'A', 'X');    .     string_replace_char(string, 'A', 'X');
    printf("result: '%s'\n", string);         .     printf("result: '%s'\n", string);
}                                             . }
---OUTPUT---                                  . ---OUTPUT---
result: 'X X B B X'                           | result: 'A A B B A'

```

LINE-BY-LINE DIFF of Expected vs Actual
---------------------------------------
```
EXPECT   8) result: 'X X B B X'
ACTUAL   8) result: 'A A B B A'

```

VALGRIND REPORT
---------------
The program is run on under valgrind as
  stdbuf -i 0 -o 0 -e 0 valgrind --error-exitcode=13 --leak-check=full --show-leak-kinds=all --track-origins=yes ./test_banlet_funcs string_replace_char_1
which may be pasted onto a command line to run it.

```
==340236== Memcheck, a memory error detector
==340236== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==340236== Using Valgrind-3.25.1 and LibVEX; rerun with -h for copyright info
==340236== Command: ./test_banlet_funcs string_replace_char_1
==340236== 
==340236== 
==340236== HEAP SUMMARY:
==340236==     in use at exit: 0 bytes in 0 blocks
==340236==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==340236== 
==340236== All heap blocks were freed -- no leaks are possible
==340236== 
==340236== For lists of detected and suppressed errors, rerun with: -s
==340236== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
```

SUMMARY
-------
Test FAILED for the following reasons
- Output Differenes: Expected/Actual do not match, check Diff Sections for details

You can also use the invocation

>> make test-prob1 testnum=5

to run a single test and see its results.

3.4 String Utility Functions

The first 3 functions to implement in banlet_funcs.c are string utility functions that will be used later. As covered in lecture, C uses raw arrays of characters as its string type. Often these are passed as a pointer to character data, thus the prototypes that involve char * as in

void string_replace_char(char *str, char old, char new)

Keep the following in mind.

  1. You'll need to iterate over the characters in the string. Since str is a pointer, one can use the indexing syntax str[i] to access individual characters in it. This can also be used to assign a new character as in str[i] = 'A';.
  2. str does not carry its length with it so there is no str.length or length(str) available and sizeof(str) WILL NOT WORK to determine the length of the string.
  3. Since one definitely needs to iterate over all characters in str, use one of the following two mechanisms to find its end
    • strlen(str): this handy function from the C string library gives returns the length of strings only
    • The \0 character: all standard strings are to be terminated with a null \0 character; one can check if str[i] == '\0' while iterating to find the end of the string. This is the technique that is used by library functions like printf() to find the end of strings.
  4. Make sure to use an equality check when comparing elements, that is the x == y operator in C, NOT the assignment operator =.

find_linebreaks()

Consider the prototype for find_linebreaks() which has some interesting features:

int *find_linebreaks(char *msg, int *nbreaks)

Importantly, this function must return 2 things determined from parameter msg

  1. An array of integers indicating the positions in msg where \n (newline) characters appear along with the end of the string. This is the return value for the function which must be a malloc()'d array. Do NOT free() the array of integers you malloc() in find_linebreaks(): it will be freed by the calling function
  2. The length of the returned array must also be returned. C does not support multiple returns per se, but pointers more than make up for this. The parameter nbreaks is meant to be SET by find_linebreaks() as indicated in the function documentation. A caller will provide the address of an integer for the function to set

       int nbreaks = -1;
       int *breaks = find_linebreaks("Hello\nWorld", &nbreaks);
       //            index in string: 012345 67890
       // nbreaks is now 2
       // breask is now [5, 11]
    

3.5 Print Fontified

Glyphs and Fonts

The function print_fontified_oneline() is used to print single line of text in a "font" as defined by the project. Normally fonts have interesting geometric curves and drawing instructions embedded in them. Most standard terminals don't support printing such things but do support printing character data. Thus "fonts" in Banlet are just a grid of text that is to be printed in when displaying a single character. For example, when printing a fontified version of "A", the following glyph is used in builtin font for Banlet.

 012345678
0   / \   
1  / _ \  
2 / ___ \ 
3/_/   \_\
4        

Basic Algorithm for Fontified Printing

This presents some interesting challenges when printing sequences of such glyphs as one must typically print across rows of text before moving the to the next line. So, to print the string "CAT", one would print the first row of each glyph, then the second, then the 3rd, and so on. As an example, here is "CAT" built incrementally. The @ symbol is used to denote where printing finishes at each step

DEMO of printing "CAT" via print_fontified_oneline() algorithm

ROW 0, PRINT 0th CHAR 'C'
  ____@

ROW 0, PRINT 1th CHAR 'A'
  ____     _    @

ROW 0, PRINT 2th CHAR 'T'
  ____     _     _____@

ROW 0 DONE, PRINT \n
  ____     _     _____
@

ROW 1, PRINT 0th CHAR 'C'
  ____     _     _____
 / ___|@

ROW 1, PRINT 1th CHAR 'A'
  ____     _     _____
 / ___|   / \   @

ROW 1, PRINT 2th CHAR 'T'
  ____     _     _____
 / ___|   / \   |_   _|@

ROW 1 DONE, PRINT \n
  ____     _     _____
 / ___|   / \   |_   _|
@

AFTER PRINTING 2th ROW ALL CHARS
  ____     _     _____ 
 / ___|   / \   |_   _|
| |      / _ \    | |  @

AFTER PRINTING 3th ROW ALL CHARS
  ____     _     _____ 
 / ___|   / \   |_   _|
| |      / _ \    | |  
| |___  / ___ \   | |  @

AFTER COMPLETING ALL ROWS / CHARS
  ____     _     _____ 
 / ___|   / \   |_   _|
| |      / _ \    | |  
| |___  / ___ \   | |  
 \____|/_/   \_\  |_|  
@

Implementing this basic printing algorithm is the purpose of the print_fontified_oneline() function.

Font and Glyph C Types

The font_t and glyph_t data types are defined in the banlet.h header file and are as follows.

 1: #define MAX_HEIGHT 10                // max height in chars for glyphs
 2: #define MAX_WIDTH  20                // max width  in chars for glyphs
 3: #define NUM_ASCII_GLYPHS 128         // number of glyphs in a font
 4: 
 5: typedef struct {                     // type associated with individual glyphs
 6:   char data[MAX_HEIGHT][MAX_WIDTH];  // 2D array of data for glyph, rows should be \0-terminated strings
 7:   int is_set;                        // 1 if set to non-default status during font_load(), 0 otherwise
 8:   int codepoint;                     // ASCII codepoint, e.g. 65 for 'A' etc.
 9:   int width;                         // width of the glyph
10: } glyph_t;
11: 
12: typedef struct {                     // type associated with a font
13:   glyph_t *glyphs;                   // array of glyphs with length NUM_ASCII_GLYPHS for each ASCII codepoint
14:   int height;                        // height of all glyphs in the font
15: } font_t;
16: 
17: extern font_t font_standard;         // font defined in banlet_font_standard.c

In order to access the glyph data to print, one must usually go through a font data structure such as in this sample code provided in banlet_demo.c.

// banlet_demo.c: shows some syntax patterns to access parts of the
// font_t and glyph_t data in banlet.

#include "banlet.h"

int main(int argc, char *argv[]){
  font_t *font = &font_standard; // builtin font
  printf("Standard font has %d rows\n",
         font->height);

  int codepoint = 'A';          // ASCII code 65
  printf("Width of codepoint %d in font is: %d\n",
         codepoint,
         font->glyphs[codepoint].width);

  printf("Row 0 of glyph is: %s\n",
         font->glyphs[codepoint].data[0]);

  printf("Row 1 of glyph is: %s\n",
         font->glyphs[codepoint].data[1]);

  printf("Row 2 of glyph is: %s\n",
         font->glyphs[codepoint].data[2]);
  return 0;
}
  • Note the combination use of the struct field dereference "arrow" operator x->y, array index x[y], and the field access "dot" x.y operators. Follow this pattern in your own code to traverse from one struct to its array fields to their fields in turn.
  • Note also the use of printf() and "rows" of the data field. For a 2D array like char data[MAX_HEIGHT][MAX_WIDTH]; access has the following pointer semantics:
    • data can be treated as a char **
    • data[i] can be treated as a char * and can be used with the %s specifier in printf()
    • data[i][j] is a char and can be used with the %c specifier in printf().

Printing Fontified

Combine the data access pattern and the central algorithm of printing each row of each glyph to complete print_fontified_oneline(). Keep in mind that the function does not handle linebreaks which is the subject of the next problem. Instead, note the prototype has a length parameter:

void print_fontified_oneline(char *msg, int length, font_t *font)

Print from the beginning of msg up to but NOT INCLUDING the character at index length.

4 Problem 2: Banlet Output and Input

4.1 Overview

This problem will combine several of the support functions from Problem 1 and implement a font-loading function in order to complete the Banlet program. It implements several more functions in banlet_funcs.c. These are used in the provided banlet_main.c program to produce commandline banner letters.

4.2 Printing Multiple Fontified Lines

The previous print_fontified_oneline() function did not handle multi-line message printing. Instead, this is handled in print_fontified().

void print_fontified(char *msg, font_t *font);
// PROBLEM 2: Uses print_fontified_oneline() with find_linebreaks() to
// correctly print 'msg' with 'font' even if there are linebreaks in
// 'msg'.  Uses find_linebreaks() to find the end of each line in
// 'msg' and then iterates over lines printing each.  Uses pointer
// arithmetic to pass the starting position of each line into calls of
// print_fontified(). Frees memory allocated (such as through use of
// count_linebreaks()) before returning.
//
// EXAMPLE:
//   print_fontified("apple\nB@N@N@\nCarr0^", &font_standard);
// Shows the following on standard output:
//                      ,-,      
//   __ _ ,_ __. ,_ __. | |  ___ 
//  / _` || '_ \ | '_ \ | | /'_`\
// | (_| || |_) || |_) || ||  __/
//  \__,_|| .__/ | .__/ |_| \___|
//        |_|    |_|             
// ,____    ____  ._  ._.   ____  ._  ._.   ____  
// | == )  / __ \ | \ | |  / __ \ | \ | |  / __ \ 
// |  _ \ / / _` ||  \| | / / _` ||  \| | / / _` |
// | |_)|| | (_| || |\  || | (_| || |\  || | (_| |
// |____/ \ \__,.||_| \_| \ \__,.||_| \_| \ \__,.|
//         \____/          \____/          \____/ 
//   ____.                     ___   /\ 
//  / ___|  __ _ ._ __.._ __. / _ \ |/\|
// | |     / _` || `__|| `__|| | | |    
// | |___.| (_| || |   | |   | |_| |    
//  \____| \__,_||_|   |_|    \___/     

This function works as follows.

  • Use the find_linebreaks() function to locate the \n in the msg string. This gives the number of lines in the message and their starting positions in msg.
  • Iterate over each "line" in the text calling print_fontified_oneline() on just that individual line. After printing each line, move to the next line.
  • Use pointer arithmetic or addressing to pass different starting positions into print_fontified(). For example, if there is a newline at position 14 in msg, then position 15 is the start of a new line and msg+15 will give a char * pointing at that starting position.
  • The length of each line can be determined using arithmetic on the indices in the linebreaks array from find_linebreaks()
  • Make sure that the array allocated by find_linebreaks() is free()'d before returning from this function to avoid a memory leak.

Once this function is finished, the provided banlet_main program should be mostly functional.

>> ./banlet_main $'Nice\nWork!'
._  ._.               
| \ | |(`)  ___.  ___ 
|  \| ||'| / __| /'_`\
| |\  || || (__ |  __/
|_| \_||_| \___| \___|
                      
_.        ._             ,_,   ._.
\ \      / /  ___  ._ __.| | __| |
 \ \ /\ / /  / _ \ | `__|| |/ /| |
  \ V  V /  | (_) || |   |   < |_|
   \_/\_/    \___/ |_|   |_|\_\(_)
                                  

4.3 Loading Fonts

The real flexibility of any font system is being able to select different fonts easily to see how the same text renders. There is a built-in font in Banlet defined in a set of C structs in banlet_font_standard.c but this is extremely cumbersome to specify and inappropriate for creating and using new fonts. New fonts in C files would require Banlet to be recompiled every time a new font is added.

Instead, it is better to allow a file format that specifies new fonts so that they can be created and loaded without modifying the source code of Banlet. This is the intent of the font_load() function.

font_t *font_load(char *filename);

This section details the format for font files and gives some details on the font_load() function which enables new fonts to be created in text files and loaded on the fly of use in Banlet.

Font Files

A number of demonstration font files are in the data/ directory for the project. This includes the font_standard.txt file which is a file version of the builtin font. It makes a good example and the first portion is here using the head command which prints the first few lines in files, 30 lines in this case:

>> head -30 data/font_alternate.txt
height: 6
32
??
??
??
??
??
??
33
?_?
|?|
|?|
|_|
(_)
???
34
?_?_?
(?|?)
?V?V?
?????
?????
?????
35
???_??_???
?_|?||?|_?
|_??..??_|
|_??????_|
??|_||_|??
??????????
36

The first and most obvious strangeness is the presence of the many ? characters. Each ? character will be replaced with a space character instead but reading data from the file is much easier without whitespace in it. For example, the glyphs for 'ABC' appear in data/font_standard.txt adjacent to one another as follows:

65
????_????
???/?\???
??/?_?\??
?/?___?\?
/_/???\_\
?????????
66
?____??
|?__?)?
|??_?\?
|?|_)?|
|____/?
???????
67
??____?
?/?___|
|?|????
|?|___?
?\____|
???????

Using the string_replace_char() will eventually make these appear in a familiar fashion as:

65
    _    
   / \   
  / _ \  
 / ___ \ 
/_/   \_\
         
66
 ____  
| __ ) 
|  _ \ 
| |_) |
|____/ 
       
67
  ____ 
 / ___|
| |    
| |___ 
 \____|
       

Aside from the ? characters, font files follow a simple pattern.

  • Line 1 is height: N where N is the height of all glyphs in the font. This will always be less than MAX_HEIGHT.
  • The next line will be a codepoint for some glyph like 32 (space) or 65 ('A')
  • After the codepoint will be N lines which comprise the data for the glyph; correct fonts will all be within the bounds set by MAX_WIDTH.
  • The pattern then repeats with another codepoint and N lines of the glyph.

Study the example font files in the data directory to make sure you have a good sense of the structure of those files.

Allocating and Initializing Fonts/Glyph Arrays

You'll need to allocate a font_t using malloc() which will eventually be returned from font_load().

Glyph arrays should also be allocated using malloc(). Allocate NUM_ASCII_GLYPHS entries in the array but multiply this by the size of the glyph_t struct.

Remember to "connect" font struct to the glyph array by assigning the font->glyph field to the malloc()'d array.

On each glyph in the array, call the provided function glyph_init() function which will fill it with some default data. This will make it much easier to identify any mistakes made in printing glyphs that weren't initialized. Uninitialized glyphs will print like below with their codepoint on the first line of display.

78XXXX
XXXXXX
XXXXXX
XXXXXX
XXXXXX
XXXXXX

Reading Font Files

Make use of standard C I/O functions such as fopen() to open the glyph files and fscanf() to read from them. Note the following tricks associated with fscanf() are useful while parsing the font file.

  • int x; fscanf(fin, "some text %d", &x); : this invocation will read exactly the characters some text then parse an integer and fill x in with it. Use this approach for the first line of the font files.
  • int x; fscanf("%d", &x); : this invocation will skip whitespace until an integer is found. This whitespace skipping includes moving to subsequent lines so no special inclusion of newline \n is needed.
  • char str2d[ROWS][COLS]; fscanf("%s", str2d[2]); : this invocation will read a string into the 2th row of str2d which itself is an array of COLS characters. Useful for reading each row of a glyph. It is best to read directly into the font->glyphs[codepoint].data[row] area.
  • int ret = fscanf("...",...); if(ret == EOF){ ... } : reading data from a file can fail if there is no data left and the End of File (EOF) has been reached. The special value EOF is returned by fscanf() in this case. On attempting to read the next glyph's codepoint, check the return value of fscanf() as you'll need to break out of your reading loop on reaching the file end.

A few other tips:

  • Set the width of each glyph according to the length of one of its rows. All rows should be of equal length and there is no requirement to check this but the .width field must be set during a load.
  • Also change the .is_set field to 1 to indicate that the font has changed the glyph away from the default picture set in glyph_init().
  • Don't forget to fclose() the font file once you are finished with it. Often times Valgrind will report memory leaks for code that neglects to close files.

4.4 Loading Text Files (slurp)

It will be convenient later for Banlet to be able to load text from a text file for printing. To that end the following function will be implemented.

char *slurp_file_two_pass(char *filename);
// PROBLEM 2: Read all characters from the file 'filename' into a
// dynamcally allocated array. Terminates the array with the '\0'
// character to form a valid C string and return that string. If the
// file cannot be opened, return NULL. 
// 
// Uses a "2-Pass" solution. Counts the characters in file via calls
// to fscanf() or fgetc(), then returns to beginning of the file with
// rewind(), allocates an appropriately sized block of memory, then
// reads all characters into it in a second pass.
// 
// CONSTRAINT: Does not use any functions that determine the size of a
// file a priori such as fstat() or fseek() or their kin.

An essential difficulty here is that the number of characters in a file is not known by the program ahead of time. There a variety of ways to do surmount this difficulty but required algorithm is discussed below with an optional implementation for Makeup Credit described later.

As described in the slurp_file_two_pass() comments, this approach does the input in 2 passes.

  1. Read the text file counting its characters. After reaching the end of the file, its size is no known so an array large enough to hold all characters can be allocated in the heap.
  2. Return to the beginning of the file then read each character again and store it in the allocated space.

Some useful functions to keep in mind here are as follows.

  • fscanf(fin,"%c",&mychar) reads a single character at a time advancing forwards in the FILE by 1 char on each read
  • rewind(fin) returns a FILE fin to its start allowing one to re-read the same input

Don't forget to null terminate the string: there will not be any \0 characters in the file if it is normal text. Instead, allocate enough space for this extra character and manually place it.

AFTER you complete the two-pass version and the remainder of the project, consider implementing the MAKEUP CREDIT version which is a more complex algorithm for input.

5 Problem 3: Banlet Main

5.1 Overview

As with all C programs, Banlet needs a main() function. This entry point is where the service functions like font_load() and print_fontified() get called.

A skeleton main() is provided in the file banlet_main.c and students should fill in the REQUIRED portions of this file. These may be long (a few dozen lines) or short (a single line with a function call). Details of what to fill in are describe below

5.2 Command Line Arguments in C Programs

To determine what text to print, what font to use, and any text data files to read, the main() function will need to access command line arguments. C programs provide this through the standard parameters passed to main()

int main(int argc, char *argv[])
//           ^^^         ^^^^
//       how many  array of strings

The provided demo cmdline_args.c shows how to iterate through the argv[] array according to the bounds set by argc. Study this program as you will need to adapt its techniques to handle the command line arguments in Banlet.

5.3 Handling Banlet Command Line Arguments

Banlet will allow arbitrary variation in the order of its command line arguments. This means all of the following are valid ways to run banlet_main.

>> banlet_main 'Hello!'
>> banlet_main 'Hello!' --fontfile font.txt
>> banlet_main --fontfile font.txt 'Hello!' 
>> banlet_main --textfile text.txt
>> banlet_main --textfile text.txt --fontfile font.txt
>> banlet_main --fontfile font.txt --textfile text.txt

NOTE: variations that do NOT appear above will not be tested. You may detect those cases and print an error or let your program crash at no penalty. This refereed to as "undefined behavior".

A naive solution to handling the allowed variations is to hard code an if() case for each and take appropriate action. This is an undesirable approach because

  1. There will be quite a bit of repeated code as several cases need to load a font all cases need to call print_fontified().
  2. Hard coding all cases does not scale: if later another option were added like --interactive, the number of conditional cases might double again from 6 to 12.

A better approach is to loop through the command line arguments (as is alluded to in the preceding section) and set program variables according to the option at stake. There are 3 kinds of arguments:

  1. --fontfile font.txt : this option will be a know fixed string "--fontfile" and it is always followed by the filename on which font_load() should be called.
  2. --textfile text.txt : again, the fixed string "--textfile" appears and is followed by a file which contains the text to print. The text can be read using slurp_file().
  3. 'Some Text' : there is no "--option", just the text that comprises the message to print.

This suggests the following rough algorithm to handle the specified command line arguments and make it possible to expand them later. The algorithm is specified in a Python-like pseudocode.

fun main(argc, argv[]):
  
  text = NULL
  font = default_font
  text_from_file = false
  
  for i=1 to argc-1:
    if argv[i] is "--fontfile":
      load argv[i+1] as a font
      error and exit if the font can't be loaded
      set font to the loaded font
      i = i+1

    else if argv[i] is "--textfile":
      load argv[i+1] as a string
      error and exit if the string can't be loaded
      set text to be the loaded string
      i = i+1

    else:
      set text to be argv[i]
  done

  check that the text is not NULL, error out if so
  display the text using font
  free any memory that was allocated
done

Should any additional command line arguments be added, they become new conditional cases in the loop over command line arguemtns.

Study this approach and implement a C version of it in banlet_main.c.

TIP: Simple uses are like banlet_main 'Hello world!'. Initially you can just handle these by assuming that argv[1] is the text to print. If pinched for time, this will allow you to pass a few more test cases if you're struggling with the command line processing. However, you'll eventually need to handle other command line arguments to pass all test cases and complete the program.

5.4 Testing banlet_main Interactively

Once you have completed the main() function can test Banlet interactively using banlet_main as shown below.

>> ./banlet_main ':-)'  
          __  
 _        \ \ 
(_)._____. | |
 _ |_____| | |
(_)        | |
          /_/ 
>> ./banlet_main --fontfile data/font_banner.txt  $'Great\nSuccess'  
 #####                             
#     # #####  ######   ##   ##### 
#       #    # #       #  #    #   
#  #### #    # #####  #    #   #   
#     # #####  #      ######   #   
#     # #   #  #      #    #   #   
 #####  #    # ###### #    #   #   
                                   
 #####                                            
#     # #    #  ####   ####  ######  ####   ####  
#       #    # #    # #    # #      #      #      
 #####  #    # #      #      #####   ####   ####  
      # #    # #      #      #           #      # 
#     # #    # #    # #    # #      #    # #    # 
 #####   ####   ####   ####  ######  ####   ####  
                                                  
>> ./banlet_main --textfile data/testing.txt --fontfile data/font_mini.txt
___                                                                                       
 |  _  __|_o._  _    _ _.._      ._ |    ._ ._      _   _|_|_  _   ._ ._ _  _ _ ._  _ _   
 | (/__> |_|| |(_|  (_(_|| |  (_)| ||\/  |_)| (_)\/(/_   |_| |(/_  |_)| (/__>(/_| |(_(/_  
                _|                   /   |                         |                      
     _                                                               
   _|_  |_     _  _   ._    _|_  _|_|_  _ o._   _.|_  _ _ ._  _ _    
(_) |   |_)|_|(_|_>o  | |(_) |_   |_| |(/_||   (_||_)_>(/_| |(_(/_o  
               _|  /                                                 
     _                _                
__  |_ _| _ _  _ ._  | \o o|  __|_._ _.
    |_(_|_>(_|(/_|   |_/| ||<_> |_| (_|
            _|           _|            

>> ./banlet_main --textfile data/testing.txt --fontfile data/font_alternate.txt
 _____             _    _                                                      _                                              _    _                                                                
|_   _|  ___  ___ | |_ (_) _ __    __ _     ___   __ _  _ __      ___   _ __  | | _   _    _ __   _ __   ___  __   __  ___   | |_ | |__    ___    _ __   _ __   ___  ___   ___  _ __    ___   ___   
  | |   / _ \/ __|| __|| || '_ \  / _` |   / __| / _` || '_ \    / _ \ | '_ \ | || | | |  | '_ \ | '__| / _ \ \ \ / / / _ \  | __|| '_ \  / _ \  | '_ \ | '__| / _ \/ __| / _ \| '_ \  / __| / _ \  
  | |  |  __/\__ \| |_ | || | | || (_| |  | (__ | (_| || | | |  | (_) || | | || || |_| |  | |_) || |   | (_) | \ V / |  __/  | |_ | | | ||  __/  | |_) || |   |  __/\__ \|  __/| | | || (__ |  __/  
  |_|   \___||___/ \__||_||_| |_| \__, |   \___| \__,_||_| |_|   \___/ |_| |_||_| \__, |  | .__/ |_|    \___/   \_/   \___|   \__||_| |_| \___|  | .__/ |_|    \___||___/ \___||_| |_| \___| \___|  
                                  |___/                                           |___/   |_|                                                    |_|                                                
         __    _                                            _      _    _            _                 _                                        
  ___   / _|  | |__   _   _   __ _  ___       _ __    ___  | |_   | |_ | |__    ___ (_) _ __     __ _ | |__   ___   ___  _ __    ___   ___      
 / _ \ | |_   | '_ \ | | | | / _` |/ __|     | '_ \  / _ \ | __|  | __|| '_ \  / _ \| || '__|   / _` || '_ \ / __| / _ \| '_ \  / __| / _ \     
| (_) ||  _|  | |_) || |_| || (_| |\__ \ _   | | | || (_) || |_   | |_ | | | ||  __/| || |     | (_| || |_) |\__ \|  __/| | | || (__ |  __/ _   
 \___/ |_|    |_.__/  \__,_| \__, ||___/( )  |_| |_| \___/  \__|   \__||_| |_| \___||_||_|      \__,_||_.__/ |___/ \___||_| |_| \___| \___|(_)  
                             |___/      |/                                                                                                      
         ._____.     _                           .____   _    _  _          _                
         | ____|  __| | ___   __ _   ___  _ __   |  _ \ (_)  (_)| | __ ___ | |_  _ __   __ _ 
 _____   | +== / _` |/ __| / _` | / _ \| '__|  | | | || |  | || |/ // __|| __|| '__| / _` |
|_____|  | |___ | (_| |\__ \| (_| ||  __/| |     | |_| || |  | ||   < \__ \| |_ | |   | (_| |
         |_____| \__,_||___/ \__, | \___||_|     |____/ |_| _/ ||_|\_\|___/ \__||_|    \__,_|
                             |___/                         |__/                              

Optional Enrichment for the Creatively Inclined

Font design is an interesting art form and the Banlet framework gives you a little bit of space in which to play. If you are so inclined, consider copying and modifying some of the provided fonts or design your own. The Figlet Homepage has several more examples of fonts. Figlet inspired Banlet and most of the fonts here are adaptations of the fonts that are part of the Figlet distribution.

There is no additional credit for font creation, just the satisfaction of an artist gets from their work.

6 Grading Criteria

6.1 Points by Section

The following criteria will be checked. Some are Automated and available during development via command like make test while others are done Manually by graders after submission.

Weight Criteria
  AUTOMATED TESTS via make test-prob1 make test-prob1
15 make test-prob1 Runs tests in test_banlet1.org on the first 4 functions in banlet_funcs.c
10 make test-prob2 Runs tests in test_banlet2.org on the remaining functions in banlet_funcs.c
15 make test-prob3 Runs tests in test_banlet3.org on banlet_main
40 SUBTOTAL
  MANUAL INSPECTION
15 Code Style: Functions adhere to CMSC 216 C Coding Style Guide which dictates
  reasonable indentation, appropriate commenting, consistency of curly usage, etc.
   
15 Appropriate code shape and flow: no over-complicated sections that with unneeded nested conditionals,
  loops, etc. when simpler solutions would suffice.
  Pay careful attention to simple, clean shapes in these functions:
  print_fontified_one_line() / print_fontified()
  font_load() slurp_file_two_pass() / slurp_file_one_pass()
  main() with respect to command line argument handling
   
10 Use of simple and appropriate C standard library functions. Use of exotic functions beyond what has
  been discussed in lecture and lab will likely lose credit. While less desirable to use in industrial
  settings, favor simple functions like strcmp() vs their more complex counterparts like strncmp().
  Favor simple input mechanism even if this might be vulnerable to memory problems on arbitrary input.
   
10 Avoids pedantic error checking such as whether malloc() returns NULL or whether function parameters
  are valid. While appropriate for industrial programs, such checks are not needed in learning projects
  like this and distract from the goals.
   
10 WORK_DISCLOSURE.txt is present, describes collaborators and resources in reasonable detail
   
60 SUBTOTAL
  MAKEUP CREDIT Implementation of slurp_file_one_pass()
5 make test-makeup runs tests in test_banlet_makeup.org on slurp_file_one_pass()
5 Review of function body to meet indicated requirements with reasonable style / algorithm

6.2 Work Disclosure

In conjunction with the Free Collaboration policy for projects, all submissions must include a WORK_DISCLOSURE.txt file. This document outlines the resources that were utilized to complete the project. Each significant resource, be it course staff member, fellow student, website, textbook, AI, or other item should be named with at least a sentence describing how that item influenced the submission.

The rough format of these disclosures is provided in the template WORK_DISCLOSURE.txt file that is part of the project. This document will be checked for reasonable completeness by staff during Manual Inspection. The provided template document is below and should be edited and included in the project submission.

                           _________________

                            WORK DISCLOSURE
                           _________________


(A) HUMAN COLLABORATORS
=======================

  Aside from the person submitting this assignment, the following people
  contributed ideas and discussion to the completion of this work
  INCLUDING course staff members. Write NONE if no collaborators were
  involved.
  - Person 1 <person1@email.com> helped understand Problem X and the
    meaning of...
  - Person 2 <person2@email.com> helped debug Code for Problem Y...
  - etc.


(B) RESOURCE UTILIZATION
========================

  The following resources such as websites, course notes, artificial
  intelligence tools (LLMs/ChatBots/etc.) were utilized in the
  completion of this work. Include course materials such as textbooks
  and lecture slides as well. (Write NONE if no resources were used
  [which would be hard to believe]).
  - Resource 1 is here <https://some.resource.org/useful_stuff.html> and
    provided help for Problem Z to understand...
  - Resource 2 is the book "C Code for Dummies" by Boo Kauthor with
    chapter 8 helping a lot with the malloc()/free() usage on Problem W
  - Resource 3 is here <https://airegurgitator.com> and provided AI
    refinements for the algorithm used on problem Q and also helped
    debug code for Problem N.
  - etc.


(C) ADHERENCE TO THE PRIME DIRECTIVE
====================================

  PRIME DIRECTIVE: Be able to explain your own work including assignment
  answers, program code, and exam solutions. The work you submit should
  be the product of your own effort and reflect your personal
  understanding. (See the course syllabus for more information.)

  I submit this work in accordance with the PRIME DIRECTIVE. I affirm
  that I can explain the code and answers within as I created them and
  they reflect my personal understanding.

  Signed,

  <REPLACE WITH SUBMITTER NAME>

7 Makeup Credit

MAKEUP CREDIT are additional points that allow students to exceed the total of points possible for the project. These points go into the overall pool of points earned on projects and will make up for lost credit on this or other projects.

MAKEUP CREDIT is NOT EXTRA CREDIT: student scores on the project portion of the course will not exceed 100%. However, MAKEUP CREDIT allows for students to ensure they get full project credit even with some mistakes.

Makeup Credit is available on this Project in the form of an alternate implementation of the Slurp File Input functionality.

  • The required slurp_file_two_pass() function must be present as part of the base project requirements
  • Optionally students can complete slurp_file_one_pass() for up to 10 additional Makeup Credit points. It's description is included in the code outline and is copied below for reference.
char *slurp_file_one_pass(char *filename);
// OPTIONAL MAKEUP CREDIT: Like surp_file_two_pass() but uses a single
// I/O pass and dynamically expands memory to handle larger
// inputs. For full makeup credit, this approach must abide by the
// following additional conditions:
// - Does not use functions that determine the size of the file
// - Does not allocate/copy memory each loop iteration, only
//   "occasionally"
// - Does not use the realloc() function, only malloc()/free()
// - Has amortized linear time and linear space; the allocated array may
//   not be any larger than 2x the number of characters in it
// - Uses a memory allocation scheme similar to thos used with
//   dynamic arrays to meet the above requirement; see
//   https://en.wikipedia.org/wiki/Dynamic_array
// - Include comments describing your makeup credit approach
// - Is used in the main() function and/or elsewhere in place of the
//   other version

8 Project Submission

8.1 Submit to Gradescope

Submission is identical to lab work. There are two options to submit to Gradescope.

  1. In a terminal, run the command

       >> make submit
    

    and punch in your email/password on Gradescope to use the provided gradescope-submit script to upload your work. This is more convenient so is recommended.

  2. Run the command make zip to create p1-complete.zip, download this file and upload it to Gradescope through a web browser. This is a good fallback if you are having trouble with command line submission.

Refer to Lab01 Submission Instructions if you need a refresher on submitting.

8.2 Late Policies

You may wish to review the policy on late project submission which will cost 1 Engagement Point per day late. No projects will be accepted more than 48 hours after the deadline.

https://www.cs.umd.edu/~profk/216/syllabus.html#late-submission


Web Accessibility
Author: Chris Kauffman (profk@umd.edu)
Date: 2026-02-08 Sun 15:50