Parsing /etc/passwdΒΆ

This example shows how to parse simple file formats such as the /etc/passwd file. This file consists of multiple lines of the form user : password : UID : GID : info : home : command.

[etc_passwd.re]

 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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// re2c $INPUT -o $OUTPUT -i
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

/*!max:re2c*/

static int lex(const char *YYCURSOR)
{
    const char *YYMARKER, *n, *p, *u, *g, *f, *h, *c;
    /*!stags:re2c format = 'const char *@@;'; */
loop:
    /*!re2c
        re2c:define:YYCTYPE = char;
        re2c:yyfill:enable = 0;
        re2c:flags:tags = 1;

        end  = "\x00";
        eol  = "\n";
        sep  = [:];
        char = [^] \ (end | eol | sep);
        user = char+;
        pass = char*;
        uid  = [0-9]+;
        gid  = [0-9]+;
        info = char*;
        home = "/" char*;
        cmd  = "/" char*;

        *   { fprintf(stderr, "error\n"); return 1; }
        end { return 0; }

        @n user sep
        @p pass sep
        @u uid  sep
        @g gid  sep
        @f info sep
        @h home sep
        @c cmd  eol {
            fprintf(stderr, "user:     %.*s\n", (int)(p - n) - 1, n);
            fprintf(stderr, "password: %.*s\n", (int)(u - p) - 1, p);
            fprintf(stderr, "UID:      %.*s\n", (int)(g - u) - 1, u);
            fprintf(stderr, "GID:      %.*s\n", (int)(f - g) - 1, g);
            fprintf(stderr, "info:     %.*s\n", (int)(h - f) - 1, f);
            fprintf(stderr, "home:     %.*s\n", (int)(c - h) - 1, h);
            fprintf(stderr, "command:  %.*s\n", (int)(YYCURSOR - c - 1), c);
            fprintf(stderr, "\n");
            goto loop;
        }
    */
}

int main()
{
    const char *fname = "etc_passwd";
    FILE *f;

    // prepare input file
    f = fopen(fname, "w");
    fprintf(f,
        "root:x:0:0:root:/root:/bin/bash\n"
        "bin:x:1:1:bin:/bin:/bin/false\n"
        "portage:x:250:250:portage:/var/tmp/portage:/bin/false\n");
    fclose(f);

    // read input file into buffer
    f = fopen(fname, "r");
    fseek(f, 0, SEEK_END);
    const size_t fsize = (size_t) ftell(f);
    fseek(f, 0, SEEK_SET);
    char *buffer = (char*) malloc(fsize + 1);
    fread(buffer, 1, fsize, f);
    buffer[fsize] = 0;
    fclose(f);

    assert(lex(buffer) == 0);

    // cleanup
    remove(fname);
    free(buffer);
    return 0;
}

Compile as: re2c -o etc_passwd.c etc_passwd.re.