Última atividade 4 days ago

jordan's Avatar jordan revisou este gist 4 days ago. Ir para a revisão

1 file changed, 324 insertions

ssg.c(arquivo criado)

@@ -0,0 +1,324 @@
1 + /* ssg.c, v1.05 Wed Jun 26 00:13:54 EDT 2024 jordanreger */
2 +
3 + #include <dirent.h>
4 + #include <stdio.h>
5 + #include <stdlib.h>
6 + #include <string.h>
7 + #include <errno.h>
8 + #include <time.h>
9 + #include <sys/stat.h>
10 + #include <sys/types.h>
11 +
12 + #define MAX_PATH_LENGTH 512
13 + #define BUFFER_SIZE 1024
14 +
15 + /*
16 + * Returns nonzero if `file` IS a valid html file,
17 + * zero otherwise. Used as the filter to scandir.
18 + */
19 + int isHtmlFile(const struct dirent *file);
20 +
21 + /*
22 + * Returns nonzero if `file` IS a valid file,
23 + * zero otherwise. Used as the filter to scandir.
24 + */
25 + int isFile(const struct dirent *file);
26 +
27 + /*
28 + * Returns nonzero if `directory` IS a valid directory,
29 + * nonzero otherwise. Used as the filter to scandir.
30 + */
31 + int isDirectory(const struct dirent *directory);
32 +
33 + /*
34 + * Returns last modified time
35 + */
36 + char *lastModified(char *path);
37 +
38 + /*
39 + * Returns file size
40 + */
41 + off_t fileSize(char *path);
42 +
43 + /*
44 + * Recursively walk all directories starting from `dirname`
45 + * iteratively templating all html files within.
46 + */
47 + int walkDirectory(char *path);
48 +
49 + /*
50 + * For each valid html file in current directory...
51 + * X Open the HTML file
52 + * X Find templates (if any)
53 + * X Create a write buffer (FILE_NAME.html.tmpl) (use mode `aw+`)
54 + * X Construct new file with templates (tmpl/TEMPLATE_NAME.html)
55 + */
56 + void processHtmlFile(const char *file_path, const char *tmpl_dir);
57 +
58 +
59 + int main(int argc, char **argv)
60 + {
61 + char path[MAX_PATH_LENGTH] = {'.'}; // start from current dir
62 +
63 + /* manually set to start from current directory */
64 + 0[path] = '.';
65 + 1[path] = 0;
66 +
67 + walkDirectory(path);
68 + }
69 +
70 + int isHtmlFile(const struct dirent *file)
71 + {
72 + const char *extension_ptr = NULL;
73 +
74 + extension_ptr = strrchr(file->d_name, '.');
75 + if (NULL == extension_ptr)
76 + return 0;
77 +
78 + if (0 != strcmp(extension_ptr + 1, "html"))
79 + return 0;
80 +
81 + /* Valid html extension */
82 + return 1;
83 + }
84 +
85 + int isFile(const struct dirent *file)
86 + {
87 + return file->d_type == DT_REG
88 + && strcmp(file->d_name, "ssg")
89 + && strcmp(file->d_name, ".gitignore")
90 + && strcmp(file->d_name, ".build.yml");
91 + }
92 +
93 + int isDirectory(const struct dirent *directory)
94 + {
95 + return directory->d_type == DT_DIR
96 + && strcmp(directory->d_name, ".")
97 + && strcmp(directory->d_name, "..")
98 + && strcmp(directory->d_name, ".git")
99 + && strcmp(directory->d_name, ".tmpl");
100 +
101 + // TODO: Add a loop and use a custom .getignore-esque file perhaps
102 + }
103 +
104 + char *lastModified(char *path)
105 + {
106 + struct stat attr;
107 + stat(path, &attr);
108 + return ctime(&attr.st_mtime);
109 + }
110 +
111 + off_t fileSize(char *path)
112 + {
113 + struct stat attr;
114 + stat(path, &attr);
115 + return attr.st_size;
116 + }
117 +
118 + int walkDirectory(char *path)
119 + {
120 + struct dirent **directories = NULL,
121 + **files = NULL;
122 + int directory_count = -1,
123 + file_count = -1,
124 + index_found = 1;
125 +
126 + FILE *index = NULL;
127 + char index_path[MAX_PATH_LENGTH];
128 + char index_header[BUFFER_SIZE];
129 + char index_footer[BUFFER_SIZE];
130 +
131 + size_t initial_path_length = strlen(path);
132 +
133 + directory_count = scandir(path, &directories, isDirectory, alphasort);
134 + if (directory_count == -1) {
135 + perror("scandir");
136 + exit(EXIT_FAILURE);
137 + }
138 +
139 + snprintf(index_path, sizeof(index_path), "%s/index.html", path);
140 + index = fopen(index_path, "r");
141 + if (NULL == index) {
142 + index_found = 0;
143 + }
144 +
145 + if (0 == index_found) {
146 + index = fopen(index_path, "w");
147 + printf("%s", path);
148 + snprintf(index_header, sizeof(index_header), "%s%s%s", "<!DOCTYPE html>\n<html>\n{ head }\n<body>\n<h1>Index of /", path + 2, "</h1>\n<hr>\n<pre><a href='../'>../</a>\n");
149 + fprintf(index, "%s", index_header);
150 + }
151 +
152 + /* Base case: no directories are found */
153 + for (int i = 0; i < directory_count; i++) {
154 + char *directory_name = NULL;
155 + char directory_path[MAX_PATH_LENGTH];
156 + size_t directory_name_size = 0;
157 + char *dir_last_modified = NULL;
158 +
159 +
160 + directory_name = directories[i]->d_name;
161 + snprintf(directory_path, sizeof(directory_path), "%s/%s", path, directory_name);
162 + directory_name_size = strlen(directory_name);
163 + dir_last_modified = lastModified(directory_path);
164 +
165 + if (0 == index_found) {
166 + fprintf(index, "<a href='%s/'>%s/</a>\n", directory_name, directory_name);
167 + }
168 +
169 + /*
170 + * Bounds checking for the `path` buffer. Upon overflow
171 + * manually set the `File name too long` errno and exit
172 + */
173 + if (strlen(path) + directory_name_size + 2 >= MAX_PATH_LENGTH) {
174 + errno = ENAMETOOLONG;
175 + perror("Recursion");
176 + exit(EXIT_FAILURE);
177 + }
178 +
179 + path = strncat(path, "/", 2); // Add the `/` between entries
180 + path = strncat(path, directory_name, directory_name_size);
181 +
182 + walkDirectory(path);
183 +
184 + /*
185 + * Manually set the path back to where it was before
186 + * the recursive call to stop infinite recursion.
187 + * No one likes infinite recursion :(
188 + */
189 + path[initial_path_length] = 0;
190 +
191 + free(directories[i]);
192 + }
193 +
194 + free(directories);
195 +
196 + /* Get all files in current directory */
197 + file_count = scandir(path, &files, isFile, alphasort);
198 + if (-1 == file_count) {
199 + perror("scandir");
200 + exit(EXIT_FAILURE);
201 + }
202 +
203 + /* escape codes print directory name in blue */
204 + printf("\033[0;34m%s\033[0m\n", path);
205 +
206 + for (int i = 0; i < file_count; i++) {
207 + char file_path[MAX_PATH_LENGTH];
208 + char *file_last_modified = NULL;
209 + size_t file_size = 0;
210 +
211 + printf(" %s\n", files[i]->d_name);
212 + snprintf(file_path, sizeof(file_path), "%s/%s", path, files[i]->d_name);
213 + file_last_modified = lastModified(file_path);
214 + file_size = fileSize(file_path);
215 +
216 + if (0 == index_found && strcmp(files[i]->d_name, "index.html")) {
217 + fprintf(index, "<a href='%s'>%s</a>\n", files[i]->d_name, files[i]->d_name);
218 + }
219 +
220 + char full_path[MAX_PATH_LENGTH];
221 + snprintf(full_path, sizeof(full_path), "%s/%s", path, files[i]->d_name);
222 +
223 + if (isHtmlFile(files[i])) {
224 + processHtmlFile(full_path, "./.tmpl");
225 + }
226 +
227 + free(files[i]);
228 + }
229 +
230 + if (0 == index_found) {
231 + snprintf(index_footer, sizeof(index_footer), "%s", "</pre>\n<hr>\n</body>\n</html>\n");
232 + fprintf(index, "%s", index_footer);
233 + fclose(index);
234 + processHtmlFile(index_path, "./.tmpl");
235 + }
236 +
237 + free(files);
238 +
239 + return EXIT_SUCCESS;
240 + }
241 +
242 + void processHtmlFile(const char *file_path, const char *tmpl_dir)
243 + {
244 + FILE *file = NULL,
245 + *output_file = NULL;
246 +
247 + char output_path[MAX_PATH_LENGTH];
248 + char line_buffer[BUFFER_SIZE];
249 +
250 + file = fopen(file_path, "r");
251 + if (NULL == file) {
252 + perror("fopen");
253 + return;
254 + }
255 +
256 + snprintf(output_path, sizeof(output_path), "%s.tmpl", file_path);
257 +
258 + output_file = fopen(output_path, "w");
259 + if (NULL == output_file) {
260 + perror("fopen");
261 + fclose(file);
262 + return;
263 + }
264 +
265 +
266 + while (NULL != fgets(line_buffer, sizeof(line_buffer), file)) {
267 + char *start = line_buffer,
268 + *open_brace = NULL;
269 +
270 +
271 + while (NULL != (open_brace = strchr(start, '{'))) {
272 + char * close_brace = NULL;
273 + FILE *template_file = NULL;
274 + char template_name[BUFFER_SIZE];
275 + char tmpl_buffer[BUFFER_SIZE];
276 + size_t n;
277 +
278 + close_brace = strchr(open_brace, '}');
279 + if (close_brace == NULL) {
280 + break; // Error handling?
281 + }
282 +
283 + /* Segment file into strings around curly braces */
284 + *open_brace = '\0';
285 + *close_brace = '\0';
286 +
287 + /* Write upto first null byte into new file */
288 + fprintf(output_file, "%s", start);
289 +
290 + /* Get rid of template name leading space */
291 + while(open_brace++, ' ' == *open_brace)
292 + continue;
293 +
294 + /* Get rid of template name trailing space */
295 + while(close_brace--, ' ' == *(close_brace - 1))
296 + continue;
297 +
298 + /* Creates a substring around the template name */
299 + *close_brace = '\0';
300 +
301 + snprintf(template_name, sizeof(template_name), "%s/%s.html", tmpl_dir, open_brace);
302 +
303 + template_file = fopen(template_name, "r");
304 + if (NULL == template_file) {
305 + fprintf(stderr, "Template file %s not found\n", template_name);
306 + exit(EXIT_FAILURE);
307 + }
308 +
309 + // maybe getline would preserve tabs better?
310 + while (0 < (n = fread(tmpl_buffer, 1, sizeof(tmpl_buffer), template_file))) {
311 + fwrite(tmpl_buffer, 1, n, output_file);
312 + }
313 + fclose(template_file);
314 +
315 + // Continue parsing after current closing_brace
316 + start = close_brace + 1;
317 + }
318 +
319 + fprintf(output_file, "%s", start);
320 + }
321 +
322 + fclose(file);
323 + fclose(output_file);
324 + }
Próximo Anterior