+/* ---- shader composition ---- */
+struct string {
+ char *text;
+ int len;
+};
+
+#define NUM_SHADER_TYPES 5
+static struct string header[NUM_SHADER_TYPES];
+static struct string footer[NUM_SHADER_TYPES];
+
+static void clear_string(struct string *str)
+{
+ free(str->text);
+ str->text = 0;
+ str->len = 0;
+}
+
+static void append_string(struct string *str, const char *s)
+{
+ int len, newlen;
+ char *newstr;
+
+ if(!s || !*s) return;
+
+ len = strlen(s);
+ newlen = str->len + len;
+ if(!(newstr = malloc(newlen + 2))) { /* leave space for a possible newline */
+ fprintf(stderr, "shader composition: failed to append string of size %d\n", len);
+ abort();
+ }
+
+ if(str->text) {
+ memcpy(newstr, str->text, str->len);
+ }
+ memcpy(newstr + str->len, s, len + 1);
+
+ if(s[len - 1] != '\n') {
+ newstr[newlen] = '\n';
+ newstr[newlen + 1] = 0;
+ }
+
+ free(str->text);
+ str->text = newstr;
+ str->len = newlen;
+}
+
+void clear_shader_header(unsigned int type)
+{
+ if(type) {
+ int idx = sdrtypeidx(type);
+ clear_string(&header[idx]);
+ } else {
+ int i;
+ for(i=0; i<NUM_SHADER_TYPES; i++) {
+ clear_string(&header[i]);
+ }
+ }
+}
+
+void clear_shader_footer(unsigned int type)
+{
+ if(type) {
+ int idx = sdrtypeidx(type);
+ clear_string(&footer[idx]);
+ } else {
+ int i;
+ for(i=0; i<NUM_SHADER_TYPES; i++) {
+ clear_string(&footer[i]);
+ }
+ }
+}
+
+void add_shader_header(unsigned int type, const char *s)
+{
+ if(type) {
+ int idx = sdrtypeidx(type);
+ append_string(&header[idx], s);
+ } else {
+ int i;
+ for(i=0; i<NUM_SHADER_TYPES; i++) {
+ append_string(&header[i], s);
+ }
+ }
+}
+
+void add_shader_footer(unsigned int type, const char *s)
+{
+ if(type) {
+ int idx = sdrtypeidx(type);
+ append_string(&footer[idx], s);
+ } else {
+ int i;
+ for(i=0; i<NUM_SHADER_TYPES; i++) {
+ append_string(&footer[i], s);
+ }
+ }
+}
+
+const char *get_shader_header(unsigned int type)
+{
+ int idx = sdrtypeidx(type);
+ return header[idx].text;
+}
+
+const char *get_shader_footer(unsigned int type)
+{
+ int idx = sdrtypeidx(type);
+ return footer[idx].text;
+}
+