---
description: 500 - 750 points
---

# A5: Printf

{% hint style="info" %}
**CMake target**: `a5`&#x20;

```bash
cmake --build .build --target a5
```
{% endhint %}

You probably have used the `printf` (Linux man page: `man printf`) function (or its relatives) many times already. With that, you likely have come to love and appreciate the abstractions it offers. If you haven't, there is a chance you will do so after having finished this assignment.

Ignoring its deserved place in the coding hall-of-fame, `printf` is just another function like any other. To prove that (because it needs to be proven, why should you just trust us here?!) in this assignment you will implement your own `printf` function, that matches the original in quite some of its functionality.

***

## Printf

The `printf` function is pretty flexible, especially in its number of arguments. How many arguments do you need to give it? 1. How many arguments can you give it? Well, pretty much unlimited.

`printf` takes a **variable number of arguments**, so the argument count is not fixed in its signature. The first argument is always the so-called _format string_. This argument is a pointer to a memory location of the first character of the string to print. This string, however, may not only include "regular" characters but also so-called _format specifiers_ (yes that's the official name of the `%d`s or `%u`s that you've been using so far). Whenever the function encounters such a specifier in the format string it will look at the next argument in the argument list for the correct substitution (at least for most specifiers, depending on the type).

***

## Assignment (500 points)

Implement a (simplified) `my_printf` subroutine. Unlike the full `printf`, your version only has to understand the format specifiers listen below. If a format specifier is not recognized, it should be printed without modification. Your function must follow the x86\_64 calling conventions. It must accept any number of arguments (so even tens or hundreds), like any standard `printf` implementation.

```c
void my_printf(char *fmt, ...);
```

Note that, compared to the original implementation, your subroutine will not return any value. Regularly, `printf` will return the length of the formatted string printed to standard output. You do not have to support this feature in your implementation.

### **Format Specifiers**

Your implementation should support the following format specifiers:

* `%d`: print a signed, 32-bit integer in decimal
* `%u`: print an unsigned, 32-bit integer in decimal
* `%s`: print a null-terminated string - no format specifiers should be parsed in this string
* `%%`: print a percentage sign

**Example:**

Suppose you have the following format string:

<pre><code><strong>"My name is %s and I am %u years old. My favorite ASCII character is %%."
</strong></code></pre>

and the additional arguments: `"Lennart"` and `21`, then the output of your subroutine should be:

```
My name is Lennart and I am 21 years old. My favorite ASCII character is %.
```

### Library Functions

{% hint style="danger" %}
You are not allowed to use **any** C library functions **except `putchar`** (Linux man page: `man putchar`) in your implementation. A working solution that uses additional library functions will not be accepted.
{% endhint %}

{% hint style="success" %}
You are of course encouraged to structure your implementation using helper functions written by yourself (in Assembly).
{% endhint %}

### Main Program

The `a5-printf.S` file includes a `main` subroutine. You **should** use this subroutine to test your implementation against the real `printf` call. However, your `main` subroutine will be ignored in grading and by the automatic tests.

CodeGrade provides a series of tests that are automatically compared to the real implementation of `printf`. When failing any of these tests, you will be shown the exact `my_printf` call used, including the format string and parameters.&#x20;

```
my_printf(FMT=My number: %d\n, ARG=(int32_t) 55)
[ERROR] my_printf did not match printf!
Expected output:
	My number: 55

Actual output:
	My number: ((not implemented))
```

Use the output to recreate any issues locally. In the example above, the call was `my_printf("My number: %d\n", 55)` and the expected output was `My number: 55`.

***

## Bonus (250 points)

Your implementation so far can print basic signed and unsigned numbers. A large part of the functionality of `printf`, however, comes from the additional formatting options that can be specified. To get an additional **250 bonus points**, you have to add support for width options including some flags (see the list below) to your implementation. You can find a comprehensive definition in any C standard library reference (like [this one](https://cplusplus.com/reference/cstdio/printf/)). Your implementation must **support all of the following**:

* **width specifiers**, both as part of the format specifier and as an additional argument (represented by an asterisk in the format specifier)
* the **minus**, **plus**, **space**, and **zero flags** - only for `%d` and `%u` specifiers
* **any combination** of flags in any order

{% hint style="warning" %}
It is **extremely important** to fully understand how these flags work before implementing your subroutine. Play around with `printf` and see how different calls behave when including special flags.

Additionally, you should see what happens when you pair conflicting parameters, such as the _plus_ and the _zero_ flags.&#x20;

Once you fully understand their functionality, you should set up a **testing suite** in your `main` subroutine. Always compare your output closely to the real `printf` implementation, perhaps analyzing the differences using [your diff implementation](a5-diff) or the [real diff](https://www.man7.org/linux/man-pages/man1/diff.1.html).
{% endhint %}
