main
xs_po.h
1/* copyright (c) 2025 - 2026 grunfink et al. / MIT license */
2
3#ifndef _XS_PO_H
4
5#define _XS_PO_H
6
7#include "xs.h"
8#include "xs_io.h"
9
10xs_dict *xs_po_to_dict(const char *fn);
11
12#ifdef XS_IMPLEMENTATION
13
14xs_dict *xs_po_to_dict(const char *fn)
15/* converts a PO file to a dict */
16{
17 xs_dict *d = NULL;
18 FILE *f;
19
20 if ((f = fopen(fn, "r")) != NULL) {
21 d = xs_dict_new();
22
23 xs *k = NULL;
24 xs *v = NULL;
25 enum { IN_NONE, IN_K, IN_V } mode = IN_NONE;
26
27 while (!feof(f)) {
28 xs *l = xs_strip_i(xs_readline(f));
29
30 /* discard empty lines and comments */
31 if (*l == '\0' || *l == '#')
32 continue;
33
34 if (xs_startswith(l, "msgid ")) {
35 if (mode == IN_V) {
36 /* flush */
37 if (xs_is_string(k) && xs_is_string(v) && *v)
38 d = xs_dict_set(d, k, v);
39
40 k = xs_free(k);
41 v = xs_free(v);
42 }
43
44 l = xs_replace_i(l, "msgid ", "");
45 mode = IN_K;
46
47 k = xs_str_new(NULL);
48 }
49 else
50 if (xs_startswith(l, "msgstr ")) {
51 if (mode != IN_K)
52 break;
53
54 l = xs_replace_i(l, "msgstr ", "");
55 mode = IN_V;
56
57 v = xs_str_new(NULL);
58 }
59
60 l = xs_replace_i(l, "\\n", "\n");
61 l = xs_strip_chars_i(l, "\"");
62
63 switch (mode) {
64 case IN_K:
65 k = xs_str_cat(k, l);
66 break;
67
68 case IN_V:
69 v = xs_str_cat(v, l);
70 break;
71
72 case IN_NONE:
73 break;
74 }
75 }
76
77 /* final flush */
78 if (xs_is_string(k) && xs_is_string(v) && *v)
79 d = xs_dict_set(d, k, v);
80
81 fclose(f);
82 }
83
84 return d;
85}
86
87#endif /* XS_IMPLEMENTATION */
88
89#endif /* XS_PO_H */