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