1 // Copyright (c) 2015 Sergio Gonzalez. All rights reserved.
2 // License: https://github.com/serge-rgb/milton#license
3
4 #include <errno.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdint.h>
8 #include <string.h>
9
10 #include "milton_configuration.h"
11
12
13 typedef int8_t i8;
14 typedef int16_t i16;
15 typedef int32_t i32;
16 typedef int64_t i64;
17
18 void handle_errno(int error);
19
20
21 static size_t
bytes_until_end(FILE * fd)22 bytes_until_end(FILE* fd)
23 {
24 fpos_t fd_pos;
25 fgetpos(fd, &fd_pos);
26 fseek(fd, 0, SEEK_END);
27 size_t len = (size_t)ftell(fd);
28 fsetpos(fd, &fd_pos);
29 return len;
30 }
31
32 char*
read_entire_file(const char * fname)33 read_entire_file(const char* fname)
34 {
35 char* contents = NULL;
36 FILE* fd = fopen(fname, "r");
37 if ( fd ) {
38 auto sz = bytes_until_end(fd);
39 contents = (char*)calloc(1, sz+2); // Add 2. A \0 char and a \n if last line not terminated
40 if ( contents ) {
41 auto read = fread(contents, 1, sz, fd);
42 // Append a newline if the last line is not newline terminated.
43 if ( contents[read-1] != '\n' ) {
44 contents[read++] = '\n';
45 }
46 contents[read]='\0';
47 }
48 else {
49 fprintf(stderr, "Could not allocate memory for reading file.\n");
50 }
51 fclose(fd);
52 }
53 else {
54 int error = errno;
55 fprintf(stderr, "Could not open shader for reading\n");
56 handle_errno(error);
57 }
58 return contents;
59 }
60
61 #define MAX_LINES 10000
62
63 char**
split_lines(char * contents,i64 * out_count,i64 * max_line=NULL)64 split_lines(char* contents, i64* out_count, i64* max_line=NULL)
65 {
66
67 char** lines = (char**)calloc(1, sizeof(char**)*MAX_LINES);
68 i64 lines_i = 0;
69
70 char* begin = contents;
71 char* iter = contents;
72 for ( ;
73 *iter!='\0';
74 ++iter ) {
75
76 if ( *iter == '\n' || *(iter+1) == '\0' ) {
77 i64 this_len = iter - begin;
78
79 if ( max_line != NULL ) {
80 if ( this_len > *max_line ) {
81 *max_line = this_len;
82 }
83 }
84 // Copy a string from beginning
85 char* line = (char*)calloc(1, (size_t)this_len*2 + 2);
86
87 int line_i = 0;
88 for ( int i = 0; i < this_len; ++i ) {
89 if ( begin[i] == '\"' ) {
90 line[line_i++] = 'Q';
91 }
92 else if (begin[i] != '\r' && begin[i] != '\n') {
93 line[line_i++] = begin[i];
94 }
95 }
96 line[line_i++] = '\n';
97 line[line_i++] = '\0';
98
99 lines[lines_i++] = line;
100
101 begin = iter+1;
102 }
103 }
104 *out_count = lines_i;
105 return lines;
106 }
107
108 // Computes the variable name of the form g_NAME_(v|f) for a given shader filename
109 // e.g. "../src/my_shader.v.glsl" -> g_my_shader_v
110 int
shadername(const char * filename,char * out_varname,size_t sz_varname)111 shadername(const char* filename, char* out_varname, size_t sz_varname)
112 {
113 if ( filename && out_varname ) {
114 // Skip filename until after all forward slashes.
115 const char* last_forward_slash = NULL;
116 for ( const char* c = filename; *c!='\0'; ++c ) {
117 if ( *c == '/' ) {
118 last_forward_slash = c;
119 }
120 }
121 if ( last_forward_slash ) {
122 filename = last_forward_slash+1;
123 }
124 size_t len_filename = strlen(filename);
125 char prefix[] = "g_";
126 size_t len_prefix = strlen(prefix);
127 if ( len_filename+3 <= sz_varname ) {
128 size_t out_i = 0;
129 size_t count_dots = 0;
130 // Append the prefix
131 for ( size_t pi = 0; pi < len_prefix; ++pi ) {
132 out_varname[out_i++] = prefix[pi];
133 }
134 // Append name. Translate the first dot to _
135 for ( const char* c = filename;
136 *c != '\0';
137 ++c ) {
138 if ( *c != '.' ) {
139 out_varname[out_i++] = *c;
140 } else {
141 if ( count_dots++ == 0 ) {
142 out_varname[out_i++] = '_';
143 } else {
144 break;
145 }
146 }
147 }
148 out_varname[out_i] = '\0';
149 } else {
150 return -1;
151 }
152 }
153 return 0;
154 }
155
156 #define VARNAME_MAX 128
157
158 void
output_shader(FILE * of,const char * fname,const char * fname_prelude=NULL)159 output_shader(FILE* of, const char* fname, const char* fname_prelude = NULL)
160 {
161 char* contents = read_entire_file(fname);
162 char** prelude_lines = NULL;
163 i64 prelude_lines_count = 0;
164 if ( fname_prelude ) {
165 char* prelude = read_entire_file(fname_prelude);
166 prelude_lines = split_lines(prelude, &prelude_lines_count);
167 }
168 char** lines;
169 i64 count;
170 lines = split_lines(contents, &count);
171
172 char varname[VARNAME_MAX] = {};
173 int err = shadername(fname, varname, VARNAME_MAX);
174 if ( err != 0 ) {
175 fprintf(stderr, "Error when computing the variable name for file %s\n", fname);
176 return;
177 }
178
179 fprintf(of, "static char %s[] = \n", varname);
180 for ( i64 i = 0; i < prelude_lines_count; ++i ) {
181 size_t len = strlen(prelude_lines[i]);
182 prelude_lines[i][len-1]='\0'; // Strip newline
183 fprintf(of, "\"%s\\n\"\n", prelude_lines[i]);
184 }
185 for ( i64 i = 0; i < count; ++i ) {
186 lines[i][strlen(lines[i])-1]='\0'; // Strip newline
187 fprintf(of, "\"%s\\n\"\n", lines[i]);
188 }
189 fprintf(of, ";\n");
190 }
191
192 // Assuming this is being called from the build directory
193 int
main(int argc,char ** argv)194 main(int argc, char** argv)
195 {
196 fprintf(stderr, "Generating shader code...\n");
197
198 FILE* outfd = fopen("src/shaders.gen.h", "w");
199 if ( outfd ) {
200 output_shader(outfd, "src/picker.v.glsl");
201 output_shader(outfd, "src/picker.f.glsl");
202 output_shader(outfd, "src/layer_blend.v.glsl");
203 output_shader(outfd, "src/layer_blend.f.glsl");
204 output_shader(outfd, "src/simple.v.glsl");
205 output_shader(outfd, "src/simple.f.glsl");
206 output_shader(outfd, "src/outline.v.glsl");
207 output_shader(outfd, "src/outline.f.glsl");
208 output_shader(outfd, "src/stroke_raster.v.glsl", "src/common.glsl");
209 output_shader(outfd, "src/stroke_raster.f.glsl", "src/common.glsl");
210 output_shader(outfd, "src/stroke_eraser.f.glsl", "src/common.glsl");
211 output_shader(outfd, "src/stroke_info.f.glsl", "src/common.glsl");
212 output_shader(outfd, "src/stroke_fill.f.glsl", "src/common.glsl");
213 output_shader(outfd, "src/stroke_clear.f.glsl", "src/common.glsl");
214 output_shader(outfd, "src/stroke_debug.f.glsl", "src/common.glsl");
215 output_shader(outfd, "src/exporter_rect.f.glsl");
216 output_shader(outfd, "src/texture_fill.f.glsl");
217 output_shader(outfd, "src/quad.v.glsl");
218 output_shader(outfd, "src/quad.f.glsl");
219 output_shader(outfd, "src/postproc.f.glsl", "third_party/Fxaa3_11.f.glsl");
220 output_shader(outfd, "src/blur.f.glsl");
221
222 fclose(outfd);
223 }
224 else {
225 fprintf(stderr, "Could not open output file.\n");
226 int error = errno;
227 handle_errno(error);
228 }
229 fprintf(stderr, "Shaders generated OK\n");
230 return EXIT_SUCCESS;
231
232 }
233
234 void
handle_errno(int error)235 handle_errno(int error)
236 {
237 const char* str = NULL;
238 switch ( error ) {
239 case E2BIG: str = "Argument list too long (POSIX.1)"; break;
240
241 case EACCES: str = "Permission denied (POSIX.1)"; break;
242
243 case EADDRINUSE: str = "Address already in use (POSIX.1)"; break;
244
245 case EADDRNOTAVAIL: str = "Address not available (POSIX.1)"; break;
246
247 case EAFNOSUPPORT: str = "Address family not supported (POSIX.1)"; break;
248
249 case EAGAIN: str = "Resource temporarily unavailable (may be the same value as EWOULDBLOCK) (POSIX.1)"; break;
250
251 case EALREADY: str = "Connection already in progress (POSIX.1)"; break;
252
253 // case EBADE: str = "Invalid exchange"; break;
254
255 case EBADF: str = "Bad file descriptor (POSIX.1)"; break;
256
257 // case EBADFD: str = "File descriptor in bad state"; break;
258
259 case EBADMSG: str = "Bad message (POSIX.1)"; break;
260
261 // case EBADR: str = "Invalid request descriptor"; break;
262
263 // case EBADRQC: str = "Invalid request code"; break;
264
265 // case EBADSLT: str = "Invalid slot"; break;
266
267 case EBUSY: str = "Device or resource busy (POSIX.1)"; break;
268
269 case ECANCELED: str = "Operation canceled (POSIX.1)"; break;
270
271 case ECHILD: str = "No child processes (POSIX.1)"; break;
272
273 // case ECHRNG: str = "Channel number out of range"; break;
274
275 // case ECOMM: str = "Communication error on send"; break;
276
277 case ECONNABORTED: str = "Connection aborted (POSIX.1)"; break;
278
279 case ECONNREFUSED: str = "Connection refused (POSIX.1)"; break;
280
281 case ECONNRESET: str = "Connection reset (POSIX.1)"; break;
282
283 case EDEADLK: str = "Resource deadlock avoided (POSIX.1)"; break;
284
285 // case EDEADLOCK: str = "Synonym for EDEADLK"; break;
286
287 case EDESTADDRREQ: str = "Destination address required (POSIX.1)"; break;
288
289 case EDOM: str = "Mathematics argument out of domain of function (POSIX.1, C99)"; break;
290
291 // case EDQUOT: str = "Disk quota exceeded (POSIX.1)"; break;
292
293 case EEXIST: str = "File exists (POSIX.1)"; break;
294
295 case EFAULT: str = "Bad address (POSIX.1)"; break;
296
297 case EFBIG: str = "File too large (POSIX.1)"; break;
298
299 // case EHOSTDOWN: str = "Host is down"; break;
300
301 case EHOSTUNREACH: str = "Host is unreachable (POSIX.1)"; break;
302
303 case EIDRM: str = "Identifier removed (POSIX.1)"; break;
304
305 case EILSEQ: str = "Illegal byte sequence (POSIX.1, C99)"; break;
306
307 case EINPROGRESS: str = "Operation in progress (POSIX.1)"; break;
308
309 case EINTR: str = "Interrupted function call (POSIX.1); see signal(7)."; break;
310
311 case EINVAL: str = "Invalid argument (POSIX.1)"; break;
312
313 case EIO: str = "Input/output error (POSIX.1)"; break;
314
315 case EISCONN: str = "Socket is connected (POSIX.1)"; break;
316
317 case EISDIR: str = "Is a directory (POSIX.1)"; break;
318
319 // case EISNAM: str = "Is a named type file"; break;
320
321 // case EKEYEXPIRED: str = "Key has expired"; break;
322
323 // case EKEYREJECTED: str = "Key was rejected by service"; break;
324
325 // case EKEYREVOKED: str = "Key has been revoked"; break;
326
327 // case EL2HLT: str = "Level 2 halted"; break;
328
329 // case EL2NSYNC: str = "Level 2 not synchronized"; break;
330
331 // case EL3HLT: str = "Level 3 halted"; break;
332
333 // case EL3RST: str = "Level 3 halted"; break;
334
335 // case ELIBACC: str = "Cannot access a needed shared library"; break;
336
337 // case ELIBBAD: str = "Accessing a corrupted shared library"; break;
338
339 // case ELIBMAX: str = "Attempting to link in too many shared libraries"; break;
340
341 // case ELIBSCN: str = "lib section in a.out corrupted"; break;
342
343 // case ELIBEXEC: str = "Cannot exec a shared library directly"; break;
344
345 case ELOOP: str = "Too many levels of symbolic links (POSIX.1)"; break;
346
347 // case EMEDIUMTYPE: str = "Wrong medium type"; break;
348
349 case EMFILE: str = "Too many open files (POSIX.1); commonly caused by exceeding the RLIMIT_NOFILE resource limit described in getrlimit(2)"; break;
350
351 case EMLINK: str = "Too many links (POSIX.1)"; break;
352
353 case EMSGSIZE: str = "Message too long (POSIX.1)"; break;
354
355 // case EMULTIHOP: str = "Multihop attempted (POSIX.1)"; break;
356
357 case ENAMETOOLONG: str = "Filename too long (POSIX.1)"; break;
358
359 case ENETDOWN: str = "Network is down (POSIX.1)"; break;
360
361 case ENETRESET: str = "Connection aborted by network (POSIX.1)"; break;
362
363 case ENETUNREACH: str = "Network unreachable (POSIX.1)"; break;
364
365 case ENFILE: str = "Too many open files in system (POSIX.1); on Linux, this is probably a result of encountering the /proc/sys/fs/file-max limit (see proc(5))."; break;
366
367 case ENOBUFS: str = "No buffer space available (POSIX.1 (XSI STREAMS option))"; break;
368
369
370 case ENODEV: str = "No such device (POSIX.1)"; break;
371
372 case ENOENT: str = "No such file or directory (POSIX.1)"; break;
373
374 // Typically, this error results when a specified
375 // pathname does not exist, or one of the components in
376 // the directory prefix of a pathname does not exist, or
377 // the specified pathname is a dangling symbolic link.
378
379 case ENOEXEC: str = "Exec format error (POSIX.1)"; break;
380
381 // case ENOKEY: str = "Required key not available"; break;
382
383 case ENOLCK: str = "No locks available (POSIX.1)"; break;
384
385 case ENOLINK: str = "Link has been severed (POSIX.1)"; break;
386
387 // case ENOMEDIUM: str = "No medium found"; break;
388
389 case ENOMEM: str = "Not enough space (POSIX.1)"; break;
390
391 case ENOMSG: str = "No message of the desired type (POSIX.1)"; break;
392
393 // case ENONET: str = "Machine is not on the network"; break;
394
395 // case ENOPKG: str = "Package not installed"; break;
396
397 case ENOPROTOOPT: str = "Protocol not available (POSIX.1)"; break;
398
399 case ENOSPC: str = "No space left on device (POSIX.1)"; break;
400
401
402
403 case ENOSYS: str = "Function not implemented (POSIX.1)"; break;
404
405 // case ENOTBLK: str = "Block device required"; break;
406
407 case ENOTCONN: str = "The socket is not connected (POSIX.1)"; break;
408
409 case ENOTDIR: str = "Not a directory (POSIX.1)"; break;
410
411 case ENOTEMPTY: str = "Directory not empty (POSIX.1)"; break;
412
413 case ENOTSOCK: str = "Not a socket (POSIX.1)"; break;
414
415 case ENOTSUP: str = "Operation not supported (POSIX.1)"; break;
416
417 case ENOTTY: str = "Inappropriate I/O control operation (POSIX.1)"; break;
418
419 // case ENOTUNIQ: str = "Name not unique on network"; break;
420
421 case ENXIO: str = "No such device or address (POSIX.1)"; break;
422
423 // case EOPNOTSUPP: str = "Operation not supported on socket (POSIX.1)"; break;
424
425 // (ENOTSUP and EOPNOTSUPP have the same value on Linux,
426 // but according to POSIX.1 these error values should be
427 // distinct.)
428
429 case EOVERFLOW: str = "Value too large to be stored in data type (POSIX.1)"; break;
430
431 case EPERM: str = "Operation not permitted (POSIX.1)"; break;
432
433 // case EPFNOSUPPORT: str = "Protocol family not supported"; break;
434
435 case EPIPE: str = "Broken pipe (POSIX.1)"; break;
436
437 case EPROTO: str = "Protocol error (POSIX.1)"; break;
438
439 case EPROTONOSUPPORT: str = "Protocol not supported (POSIX.1)"; break;
440
441 case EPROTOTYPE: str = "Protocol wrong type for socket (POSIX.1)"; break;
442
443 case ERANGE: str = "Result too large (POSIX.1, C99)"; break;
444
445 // case EREMCHG: str = "Remote address changed"; break;
446
447 // case EREMOTE: str = "Object is remote"; break;
448
449 // case EREMOTEIO: str = "Remote I/O error"; break;
450
451 // case ERESTART: str = "Interrupted system call should be restarted"; break;
452
453 case EROFS: str = "Read-only filesystem (POSIX.1)"; break;
454
455 // case ESHUTDOWN: str = "Cannot send after transport endpoint shutdown"; break;
456
457 case ESPIPE: str = "Invalid seek (POSIX.1)"; break;
458
459 // case ESOCKTNOSUPPORT: str = "Socket type not supported"; break;
460
461 case ESRCH: str = "No such process (POSIX.1)"; break;
462
463 // case ESTALE: str = "Stale file handle (POSIX.1)"; break;
464
465 // This error can occur for NFS and for other
466 // filesystems
467
468 // case ESTRPIPE: str = "Streams pipe error"; break;
469
470
471 // (POSIX.1 says "STREAM ioctl(2) timeout")
472
473 case ETIMEDOUT: str = "Connection timed out (POSIX.1)"; break;
474
475 case ETXTBSY: str = "Text file busy (POSIX.1)"; break;
476
477 // case EUCLEAN: str = "Structure needs cleaning"; break;
478
479 // case EUNATCH: str = "Protocol driver not attached"; break;
480
481 // case EUSERS: str = "Too many users"; break;
482
483 // case EWOULDBLOCK: str = "Operation would block (may be same value as EAGAIN) (POSIX.1)"; break;
484
485 case EXDEV: str = "Improper link (POSIX.1)"; break;
486
487 // case EXFULL: str = "Exchange full"; break;
488 }
489 if ( str ) {
490 fprintf(stderr, "Errno is set to \"%s\"\n", str);
491 }
492 }
493