file-handling · beginner · ~10 min

Convert CRLF to LF

Stream-rewrite a buffer while changing its length safely.

Challenge

Normalize Windows-style line endings to Unix: turn every \r\n into a single \n and drop stray \r bytes, avoiding the cross-platform bugs they cause.

Task

Implement int normalize_eol(const char *in, char *out, size_t cap) that copies in to out (NUL-terminated), replacing each \r\n with \n and removing any lone \r.

Input

  • in: NUL-terminated source text.
  • out / cap: destination buffer and its size.

Output

Return the number of bytes written to out. Return -1 if in/out is NULL or the result doesn't fit in cap.

Example

normalize_eol("a\r\nb\r\n", out, 64)   ->   4,  out = "a\nb\n"
normalize_eol("plain\n", out, 64)        ->   out = "plain\n"  (unchanged)
normalize_eol("x\ry", out, 64)           ->   out = "xy"        (lone CR dropped)

Edge cases

  • Already-LF text is unchanged.
  • A lone \r (not followed by \n) is removed.
  • Output buffer too small: -1.

Rules

  • Reserve one byte for the NUL and check cap before each write. (Hint: dropping every \r handles both CRLF and lone CR.)

Why this matters

Windows files arrive with CRLF; normalising to LF avoids the stray '\r' bugs that plague cross-platform text handling.

Input format

in: NUL-terminated source text. out/cap: result buffer and size.

Output format

int bytes written to out, or -1 on NULL / overflow.

Constraints

Convert CRLF to LF and drop lone CR; bound each write by cap.

Starter code

#include <stddef.h>
int normalize_eol(const char *in, char *out, size_t cap) {
    /* TODO */
    (void)in; (void)out; (void)cap;
    return -1;
}

Common mistakes

Only handling CRLF and leaving lone CRs. Forgetting the NUL's byte in the cap check.

Edge cases to handle

Already-LF text is unchanged. A lone CR is removed. Tiny output buffer.

Complexity

O(n).

Background lessons

Up next

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.