nrdblg

Ever needed to generate a QR code without pulling in a massive framework? Let’s build a simple CLI tool in C that takes a URL and spits out a scannable QR code right in your terminal. Pure C, no fluff. 🎯

The Library

We’re using qrcodegen by Project Nayuki. It’s a clean, dependency-free C library (MIT licensed) that handles all the QR encoding magic. Just grab qrcodegen.c and qrcodegen.h and drop them in your project folder.

The library supports:

The Includes

1
2
3
4
#include <stdio.h>
#include <stdlib.h>
#include <regex.h>
#include "qrcodegen.h"

We need regex.h for input validation (POSIX regex) and qrcodegen.h for the QR magic.

Input Validation

First, let’s make sure we get exactly one URL as input and that it looks legit:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
int main(int argc, char *argv[])
{
  int sizeOfArgv = 0;
  for (int i = 1; i < argc; i++) {
    printf("%s\n", argv[i]);
    sizeOfArgv++;
  }

  char *inputUrl = argv[1];
  regex_t regex;
  int ret;

  // Regular expression pattern for basic URL validation:
  // ^(https?://)         -> URL must start with "http://" or "https://"
  // ([a-zA-Z0-9.-]+)      -> Domain name with letters, digits, dots, or hyphens
  // (:[0-9]+)?           -> Optional port number
  // (/[a-zA-Z0-9./?=&%-]*)?$ -> Optional path and query parameters
  char *pattern = "^(https?://)([a-zA-Z0-9.-]+)(:[0-9]+)?(/[a-zA-Z0-9./?=&%-]*)?$";

  // Compile the regular expression with extended syntax
  ret = regcomp(&regex, pattern, REG_EXTENDED);
  if (ret) {
    printf("Could not compile regex\n");
    return EXIT_FAILURE;
  }

  // Execute the regular expression on the URL string
  ret = regexec(&regex, inputUrl, 0, NULL, 0);

  if (sizeOfArgv > 1 || ret != 0) {
    printf("Please only provide a single URL as an input.");
    return EXIT_FAILURE;
  }

  // Free the compiled regular expression
  regfree(&regex);

The regex is basic but catches the essentials: protocol (http:// or https://), domain, optional port, and path. We also enforce a single argument - no batch mode shenanigans here.

Generating the QR Code

Here’s where qrcodegen does its thing. We need two buffers:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
  // Buffers for QR code generation
  uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX];
  uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX];

  bool ok = qrcodegen_encodeText(
      inputUrl,
      tempBuffer,
      qrcode,
      qrcodegen_Ecc_MEDIUM,      // Error correction level
      qrcodegen_VERSION_MIN,     // Min version (size)
      qrcodegen_VERSION_MAX,     // Max version
      qrcodegen_Mask_AUTO,       // Let it pick the best mask
      true                       // Boost ECC if possible
  );

  if (!ok) {
    printf("Failed to generate QR code\n");
    return EXIT_FAILURE;
  }

A few things happening here:

Printing to Terminal

Now the fun part - rendering it with Unicode block characters:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
  int size = qrcodegen_getSize(qrcode);

  // Print with a quiet zone (border)
  for (int y = -2; y < size + 2; y++) {
    for (int x = -2; x < size + 2; x++) {
      if (qrcodegen_getModule(qrcode, x, y)) {
        printf("██");  // Dark module
      } else {
        printf("  ");  // Light module
      }
    }
    printf("\n");
  }

  return EXIT_SUCCESS;
}

We print ██ (two full blocks) for dark modules and two spaces for light ones. This keeps the aspect ratio square-ish in most terminal fonts.

Compile and Run

1
2
gcc -o qrcode qrcode.c qrcodegen.c
./qrcode "https://example.com"

And boom 💥 - a scannable QR code right in your terminal. Try it with your phone’s camera!

The Complete Code

Here’s everything in one place:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <stdio.h>
#include <stdlib.h>
#include "qrcodegen.h"
#include <regex.h>

int main(int argc, char *argv[])
{
  int sizeOfArgv = 0;
  for (int i = 1; i < argc; i++) {
    printf("%s\n", argv[i]);
    sizeOfArgv++;
  }

  char *inputUrl = argv[1];
  regex_t regex;
  int ret;

  char *pattern = "^(https?://)([a-zA-Z0-9.-]+)(:[0-9]+)?(/[a-zA-Z0-9./?=&%-]*)?$";

  ret = regcomp(&regex, pattern, REG_EXTENDED);
  if (ret) {
    printf("Could not compile regex\n");
    return EXIT_FAILURE;
  }

  ret = regexec(&regex, inputUrl, 0, NULL, 0);

  if (sizeOfArgv > 1 || ret != 0) {
    printf("Please only provide a single URL as an input.");
    return EXIT_FAILURE;
  }

  regfree(&regex);

  uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX];
  uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX];

  bool ok = qrcodegen_encodeText(
      inputUrl,
      tempBuffer,
      qrcode,
      qrcodegen_Ecc_MEDIUM,
      qrcodegen_VERSION_MIN,
      qrcodegen_VERSION_MAX,
      qrcodegen_Mask_AUTO,
      true
  );

  if (!ok) {
    printf("Failed to generate QR code\n");
    return EXIT_FAILURE;
  }

  int size = qrcodegen_getSize(qrcode);

  for (int y = -2; y < size + 2; y++) {
    for (int x = -2; x < size + 2; x++) {
      if (qrcodegen_getModule(qrcode, x, y)) {
        printf("██");
      } else {
        printf("  ");
      }
    }
    printf("\n");
  }

  return EXIT_SUCCESS;
}

What’s Next?

This is just the basics. You could extend it to:

The qrcodegen library handles all the heavy lifting - Reed-Solomon error correction, mask pattern optimization, version selection. We just feed it text and it handles the rest automagically. And I love it. 🖤

#C #Qrcode