/* Given a GString containing MMD source, and optional base directory, substitute transclusion references in the source Pass the path to the current folder if available -- should be a full path. Keep track of what we're parsing to prevent recursion using stack. */ void transclude_source(GString *source, char *basedir, char *stack, int output_format, GString *manifest) { char *base = NULL; char *path = NULL; char *start; char *stop; char *temp; int curchar; size_t pos; char real[1000]; FILE *input; long offset; if (basedir == NULL) { base = strdup(""); } else { base = strdup(basedir); } GString *folder = NULL; GString *filename = NULL; GString *filebuffer = NULL; GString *stackstring = NULL; path = strdup(base); /* Look for override folder inside document */ if (has_metadata(source->str, 0x000000)) { char *meta = extract_metadata_value(source->str, 0x000000, "transcludebase"); if (meta != NULL) path = path_from_dir_base(base, meta); } if (path == NULL) { /* We have nowhere to look, so nothing to do */ free(path); free(base); return; } folder = g_string_new(path); /* Ensure that folder ends in "/" */ /* TODO: adjust for windows */ if (!(folder->str[strlen(folder->str)-1] == '/') ) { g_string_append_c(folder, '/'); } /* fprintf(stderr, "Transclude using '%s'\n", folder->str); */ /* Iterate through {{foo.txt}} and substitute contents of file without metadata */ start = strstr(source->str,"{{"); while (start != NULL) { stop = strstr(start,"}}"); if (stop == NULL) break; /* Check that we found something reasonable -- we cap at 1000 characters */ if (stop - start < 1000) { strncpy(real,start+2,stop-start-2); real[stop-start-2] = '\0'; if (is_separator(real[0])) { filename = g_string_new(real); } else { filename = g_string_new(folder->str); g_string_append_printf(filename, "%s",real); } if (strcmp(filename->str,"./TOC") == 0) { pos = stop - source->str; start = strstr(source->str + pos,"{{"); g_string_free(filename, true); continue; } /* Adjust for wildcard extensions */ /* But not if output_format == 0 */ if (output_format && strncmp(&filename->str[strlen(filename->str) - 2],".*",2) == 0) { g_string_erase(filename, strlen(filename->str) - 2, 2); if (output_format == TEXT_FORMAT) { g_string_append(filename,".txt"); } else if (output_format == HTML_FORMAT) { g_string_append(filename,".html"); } else if (output_format == LATEX_FORMAT) { g_string_append(filename,".tex"); } else if (output_format == BEAMER_FORMAT) { g_string_append(filename,".tex"); } else if (output_format == MEMOIR_FORMAT) { g_string_append(filename,".tex"); } else if (output_format == ODF_FORMAT) { g_string_append(filename,".fodt"); } else if (output_format == OPML_FORMAT) { g_string_append(filename,".opml"); } else if (output_format == LYX_FORMAT) { g_string_append(filename,".lyx"); } else if (output_format == RTF_FORMAT) { g_string_append(filename,".rtf"); } else { /* default extension -- in this case we only have 1 */ g_string_append(filename,".txt"); } } pos = stop - source->str; /* Add to the manifest (if not already included) */ if (manifest != NULL) { temp = strstr(manifest->str,filename->str); offset = temp - manifest->str; if ((temp != NULL) && ((temp == manifest->str) || ((manifest->str)[offset - 1] == '\n')) && (temp[strlen(filename->str)] == '\n') ){ /* Already on manifest, so don't add again */ } else { g_string_append_printf(manifest,"%s\n",filename->str); } } /* Don't reparse ourselves */ if (stack != NULL) { temp = strstr(stack,filename->str); if ((temp != NULL) && (temp[strlen(filename->str)] == '\n')){ start = strstr(source->str + pos,"{{"); g_string_free(filename, true); continue; } } /* Read file */ #if defined(__WIN32) int wchars_num = MultiByteToWideChar(CP_UTF8, 0, filename->str, -1, NULL, 0); wchar_t wstr[wchars_num]; MultiByteToWideChar(CP_UTF8, 0, filename->str, -1, wstr, wchars_num); if ((input = _wfopen(wstr, L"r")) != NULL ) { #else if ((input = fopen(filename->str, "r")) != NULL ) { #endif filebuffer = g_string_new(""); while ((curchar = fgetc(input)) != EOF) g_string_append_c(filebuffer, curchar); fclose(input); pos = start - source->str; g_string_erase(source, pos, 2 + stop - start); /* Update stack list */ stackstring = g_string_new(stack); g_string_append_printf(stackstring,"%s\n",filename->str); /* Recursively transclude files */ /* We want to reset the base directory if we enter a subdirectory */ char * new_dir; char * file_only; split_path_file(&new_dir, &file_only, filename->str); /* transclude_source(filebuffer, folder->str, stackstring->str, output_format, manifest); */ transclude_source(filebuffer, new_dir, stackstring->str, output_format, manifest); free(new_dir); free(file_only); temp = source_without_metadata(filebuffer->str, 0x000000); g_string_insert(source, pos, temp); pos += strlen(temp); g_string_free(filebuffer, true); g_string_free(stackstring, true); } else { /* fprintf(stderr, "error opening file: %s\n", filename->str); */ } g_string_free(filename, true); } else { /* Our "match" was > 1000 characters long */ pos = stop - source->str; } start = strstr(source->str + pos,"{{"); } g_string_free(folder, true); free(path); free(base); } /* Allow for a footer to specify files to be appended to the end of the text, and then transcluded. Useful for appending a list of footnotes, citations, abbreviations, etc. to each separate file, but not including multiple copies when processing the master file. */ void append_mmd_footer(GString *source) { /* Look for mmd_footer metadata */ if (has_metadata(source->str, 0x000000)) { char *meta = extract_metadata_value(source->str, 0x000000, "mmdfooter"); if (meta != NULL) g_string_append_printf(source, "\n\n{{%s}}\n", meta); } } void prepend_mmd_header(GString *source) { /* Same thing, but to be inserted after metadata and before content */ if (has_metadata(source->str, 0x000000)) { char *meta = extract_metadata_value(source->str, 0x000000, "mmdheader"); if (meta != NULL) { char *content = strstr(source->str, "\n\n"); if (content != NULL) { size_t pos = content - source->str; g_string_insert_printf(source, pos, "\n\n{{%s}}", meta); } else { g_string_append_printf(source, "\n\n{{%s}}\n", meta); } } } }
/* Given a GString containing MMD source, and optional base directory, substitute transclusion references in the source Pass the path to the current folder if available -- should be a full path. Keep track of what we're parsing to prevent recursion using stack. */ void transclude_source(GString *source, char *basedir, char *stack, int output_format) { char *base = NULL; char *path = NULL; char *start; char *stop; char *temp; int curchar; size_t pos; char real[1000]; FILE *input; if (basedir == NULL) { base = strdup(""); } else { base = strdup(basedir); } GString *folder = NULL; GString *filename = NULL; GString *filebuffer = NULL; GString *stackstring = NULL; path = strdup(base); /* Look for override folder inside document */ if (has_metadata(source->str, 0x000000)) { char *meta = extract_metadata_value(source->str, 0x000000, "transcludebase"); if (meta != NULL) path = path_from_dir_base(base, meta); } if (path == NULL) { /* We have nowhere to look, so nothing to do */ free(path); free(base); return; } folder = g_string_new(path); /* Ensure that folder ends in "/" */ /* TODO: adjust for windows */ if (!(folder->str[strlen(folder->str)-1] == '/') ) { g_string_append_c(folder, '/'); } //fprintf(stderr, "Transclude using '%s'\n", folder->str); /* Iterate through {{foo.txt}} and substitute contents of file without metadata */ start = strstr(source->str,"{{"); while (start != NULL) { stop = strstr(start,"}}"); if (stop == NULL) break; // TODO: Need to check that we found something reasonable strncpy(real,start+2,stop-start-2); real[stop-start-2] = '\0'; filename = g_string_new(folder->str); g_string_append_printf(filename, "%s",real); /* Adjust for wildcard extensions */ /* But not if output_format == 0 */ if (output_format && strncmp(&filename->str[strlen(filename->str) - 2],".*",2) == 0) { g_string_erase(filename, strlen(filename->str) - 2, 2); if (output_format == TEXT_FORMAT) { g_string_append(filename,".txt"); } else if (output_format == HTML_FORMAT) { g_string_append(filename,".html"); } else if (output_format == LATEX_FORMAT) { g_string_append(filename,".tex"); } else if (output_format == BEAMER_FORMAT) { g_string_append(filename,".tex"); } else if (output_format == MEMOIR_FORMAT) { g_string_append(filename,".tex"); } else if (output_format == ODF_FORMAT) { g_string_append(filename,".fodt"); } else if (output_format == OPML_FORMAT) { g_string_append(filename,".opml"); } else if (output_format == LYX_FORMAT) { g_string_append(filename,".lyx"); } else if (output_format == RTF_FORMAT) { g_string_append(filename,".rtf"); } else { /* default extension -- in this case we only have 1 */ g_string_append(filename,".txt"); } } pos = stop - source->str; /* Don't reparse ourselves */ if (stack != NULL) { temp = strstr(stack,filename->str); if ((temp != NULL) && (temp[strlen(filename->str)] == '\n')){ start = strstr(source->str + pos,"{{"); g_string_free(filename, true); continue; } } /* Read file */ if ((input = fopen(filename->str, "r")) != NULL ) { filebuffer = g_string_new(""); while ((curchar = fgetc(input)) != EOF) g_string_append_c(filebuffer, curchar); fclose(input); pos = start - source->str; g_string_erase(source, pos, 2 + stop - start); /* Update stack list */ stackstring = g_string_new(stack); g_string_append_printf(stackstring,"%s\n",filename->str); /* Recursively transclude files */ transclude_source(filebuffer, folder->str, stackstring->str, output_format); temp = source_without_metadata(filebuffer->str, 0x000000); g_string_insert(source, pos, temp); pos += strlen(temp); g_string_free(filebuffer, true); g_string_free(stackstring, true); } else { /* fprintf(stderr, "error opening file: %s\n", filename->str); */ } start = strstr(source->str + pos,"{{"); g_string_free(filename, true); } g_string_free(folder, true); free(path); free(base); }