From 3b185cb32ef10535ab39ab5deba00db07c500d2a Mon Sep 17 00:00:00 2001 From: David Baer Date: Thu, 30 Jun 2016 14:01:55 -0400 Subject: [PATCH] Enable writing ODT files with correct metadata --- configure.ac | 2 +- data/Makefile.am | 4 +- data/ms_odt.odt | Bin 0 -> 10028 bytes data/odt.odt | Bin 0 -> 24505 bytes src/Makefile.am | 1 + src/main.c | 74 +++++++++++++++++++++- src/meta.h | 55 ++++++++++++++++ src/odt.c | 151 ++++++++++++++++++++++++++++++++++++++++++++ src/odt.h | 45 +++++++++++++ src/odttemplate.c | 46 +++++--------- src/options.c | 59 +++++++++++++++-- src/options.h | 6 ++ src/sermon.h | 1 + src/sermon_parser.y | 1 + src/xml.c | 5 +- src/xml.h | 9 ++- src/xslt.c | 2 +- 17 files changed, 413 insertions(+), 48 deletions(-) create mode 100644 data/ms_odt.odt create mode 100644 data/odt.odt create mode 100644 src/meta.h create mode 100644 src/odt.c create mode 100644 src/odt.h diff --git a/configure.ac b/configure.ac index 2c4b902..03362e3 100644 --- a/configure.ac +++ b/configure.ac @@ -33,7 +33,7 @@ AC_TYPE_UINT8_T # Checks for library functions. AC_FUNC_MALLOC AC_FUNC_REALLOC -AC_CHECK_FUNCS([memset realpath strdup strndup]) +AC_CHECK_FUNCS([memset pledge realpath strdup strndup]) AC_CONFIG_FILES([Makefile data/Makefile diff --git a/data/Makefile.am b/data/Makefile.am index 6c8e803..e3c9689 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -1,2 +1,2 @@ -pkgdata_DATA = *.xsl sermon.dtd -EXTRA_DIST = *.xsl sermon.dtd +pkgdata_DATA = *.xsl ms_odt.odt odt.odt sermon.dtd +EXTRA_DIST = *.xsl ms_odt.odt sermon.dtd diff --git a/data/ms_odt.odt b/data/ms_odt.odt new file mode 100644 index 0000000000000000000000000000000000000000..76646ffa44a1e082e4752b5d8689d1aeec6bbf43 GIT binary patch literal 10028 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 literal 0 HcmV?d00001 diff --git a/data/odt.odt b/data/odt.odt new file mode 100644 index 0000000000000000000000000000000000000000..eca53e9e63768f31e81d14252065ea7846c038f8 GIT binary patch literal 24505 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 z z3%~JbE{`qFGiUqV&so){H_&#etnYJF2)Q{Zgf#nUj?XtgwEwW^Y~~a-KSf{(-)Y7$ z$ZKK9?gq)!MyPby=leWYO(QLp*GkF&uNywOnu^?Fl3;~3F{@EaaZ`9fLiY^xNWg|= zMd!BAL}8jU!!}5y50QOBQmT+Q(2f}<0lr7as0JVIQko6ee#W8lM)GI z?So`_S9vc|cR-n^VckQ8DW(OT?f{;5%$1cNs`%Vrx%Xf!smqu;77tkWErOJ#HaMw4 zkLllpo$yF+HWgGs1TG9rh%(Y%JgUX@;u?XiiA*wg^<}-mVF_Lg;mMKUc8+OBRuJbf zo|_X)A4Y=PU6*)`-lSriv+11;i<#=mw#_Gdn*Xq>73-oXc&0CqT;?zXa{1B^wncqv zc9wlQm`-(D1LXzqYWypvny!EM`d)JEXKsZ#?a9JT%?@VA!AQ1;8n#DAGTSOWr)B{p zwq*dKhrWFnk>lr7W@}4P*>5ZozVLLV4Dh!nsJX+^<15h&TrZW*Ua*`ZbFH45j(UvS zk+s-g6p=dLJi7eQcnkf`goUGk|8HVqVraSG`-2U$3-wyU{6mmvYtLQSU?j zn14OrsS3ZL-c=U=bArqkgYFRD=?*N^`1-5M`G?B* zpS$v_cbMo7UGq2ee`}5Z98P%$Uhv=HKNQD*9^~%$-25iU58d&f(@F1iK^0n6{dM$z zRv-U4|8BCr{2l*}2Kmn;-0i`|ZzBAxME-OB-SJn3O2mI1p&xqWKc}PI8UH*e{U=rO z-{F2#-0zB#|C+hbEP>A4<)3kPwaHNQAI?*>J8v2dEm8iY=+)Nx-`@4E<@e`RX5=pL z7w7Naz3z{ZxV!i8Gd)8Z=9is_|BZNetKsLdy@nEhY&`rO?Y|X~f2O&}L23UkDgPbq zM{n;|ihrg}L1}mE#{W$G&nn5!L^c&D@u!*zl=r=6@*m4QA`&$5VDO #include #include +#include +#include "meta.h" +#include "odt.h" #include "options.h" #include "sermon.h" #include "xml.h" @@ -39,10 +42,31 @@ extern int yyparse(Sermon *); extern FILE* yyin; +char* +constructMetaXml(const char* sermonTitle, const char* timestamp, + const char* sermonAuthor) { + size_t sz = sizeof(META_XML_TEMPLATE) - 13 + 2 * strlen(sermonTitle) + + 3 * strlen(timestamp) + 2 * strlen(sermonAuthor); + char* result = malloc(sz); + if (!result) { + perror("malloc"); + exit(1); + } + snprintf(result, sz, META_XML_TEMPLATE, sermonTitle, timestamp, + sermonAuthor, timestamp, sermonAuthor, sermonTitle, timestamp); + + return result; +} + int main(int argc, char* argv[]) { Sermon sermon; xmlDocPtr document, transformed; int i = 0, block = 0, normal = 0; +#ifdef HAVE_PLEDGE + if (-1 == pledge("stdio rpath wpath tmppath proc exec", NULL)) { + err(1, "pledge"); + } +#endif /* !def(HAVE_PLEDGE) */ InitOptions(argc, (const char**)argv); InitSermon(&sermon); @@ -53,17 +77,61 @@ int main(int argc, char* argv[]) { } yyparse(&sermon); +#if 0 +#ifdef HAVE_PLEDGE + if (-1 == pledge("stdio", NULL)) { + err(1, "pledge"); + } +#endif /* !def(HAVE_PLEDGE) */ +#endif + document = sermonToXmlDoc(&sermon); - transformed = applyStyleSheet(document, options.styleSheetName); - printXML(transformed); + if (strcmp(options.styleSheetName, "none") != 0) { + transformed = applyStyleSheet(document, options.styleSheetName); + } else { + transformed = document; + } + if (strcmp(options.outputFileName, "-") == 0) { + printXML(transformed); + } else { + if ((strcmp(options.styleSheetName, "odt") == 0) || + (strcmp(options.styleSheetName, "ms_odt") == 0)) { + ODTFileEntry entry[2]; + int sz; + xmlChar* doc; + char templateFileName[FILENAME_MAX]; + char timestamp[20]; + time_t tm; + + /* get timestamp */ + time(&tm); + strftime(timestamp, 20, "%Y-%m-%dT%H:%M:%S", localtime(&tm)); + + xmlDocDumpMemoryEnc(transformed, &doc, &sz, "utf-8"); + entry[0].path = "content.xml"; + entry[0].content = doc; + entry[1].path = "meta.xml"; + entry[1].content = constructMetaXml(sermon.sermonTitle, timestamp, sermon.sermonAuthor ? sermon.sermonAuthor : options.authorName); + snprintf(templateFileName, FILENAME_MAX, "%s.odt", options.styleSheetName); + constructODT(options.outputFileName, OptionsDataFile(templateFileName), 2, entry); + free(doc); + free(entry[1].content); + } else { + xmlSaveFileEnc(options.outputFileName, transformed, "utf-8"); + } + } /* clean up, clean up, everybody, everywhere! */ + VERBOSE_PRINTF("Cleaning up\n"); xmlFreeDoc(document); - xmlFreeDoc(transformed); + if (strcmp(options.styleSheetName, "none") != 0) { + xmlFreeDoc(transformed); + } FreeSermon(&sermon); if (strcmp(options.inputFileName, "-") != 0) { fclose(yyin); } FreeOptions(); + VERBOSE_PRINTF("Cleanup done\n"); } diff --git a/src/meta.h b/src/meta.h new file mode 100644 index 0000000..3e83de6 --- /dev/null +++ b/src/meta.h @@ -0,0 +1,55 @@ +/* + * meta.h + * Copyright © 2016 David A. Baer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the organization nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY David A. Baer ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL David A. Baer BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _META_H +#define _META_H + +#define META_XML_TEMPLATE \ + "\n" \ + "\n" \ + " \n" \ + " OpenOffice.org/3.1$Unix OpenOffice.org_project/310m19$Build-9420\n" \ + " %s\n" \ + " %s\n" \ + " en-US\n" \ + " 4\n" \ + " PT00H11M59S\n" \ + " %s\n" \ + " %s\n" \ + " %s\n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + "\n" + +#endif /* !def(_META_H) */ diff --git a/src/odt.c b/src/odt.c new file mode 100644 index 0000000..746d8de --- /dev/null +++ b/src/odt.c @@ -0,0 +1,151 @@ +/* + * odt.c + * Copyright © 2016 David A. Baer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the organization nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY David A. Baer ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL David A. Baer BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include "odt.h" +#include "options.h" + +void +saveEntry(const char* rootPath, const ODTFileEntry entry) { + char* fullpath = malloc(FILENAME_MAX); + FILE* ptr; + snprintf(fullpath, FILENAME_MAX, "%s/%s", rootPath, entry.path); + ptr = fopen(fullpath, "w"); + fwrite(entry.content, 1, strlen(entry.content), ptr); + fclose(ptr); + free(fullpath); +} + +static void +extractTemplateDocument(const char* const templateFilename, const char* const tempDir) { + pid_t child_pid; + if ((child_pid = fork()) == 0) { + /* you are the child */ + char* const args[] = { "unzip", "-d", tempDir, templateFilename, NULL }; + freopen("/dev/null", "w", stdout); + execvp("unzip", args); + perror("execvp"); + exit(1); + } else { + /* you are the parent */ + int status; + + waitpid(child_pid, &status, 0); + if (status != 0) { + fprintf(stderr, "unzip exited with status %d.\n", status); + exit(1); + } + } +} + +static void +createOutputDocument(const char* const outputFilename, const char* const tempDir) { + pid_t child_pid; + if ((child_pid = fork()) == 0) { + /* you are the child */ + char outputRealPath[FILENAME_MAX]; + char* const args[] = { "zip", "-r", outputRealPath, ".", NULL }; + realpath(outputFilename, outputRealPath); + chdir(tempDir); + freopen("/dev/null", "w", stdout); + execvp("zip", args); + perror("execvp"); + exit(1); + } else { + /* you are the parent */ + int status; + + waitpid(child_pid, &status, 0); + if (status != 0) { + fprintf(stderr, "zip exited with status %d.\n", status); + exit(1); + } + } +} + +static void +removeDirectory(const char* const tempDir) { + pid_t child_pid; + if ((child_pid = fork()) == 0) { + /* you are the child */ + char* const args[] = { "rm", "-rf", tempDir, NULL }; + freopen("/dev/null", "w", stdout); + execvp("rm", args); + perror("execvp"); + exit(1); + } else { + /* you are the parent */ + int status; + + waitpid(child_pid, &status, 0); + if (status != 0) { + fprintf(stderr, "rm exited with status %d.\n", status); + exit(1); + } + } +} + +int +constructODT(const char* const outputFilename, const char* const templateFilename, + unsigned int numEntries, const ODTFileEntry* entries) { + char tempDir[23]; + int i; + + /* create temporary workspace */ + strlcpy(tempDir, "/tmp/sermon.XXXXXXXXXX", sizeof(tempDir)); + if (mkdtemp(tempDir) == NULL) { + perror("mkdtemp"); + exit(1); + } + VERBOSE_PRINTF("Making temporary directory %s\n", tempDir); + + /* extract template document to workspace */ + VERBOSE_PRINTF("Extracting template %s to temporary directory\n", templateFilename); + extractTemplateDocument(templateFilename, tempDir); + + /* replace/add entries */ + for (i = 0; i < numEntries; i++) { + VERBOSE_PRINTF("Adding %s\n", entries[i].path); + saveEntry(tempDir, entries[i]); + } + + /* create output file */ + VERBOSE_PRINTF("Creating output file %s\n", outputFilename); + createOutputDocument(outputFilename, tempDir); + + /* clean up */ + VERBOSE_PRINTF("Removing %s\n", tempDir); + removeDirectory(tempDir); + + VERBOSE_PRINTF("ODT output successfully created\n"); + return 0; +} diff --git a/src/odt.h b/src/odt.h new file mode 100644 index 0000000..3488121 --- /dev/null +++ b/src/odt.h @@ -0,0 +1,45 @@ +/* + * odt.h + * Copyright © 2016 David A. Baer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the organization nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY David A. Baer ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL David A. Baer BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _ODT_H +#define _ODT_H 1 + +typedef struct { + char* path; + char* content; +} ODTFileEntry; + +int +constructODT( + const char* const outputFilename, const char* const templateFilename, + unsigned int numEntries, const ODTFileEntry* entries +); + + +#endif /* !def(_ODT_H) */ diff --git a/src/odttemplate.c b/src/odttemplate.c index db0d0c4..595b48f 100644 --- a/src/odttemplate.c +++ b/src/odttemplate.c @@ -35,7 +35,8 @@ int odttemplate(const char* template_fn, const char* output_fn, struct dict* substitutions_dict) { - struct archive* arch_in = archive_read_new(); + struct archive* arch_in = archive_read_new(), + *arch_out = archive_write_new(); struct archive_entry* ent; struct dict_iter* i; char ftype; @@ -43,41 +44,24 @@ int odttemplate(const char* template_fn, const char* output_fn, struct dict* sub if (archive_read_support_format_zip(arch_in) != ARCHIVE_OK) { return ODTTEMPLATE_FATAL; } + if (archive_write_set_format_zip(arch_out) != ARCHIVE_OK) { + return ODTTEMPLATE_FATAL; + } + if (archive_write_zip_set_compression_deflate(arch_out) != ARCHIVE_OK) { + return ODTTEMPLATE_FATAL; + } if (archive_read_open_filename(arch_in, template_fn, ODTTEMPLATE_BLOCK_SIZE) != ARCHIVE_OK) { return ODTTEMPLATE_FATAL; } + if (archive_write_open_filename(arch_out, output_fn) != ARCHIVE_OK) { + return ODTTEMPLATE_FATAL; + } while (archive_read_next_header(arch_in, &ent) != ARCHIVE_EOF) { - switch (archive_entry_filetype(ent)) { - case AE_IFREG: - ftype = 'F'; - break; - case AE_IFLNK: - ftype = 'L'; - break; - case AE_IFSOCK: - ftype = 'S'; - break; - case AE_IFCHR: - ftype = 'C'; - break; - case AE_IFBLK: - ftype = 'B'; - break; - case AE_IFDIR: - ftype = 'D'; - break; - case AE_IFIFO: - ftype = 'P'; - break; - default: - ftype = '?'; + if ((archive_entry_filetype(ent) == AE_IFREG) && + (dict_find(substitutions_dict, archive_entry_pathname(ent), NULL) + == DICT_OK)) { + } else { } - fn = archive_entry_pathname(ent); - printf("%s [%c]", fn, ftype); - if (substitutions_dict && (dict_find(substitutions_dict, fn, NULL) == DICT_OK)) { - printf(" - in dictionary, skip"); - } - printf("\n"); archive_read_data_skip(arch_in); } archive_read_close(arch_in); diff --git a/src/options.c b/src/options.c index 9997529..e578fbb 100644 --- a/src/options.c +++ b/src/options.c @@ -36,7 +36,9 @@ #include "options.h" Options options = { - .progname = NULL, .datadir = DATADIR, .styleSheetName = "html5" + .progname = NULL, .datadir = DATADIR, .styleSheetName = NULL, + .outputFileName = "-", .placeName = "Highlands Presbyterian Church", + .authorName = "David A. Baer", .verbose = 0 }; char* @@ -70,10 +72,14 @@ datadir(const char* progname) { } static void usage(const char* progname) { - fprintf(stderr, "Usage: %s [-h] [-s STYLESHEET] FILE\n" + fprintf(stderr, "Usage: %s [options] FILE\n" "\n" - " -h Display help message\n" - " -s STYLESHEET Apply stylesheet (default \"html5\")\n" + "Options:\n" + " -h|--help Display this help message\n" + " -a|--author AUTHOR Author name\n" + " -o|--output OUTPUT Output file name\n" + " -s|--stylesheet STYLESHEET Apply stylesheet (default \"html5\")\n" + " -v|--verbose Verbose output\n" "\n" " FILE sermon file to scan (\"-\" for stdin)\n", progname); } @@ -83,13 +89,35 @@ void InitOptions(int argc, const char* argv[]) { options.progname = argv[0]; options.datadir = datadir(options.progname); while (++i < argc) { - if (strcmp(argv[i], "-h") == 0) { usage(options.progname); exit(0); } - else if (strcmp(argv[i], "-") == 0) { + if ((strcmp(argv[i], "-h") == 0) || + (strcmp(argv[i], "--help") == 0)) { + usage(options.progname); + exit(0); + } else if (strcmp(argv[i], "-") == 0) { options.inputFileName = argv[i]; - } else if (strcmp(argv[i], "-s") == 0) { + } else if ((strcmp(argv[i], "-s") == 0) || + (strcmp(argv[i], "--stylesheet") == 0)) { options.styleSheetName = argv[++i]; } else if (strncmp(argv[i], "-s", 2) == 0) { options.styleSheetName = argv[i] + 2; + } else if ((strcmp(argv[i], "-o") == 0) || + (strcmp(argv[i], "--output") == 0)) { + options.outputFileName = argv[++i]; + } else if (strncmp(argv[i], "-o", 2) == 0) { + options.outputFileName = argv[i] + 2; + } else if ((strcmp(argv[i], "-a") == 0) || + (strcmp(argv[i], "--author") == 0)) { + options.authorName = argv[++i]; + } else if (strncmp(argv[i], "-a", 2) == 0) { + options.authorName = argv[i] + 2; + } else if ((strcmp(argv[i], "-p") == 0) || + (strcmp(argv[i], "--place") == 0)) { + options.placeName = argv[++i]; + } else if (strncmp(argv[i], "-p", 2) == 0) { + options.placeName = argv[i] + 2; + } else if ((strcmp(argv[i], "-v") == 0) || + (strcmp(argv[i], "--verbose") == 0)) { + options.verbose = 1; } else if (argv[i][0] == '-') { fprintf(stderr, "Unknown option: %s\n", argv[i]); } else { @@ -102,6 +130,23 @@ void InitOptions(int argc, const char* argv[]) { usage(options.progname); exit(1); } + + /* set stylesheet based on output file name */ + if (!options.styleSheetName && options.outputFileName) { + char* outputExten = strrchr(options.outputFileName, '.'); + if (outputExten) { + if (strcasecmp(outputExten, ".odt") == 0) { + options.styleSheetName = "odt"; + } else if (strcasecmp(outputExten, ".html") == 0) { + options.styleSheetName = "html5"; + } + } + } + + /* default stylesheet is html5 */ + if (!options.styleSheetName) { + options.styleSheetName = "html5"; + } } char* diff --git a/src/options.h b/src/options.h index 9a8ee51..e9814ea 100644 --- a/src/options.h +++ b/src/options.h @@ -34,7 +34,11 @@ typedef struct { const char* progname; char* datadir; const char* inputFileName; + const char* outputFileName; const char* styleSheetName; + const char* authorName; + const char* placeName; + int verbose; } Options; extern Options options; @@ -43,4 +47,6 @@ void InitOptions(int argc, const char* argv[]); char* OptionsDataFile(const char* fname); void FreeOptions(); +#define VERBOSE_PRINTF(x...) { if (options.verbose) printf(x); } + #endif /* !def _OPTIONS_H */ diff --git a/src/sermon.h b/src/sermon.h index e33ba4e..a70d400 100644 --- a/src/sermon.h +++ b/src/sermon.h @@ -58,6 +58,7 @@ typedef struct { char* sermonAuthor; char* sermonDate; char* sermonOccasion; + char* sermonPlace; char* sermonText; int numParagraphs; diff --git a/src/sermon_parser.y b/src/sermon_parser.y index 87baed6..bf95352 100644 --- a/src/sermon_parser.y +++ b/src/sermon_parser.y @@ -105,6 +105,7 @@ header: else if (strcmp($2, "text") == 0) { sermon->sermonText = $4; } else if (strcmp($2, "occasion") == 0) { sermon->sermonOccasion = $4; } else if (strcmp($2, "date") == 0) { sermon->sermonDate = $4; } + else if (strcmp($2, "place") == 0) { sermon->sermonPlace = $4; } else { free($4); } free($2); } diff --git a/src/xml.c b/src/xml.c index d93cfb9..e0dcce3 100644 --- a/src/xml.c +++ b/src/xml.c @@ -1,5 +1,6 @@ #include #include +#include "options.h" #include "sermon.h" static xmlNsPtr srmNs = NULL; @@ -18,8 +19,9 @@ xmlNodePtr sermonHeader(xmlNsPtr sermon_ns, const Sermon* srm) { xmlNodePtr header = xmlNewNode(sermon_ns, "header"); appendHeaderNode(sermon_ns, header, "title", srm->sermonTitle); - appendHeaderNode(sermon_ns, header, "author", srm->sermonAuthor); + appendHeaderNode(sermon_ns, header, "author", srm->sermonAuthor ? srm->sermonAuthor : options.authorName); appendHeaderNode(sermon_ns, header, "occasion", srm->sermonOccasion); + appendHeaderNode(sermon_ns, header, "place", srm->sermonPlace ? srm->sermonPlace : options.placeName); appendHeaderNode(sermon_ns, header, "date", srm->sermonDate); appendHeaderNode(sermon_ns, header, "text", srm->sermonText); return header; @@ -166,3 +168,4 @@ printXML(xmlDocPtr document) { xmlOutputBufferPtr output = xmlOutputBufferCreateFd(1, encoding); xmlSaveFileTo(output, document, "utf-8"); } + diff --git a/src/xml.h b/src/xml.h index 6a74710..7cd3588 100644 --- a/src/xml.h +++ b/src/xml.h @@ -1,7 +1,12 @@ #ifndef _XML_H #define _XML_H -xmlDocPtr sermonToXmlDoc(const Sermon*); -void printXML(xmlDocPtr); +#include + +xmlDocPtr +sermonToXmlDoc(const Sermon*); + +void +printXML(xmlDocPtr); #endif /* !def _XML_H */ diff --git a/src/xslt.c b/src/xslt.c index 511ebd8..531e89d 100644 --- a/src/xslt.c +++ b/src/xslt.c @@ -15,7 +15,7 @@ applyStyleSheet(xmlDocPtr document, const char* styleSheetName) { snprintf(t, l, "%s.xsl", styleSheetName); styleSheetFileName = OptionsDataFile(t); free(t); - fprintf(stderr, "Loading stylesheet %s ...\n", styleSheetFileName); + /*fprintf(stderr, "Loading stylesheet %s ...\n", styleSheetFileName);*/ xmlSubstituteEntitiesDefault(1); xmlLoadExtDtdDefaultValue = 1;