Misc updates..
[site] / src / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <markdown_lib.h>
6
7 #include "html.h"
8
9 /* HTML helper functions */
10 void href(const char *url)
11 {
12         const char *path   = getenv("REQUEST_URI");
13         const char *script = getenv("SCRIPT_NAME");
14         if (path && script) {
15                 for (; *path; path++, script++)
16                         if (*path != *script)
17                                 break;
18                 for (; *path; path++)
19                         if (*path == '/')
20                                 printf("../");
21         }
22         printf("%s", url[0] ? url : ".");
23 }
24
25 int str_sort(gconstpointer _a, gconstpointer _b)
26 {
27         const char **a = (const char**)_a;
28         const char **b = (const char**)_b;
29         return strcmp(*a, *b);
30 }
31
32 gchar **lsdir(gchar *path)
33 {
34         GPtrArray *ents = g_ptr_array_new();
35         GDir      *dir  = g_dir_open(path, 0, NULL);
36         if (dir) {
37                 const char *name;
38                 while ((name = g_dir_read_name(dir))) {
39                         if (name[0] == '.')
40                                 continue;
41                         if (name[strlen(name)-1] == '~')
42                                 continue;
43                         g_ptr_array_add(ents, g_strdup(name));
44                 }
45                 g_dir_close(dir);
46         }
47         g_ptr_array_sort(ents, str_sort);
48         g_ptr_array_add(ents, NULL);
49         return (gchar**)g_ptr_array_free(ents, FALSE);
50 }
51
52 page_t *get_page(const char *path)
53 {
54         char     *dir  = g_path_get_dirname(path);
55         char     *base = g_path_get_basename(path);
56         char     *ini  = g_build_filename(BASE, dir, "menu.ini", NULL);
57         GKeyFile *conf = g_key_file_new();
58
59         g_key_file_load_from_file(conf, ini, 0, NULL);
60         //printf("load: %s -> %s\n", path, ini);
61
62         page_t *page = g_new0(page_t, 1);
63
64         page->title = g_key_file_get_string(conf, base, "title", NULL);
65         page->keys  = g_key_file_get_string(conf, base, "keys",  NULL);
66         page->desc  = g_key_file_get_string(conf, base, "desc",  NULL);
67
68         g_key_file_free(conf);
69         g_free(ini);
70         g_free(base);
71         g_free(dir);
72
73         return page;
74 }
75
76 menu_t *get_menu_entry(const char *prefix, const char *entry)
77 {
78         static GRegex *regex = NULL;
79         if (regex == NULL)
80                 regex = g_regex_new("^([a-zA-Z0-9_-]+)\\.md$", 0, 0, NULL);
81
82         GMatchInfo *matches = NULL;
83         menu_t *menu = NULL;
84         char *path = g_build_filename(BASE, prefix, entry, NULL);
85
86         /* Test for directory */
87         if (g_file_test(path, G_FILE_TEST_IS_DIR)) {
88                 menu = g_new0(menu_t, 1);
89                 menu->path = g_build_filename(prefix, entry, "/", NULL);
90                 menu->name = g_strdup(entry);
91                 menu->base = g_strdup(entry);
92                 //printf("\tchild=[%s]\n", path);
93         }
94
95         /* Test for markdown */
96         else if (g_file_test(path, G_FILE_TEST_IS_REGULAR) &&
97                  g_regex_match(regex, entry, 0, &matches)) {
98                 char *name = g_match_info_fetch(matches, 1);
99                 menu = g_new0(menu_t, 1);
100                 menu->path = g_build_filename(prefix, name, NULL);
101                 menu->name = g_strdup(name);
102                 menu->base = g_strdup(name);
103                 for (char *c = menu->name; *c; c++)
104                         if (*c == '_')
105                                 *c = ' ';
106                 //menu->name[0] = toupper(menu->name[0]);
107                 g_match_info_free(matches);
108                 //printf("\tname=[%s]\n", name);
109         }
110
111         g_free(path);
112         return menu;
113 }
114
115 menu_t *get_menu_rec(char *prefix, char **parts)
116 {
117         menu_t  head = {};
118         menu_t *cur  = &head;
119         menu_t *next = NULL;
120         char **entries = NULL;
121
122         char *path = g_build_filename(BASE, prefix, NULL);
123         if (!g_file_test(path, G_FILE_TEST_IS_DIR))
124                 goto error;
125
126         /* Load menu from key file */
127         char     *ini  = g_build_filename(path, "menu.ini", NULL);
128         GKeyFile *conf = g_key_file_new();
129         g_key_file_load_from_file(conf, ini, 0, NULL);
130         entries = g_key_file_get_groups(conf, NULL);
131         for (int i = 0; entries[i]; i++) {
132                 next = get_menu_entry(prefix, entries[i]);
133                 if (!next) {
134                         next = g_new0(menu_t, 1);
135                         next->base = g_strdup(entries[i]);
136                 }
137                 char *pathval = g_key_file_get_string(conf, entries[i], "path", NULL);
138                 char *nameval = g_key_file_get_string(conf, entries[i], "name", NULL);
139                 if (pathval) next->path = pathval;
140                 if (nameval) next->name = nameval;
141                 if (!next->path) next->path = g_strdup(entries[i]);
142                 if (!next->name) next->name = g_strdup(entries[i]);
143                 if (!parts[0] && !strcmp(next->base, "index"))
144                         next->show = SHOW_ACTIVE;
145                 cur = cur->next = next;
146         }
147         g_strfreev(entries);
148         g_free(ini);
149
150         /* Load menu from directory entries */
151         entries = lsdir(path);
152         for (int i = 0; entries[i]; i++) {
153                 //printf("test: path=%s entry=%s\n", path, entries[i]);
154                 if (g_key_file_has_group(conf, entries[i]))
155                         continue;
156                 if ((next = get_menu_entry(prefix, entries[i])))
157                         cur = cur->next = next;
158                 if (!strcmp(cur->base, "index"))
159                         cur->show = SHOW_HIDDEN;
160         }
161         g_strfreev(entries);
162         g_key_file_free(conf);
163
164         /* Add child nodes */
165         for (cur = head.next; cur; cur = cur->next) {
166                 if (parts && parts[0] && cur->base && !strcmp(cur->base, parts[0])) {
167                         //printf("test: [%s, %s == %s]\n",
168                         //      cur->path, cur->base, parts[0]);
169                         cur->kids = get_menu_rec(cur->path, parts+1);
170                         cur->show = SHOW_ACTIVE;
171                 }
172         }
173
174 error:
175         g_free(path);
176         return head.next;
177 }
178
179 menu_t *get_menu(char *path)
180 {
181 #if 0
182         static menu_t dev      = { "dev",      "Develop",  NULL,      NULL   };
183         static menu_t news     = { "news",     "News",     &dev,      NULL   };
184         static menu_t about    = { "about",    "About",    &news,     NULL   };
185
186         static menu_t lackey   = { "lackey",   "lackey",   NULL,      NULL   };
187         static menu_t wmpus    = { "wmpus",    "wmpus",    &lackey,   NULL   };
188         static menu_t grits    = { "grits",    "Grits",    &wmpus,    &about };
189         static menu_t aweather = { "aweather", "AWeather", &grits,    NULL   };
190         static menu_t home     = { "",         "Home",     &aweather, NULL   };
191
192         return &home;
193 #else
194         char  **parts = g_strsplit(path, "/", -1);
195         menu_t *menu = get_menu_rec("", parts);
196         g_strfreev(parts);
197         return menu;
198 #endif
199 }
200
201 void print_menu(menu_t *menu, int first, int last)
202 {
203         for (menu_t *cur = menu; cur; cur = cur->next) {
204                 if (cur->show == SHOW_HIDDEN)
205                         continue;
206                 if (first <= 0)
207                         print_link(cur->path, cur->name,
208                                 cur->show == SHOW_ACTIVE);
209                 if (cur->kids && last != 0)
210                         print_menu(cur->kids, first-1, last-1);
211         }
212 }
213
214 void debug_menu(page_t *page, menu_t *menu, int depth)
215 {
216         if (page) {
217                 printf("title: '%s'\n", page->title);
218                 printf("keys:  '%s'\n", page->keys);
219                 printf("desc:  '%s'\n", page->desc);
220         }
221         for (menu_t *cur = menu; cur; cur = cur->next) {
222                 for (int i = 0; i < depth; i++)
223                         printf("  ");
224                 printf("menu: %d %s %s '%s'\n",
225                         cur->show, cur->base, cur->path, cur->name);
226                 if (cur->kids)
227                         debug_menu(NULL, cur->kids, depth+1);
228         }
229 }
230
231 void free_menu(menu_t *menu)
232 {
233 }
234
235 /* Page display methods */
236 static void do_lsdir(page_t *page, const char *dir)
237 {
238         page->html = "Lsdir";
239 }
240
241 static void do_regular(page_t *page, const char *file)
242 {
243         const char *query = getenv("QUERY_STRING") ?: "";
244         if (g_file_get_contents(file, &page->text, NULL, NULL)) {
245                 if (!strcmp(query, "src"))
246                         page->html = "source";
247                 else if (!strcmp(query, "hist"))
248                         page->html = "history";
249                 else
250                         page->html = markdown_to_string(page->text, 0, 0);
251         } else {
252                 page->error = g_strdup("403 Forbidden");
253                 page->html  = g_strdup("Page not accessable\n");
254         }
255 }
256
257 static void do_notfound(page_t *page, const char *path)
258 {
259         page->error = g_strdup("404 Not Found");
260         page->html  = g_strdup("Page not found\n");
261 }
262
263 static char *clean(const char *src)
264 {
265         int   len = strlen(src);
266         char *dst = malloc(len+1);
267         int si=0, ei=len-1, di=0;
268         while (src[si] == '/')
269                 si++;
270         while (src[ei] == '/')
271                 ei--;
272         while (si <= ei) {
273                 dst[di++] = src[si++];
274                 while (src[si] == '/' && src[si+1] == '/')
275                         si++;
276         }
277         dst[di] = '\0';
278         return dst;
279 }
280
281 /* Main */
282 int main(int argc, char **argv)
283 {
284         char *path = clean(getenv("PATH_INFO") ?: "/");
285
286         page_t *page = get_page(path);
287         menu_t *menu = get_menu(path);
288         //debug_menu(page, menu, 0);
289         //return 0;
290
291         char *dir   = g_strdup_printf("pages/%s",          path);
292         char *index = g_strdup_printf("pages/%s/index.md", path);
293         char *file  = g_strdup_printf("pages/%s.md",       path);
294
295         if (g_file_test(index, G_FILE_TEST_IS_REGULAR))
296                 do_regular(page, index);
297         else if (g_file_test(file, G_FILE_TEST_IS_REGULAR))
298                 do_regular(page, file);
299         else if (g_file_test(dir, G_FILE_TEST_IS_DIR))
300                 do_lsdir(page, dir);
301         else
302                 do_notfound(page, path);
303
304         g_free(dir);
305         g_free(index);
306         g_free(file);
307
308         print_header(page);
309         print_page(page, menu);
310
311         return 0;
312 }