💾 Archived View for retroforth.org › nga › docs › Unu.md captured on 2024-07-09 at 00:57:27.

View Raw

More Information

⬅️ Previous capture (2023-01-29)

-=-=-=-=-=-=-

# Unu

    unu
    (verb) (-hia) pull out, withdraw, draw out, extract.

Unu is a tool for extracting fenced code blocks from Markdown
documents.

I always found documenting my projects annoying. Eventually I
decided to start mixing the code and commentary using Markdown.
Unu is the tool I use to extract the sources from the original
files. I've found that this makes it easier for me to keep the
commentary up to date, and has lead to better commented code.

## The Code

First, headers:

~~~
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
~~~

I use this for readability purposes.

~~~
#define KiB * 1024
~~~

The `read_line` function is intended to read in a single line
into a target buffer. This considers a line to end with either
a `\n` or EOF.

~~~
void read_line(FILE *file, char *line_buffer) {
  int ch, count;

  if (file == NULL || line_buffer == NULL)
  {
    printf("Error: file or line buffer pointer is null.");
    exit(1);
  }

  ch = getc(file);
  count = 0;

  while ((ch != '\n') && (ch != EOF)) {
    line_buffer[count] = ch;
    count++;
    ch = getc(file);
  }

  line_buffer[count] = '\0';
}
~~~

The line buffer needs to be big enough for the longest lines in
your source files. Here it's set to 16KiB, which suffices for
everything I've used Unu with so far.

~~~
char source[16 KiB];
~~~

Unu looks for Markdown style fenced blocks of code. It supports
both backticks and tildes for this. This will return `1` if the
line appears to be a start/stop of a fence, or `0` otherwise.

~~~
int fenced(char *s)
{
  int a = strcmp(s, "```");
  int b = strcmp(s, "~~~");
  if (a == 0) return 1;
  if (b == 0) return 1;
              return 0;
}
~~~

The actual `extract` function is straightforward.


  * if in a fenced region, write the lines to stdout


~~~
void extract(char *fname) {
  char *buffer = (char *)source;
  char fence[4];
  FILE *fp;
  int inBlock;
  inBlock = 0;
  fp = fopen(fname, "r");
  if (fp == NULL)
    return;
  while (!feof(fp)) {
    read_line(fp, buffer);
    strncpy(fence, buffer, 3);
    fence[3] = '\0';
    if (fenced(fence)) {
      if (inBlock == 0)
        inBlock = 1;
      else
        inBlock = 0;
    } else {
      if ((inBlock == 1) && (strlen(buffer) != 0))
        printf("%s\n", buffer);
    }
  }
  fclose(fp);
}
~~~

And finally, the main routine, which just runs `extract` on
each specified file.

~~~
int main(int argc, char **argv) {
  int i = 1;
  if (argc > 1) {
    while (i < argc) {
      extract(argv[i++]);
    }
  }
  else
    printf("unu\n(c) 2013-2019 charles childers\n\nTry:\n  %s filename\n", argv[0]);
  return 0;
}
~~~