17 Commits
v1.1 ... master

Author SHA1 Message Date
cf79834870 Pointer qualifier fix 2025-08-05 12:03:50 -04:00
4171c81bfd Bump version number 2025-08-05 11:39:41 -04:00
09b801c150 Fix missing #includes 2025-08-02 21:16:32 -04:00
1807044977 Account for different implementations of realpath(3) 2025-06-05 11:06:49 -04:00
David Baer
da8080a7de Bump version, ensure manpage comes along 2018-09-08 23:15:55 -04:00
David Baer
ddee2d2702 Remove (probably useless) typecast to avoid error message from GCC 2018-09-08 23:14:04 -04:00
David Baer
ec5275ab88 Fix segfault caused by apparently poor understanding of operator precedence 2018-09-08 22:17:32 -04:00
David Baer
3b94588d25 mimetype needs to come first, in opendocument spec 2018-09-08 22:16:22 -04:00
David Baer
321731f51f Install man page 2018-07-17 16:03:09 -04:00
David Baer
dad4175887 Bump minor version 2018-07-17 16:00:25 -04:00
David Baer
95834f00ff Enable line-breaking blockquotes (prefaced by |)
TODO: No indentation on first line
2018-07-17 15:56:58 -04:00
David Baer
44b2187d76 Make scanner portable (works with regular lex) 2018-07-03 09:56:28 -04:00
David Baer
c98e91d810 Add license and installation instructions 2017-12-20 10:18:12 -05:00
David Baer
5c693aa638 Bump version to 1.2 2017-08-09 22:31:37 -04:00
David Baer
517a9d9605 Clang compatibility 2017-08-09 22:27:29 -04:00
David Baer
9a084fe0bd Add some needed checks 2017-08-09 22:27:06 -04:00
David Baer
91e3daf3dc README.md edited online with Bitbucket 2017-01-23 05:18:11 +00:00
15 changed files with 141 additions and 34 deletions

22
COPYING Normal file
View File

@@ -0,0 +1,22 @@
COPYRIGHT (C) 2015-2017 by David Baer <david@amyanddavid.net>
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.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.

1
INSTALL Symbolic link
View File

@@ -0,0 +1 @@
/usr/local/share/automake-1.15/INSTALL

View File

@@ -1,2 +1,3 @@
SUBDIRS = src data
dist_doc_DATA = README.md
dist_man1_MANS = sermon.1

View File

@@ -1,4 +1,4 @@
sermon 1.0
sermon 1.4
==========
This utility converts text markup into various presentable forms.

View File

@@ -2,13 +2,13 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([sermon], [1.1], [david.a.baer@gmail.com])
AC_INIT([sermon], [1.5], [david.a.baer@gmail.com])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_CONFIG_SRCDIR([config.h.in])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CC
AC_PROG_CC_C99
AC_PROG_LEX
AC_PROG_YACC
@@ -24,16 +24,20 @@ AC_CHECK_HEADERS([inttypes.h libintl.h limits.h malloc.h stddef.h stdint.h stdli
AC_C_INLINE
AC_TYPE_INT16_T
AC_TYPE_INT32_T
AC_TYPE_INT64_T
AC_TYPE_INT8_T
AC_TYPE_PID_T
AC_TYPE_SIZE_T
AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
AC_TYPE_UINT64_T
AC_TYPE_UINT8_T
# Checks for library functions.
AC_FUNC_FORK
AC_FUNC_MALLOC
AC_FUNC_REALLOC
AC_CHECK_FUNCS([memset pledge realpath strdup strndup])
AC_CHECK_FUNCS([localtime_r pledge memset realpath strcasecmp strdup strndup strrchr])
AC_CONFIG_FILES([Makefile
data/Makefile

View File

@@ -78,7 +78,7 @@
</xsl:template>
<xsl:template match="ser:p" mode="quote">
<text:p text:style-name="Quotations"><text:span text:style-name="T2"><xsl:apply-templates select="*|text()"/></text:span></text:p>
<text:p text:style-name="Quotations"><xsl:apply-templates select="*|text()"/></text:p>
</xsl:template>
<xsl:template match="ser:quote" mode="body">

View File

@@ -27,6 +27,7 @@
*
*/
#include <ctype.h>
#include <string.h>
#include "queue.h"
#include "stack.h"
@@ -45,6 +46,7 @@ typedef enum {
TOK_STAR,
TOK_REF,
TOK_URL,
TOK_BREAK,
/*
TOK_DASH,
TOK_OPEN_DOUBLE_QUOTE,
@@ -69,35 +71,35 @@ freeTokenizer(utf8iterator* iter) {
utf8FreeIterator(iter);
}
inline int
int
greekChar(uint32_t ch) {
return (((0x370 <= ch) && (ch <= 0x3ff)) ||
((0x1f00 <= ch) && (ch <= 0x1fff)));
}
inline int
int
extendedPunctuation(uint32_t ch) {
return ((0x2000 <= ch) && (ch <= 0x206f));
}
inline int
int
latinChar(uint32_t ch) {
return (ch <= 0xff) || extendedPunctuation(ch);
}
inline int
int
httpAt(Tokenizer tokenizer) {
return ((tolower(tokenizer->txt[tokenizer->byteIndex]) == 'h') &&
(tolower(tokenizer->txt[tokenizer->byteIndex + 1]) == 't') &&
(tolower(tokenizer->txt[tokenizer->byteIndex + 2]) == 't') &&
(tolower(tokenizer->txt[tokenizer->byteIndex + 3]) == 'p') &&
((tokenizer->txt[tokenizer->byteIndex + 4] == ':') &&
(((tokenizer->txt[tokenizer->byteIndex + 4] == ':') &&
(tokenizer->txt[tokenizer->byteIndex + 5] == '/') &&
(tokenizer->txt[tokenizer->byteIndex + 6] == '/')) ||
((tolower(tokenizer->txt[tokenizer->byteIndex + 4]) == 's') &&
(tokenizer->txt[tokenizer->byteIndex + 5] == ':') &&
(tokenizer->txt[tokenizer->byteIndex + 6] == '/') &&
(tokenizer->txt[tokenizer->byteIndex + 7] == '/')));
(tokenizer->txt[tokenizer->byteIndex + 7] == '/'))));
}
static Token
@@ -115,6 +117,11 @@ nextToken(Tokenizer tokenizer) {
result.toktype = TOK_STAR;
result.toktext = NULL;
return result;
} else if (ch == '\n') {
utf8Advance(tokenizer);
result.toktype = TOK_BREAK;
result.toktext = NULL;
return result;
} else if (greekChar(ch)) {
while ((ch != 0) &&
(greekChar(ch) || (ch == ' ') || (ch == ',') || (ch == '.'))) {
@@ -158,7 +165,7 @@ nextToken(Tokenizer tokenizer) {
result.toktext = strndup(tokenizer->txt + startIndex, endIndex - startIndex);
return result;
} else if (latinChar(ch)) {
while ((ch != 0) && latinChar(ch) && (ch != '*')) {
while ((ch != 0) && latinChar(ch) && (ch != '*') && (ch != '\n')) {
utf8Advance(tokenizer);
ch = utf8CharAt(tokenizer);
if (ch == '^') {
@@ -206,6 +213,10 @@ int formatText(const char* txt, FormatElement** dst, CitationRecordQueue* citati
REINIT_QUEUE(formatElementQ);
em = 1;
}
} else if (tok.toktype == TOK_BREAK) {
FormatElement elt = { .elementType = FORMAT_BR, .elementContentLength = 0,
.elementContent = { .textContent = NULL } };
APPEND_QUEUE(FormatElementQueue, formatElementQ, elt);
} else {
FormatElementType t;
FormatElement elt = { .elementContent = { .textContent = tok.toktext } } ;

View File

@@ -34,6 +34,7 @@
typedef enum {
FORMAT_TEXT,
FORMAT_EM,
FORMAT_BR,
FORMAT_STRONG,
FORMAT_CITATION,
FORMAT_GREEK,

View File

@@ -31,6 +31,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include "odt.h"
#include "options.h"
@@ -50,7 +51,7 @@ extractTemplateDocument(const char* const templateFilename, const char* const te
pid_t child_pid;
if ((child_pid = fork()) == 0) {
/* you are the child */
char* const args[] = { "unzip", "-d", tempDir, templateFilename, NULL };
char* const args[] = { "unzip", "-d", (char* const)tempDir, (char* const)templateFilename, NULL };
freopen("/dev/null", "w", stdout);
execvp("unzip", args);
perror("execvp");
@@ -72,9 +73,40 @@ createOutputDocument(const char* const outputFilename, const char* const tempDir
pid_t child_pid;
if ((child_pid = fork()) == 0) {
/* you are the child */
/* In some implementations, realpath will give an error
* if the file does not exist, so we need to run it on
* the output directory, not the as-yet nonexistent
* output filename. That's what all this (below) is for. */
char outputDir[FILENAME_MAX];
char outputBase[FILENAME_MAX];
char outputRealPath[FILENAME_MAX];
char* const args[] = { "zip", "-r", outputRealPath, ".", NULL };
realpath(outputFilename, outputRealPath);
strncpy(outputDir, outputFilename, FILENAME_MAX);
char *ptr = strrchr(outputDir, '/');
if (ptr) {
strncpy(outputBase, ptr + 1, FILENAME_MAX);
*(ptr + 1) = '\0';
} else {
strncpy(outputDir, ".", FILENAME_MAX);
strncpy(outputBase, outputFilename, FILENAME_MAX);
}
if (realpath(outputDir, outputRealPath) == NULL) {
/* hopefully this doesn't run, but it will give good info
* if it does */
perror("realpath");
fprintf(stderr, "outputFilename was \"%s\"\n", outputFilename);
char curdir[FILENAME_MAX];
getcwd(curdir, FILENAME_MAX);
fprintf(stderr, "curdir was \"%s\"\n", curdir);
exit(1);
}
/* Then we append the output filename. */
strncat(outputRealPath, "/", FILENAME_MAX);
strncat(outputRealPath, outputBase, FILENAME_MAX);
char* const args[] = { "zip", "-r", outputRealPath, "mimetype", ".", NULL };
chdir(tempDir);
freopen("/dev/null", "w", stdout);
execvp("zip", args);
@@ -97,7 +129,7 @@ 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 };
char* const args[] = { "rm", "-rf", (char* const)tempDir, NULL };
freopen("/dev/null", "w", stdout);
execvp("rm", args);
perror("execvp");

View File

@@ -69,7 +69,7 @@ typedef struct { \
} N
#define NEW_QUEUE(T, N) \
T N = (T) { .length = 0, .head = NULL, .tail = NULL }
T N = { .length = 0, .head = NULL, .tail = NULL }
/* WARNING: this is probably not what you want -- see DESTROY_QUEUE below */
#define REINIT_QUEUE(N) { \

View File

@@ -38,7 +38,8 @@ typedef struct {
typedef enum {
PARA_DEFAULT,
PARA_BLOCKQUOTE
PARA_BLOCKQUOTE,
PARA_BLOCKPRESERVE
} SermonParagraphType;
typedef struct {

View File

@@ -9,6 +9,7 @@
#define LEXPRINT(x...)
#endif
%}
%option noyywrap
%s HEADER HEADERVAL REFERENCEINTRO REFERENCENAME REFERENCE BLOCK
WHITESPACE [ \t]
ID [A-Za-z_][A-Za-z_0-9]*
@@ -26,10 +27,19 @@ ID [A-Za-z_][A-Za-z_0-9]*
<REFERENCENAME>: { BEGIN(REFERENCE); yylloc.first_column = ++yylloc.last_column; return ':'; }
<REFERENCE>[^}]* { yylloc.first_column = yylloc.last_column + 1; yylloc.last_column = yylloc.first_column + strlen(yytext) + 1; yylval.sval = strdup(yytext); return REFTEXT; }
<REFERENCE>[}] { BEGIN(INITIAL); yylloc.first_column = ++yylloc.last_column; return '}'; }
^[^[{>\n].* { yylloc.first_column = ++yylloc.last_column; yylval.sval = strdup(yytext); LEXPRINT("LINE: %s\n", yytext); return LINE; }
^>{WHITESPACE}*\n { yylloc.first_column = yylloc.last_column = 0; yylloc.first_line++; yylloc.last_line++; return '\n'; }
^>{WHITESPACE}* { BEGIN(BLOCK); yylloc.first_column = yylloc.last_column = 1; return '>'; }
<BLOCK>..* { BEGIN(INITIAL); yylloc.first_column = yylloc.last_column + 1; yylloc.last_column = yylloc.first_column + strlen(yytext) - 1; yylval.sval = strdup(yytext); return LINE; }
^[^[{>|\n].* { yylloc.first_column = ++yylloc.last_column; yylval.sval = strdup(yytext); LEXPRINT("LINE: %s\n", yytext); return LINE; }
^[>|]{WHITESPACE}*\n { yylloc.first_column = yylloc.last_column = 0; yylloc.first_line++; yylloc.last_line++; return '\n'; }
^[>|]{WHITESPACE}* {
BEGIN(BLOCK);
yylloc.first_column = yylloc.last_column = 1;
return yytext[0];
}
<BLOCK>..* {
BEGIN(INITIAL);
yylloc.first_column = yylloc.last_column + 1;
yylloc.last_column = yylloc.first_column + strlen(yytext) - 1;
yylval.sval = strdup(yytext); return LINE;
}
<INITIAL>\n { yylloc.first_column = yylloc.last_column = 0; yylloc.first_line++; yylloc.last_line++; return '\n'; }
<<EOF>> { return 0; }
%%

View File

@@ -37,7 +37,13 @@ char* lineQueueToString(LineQueue* lq) {
FOREACH_QUEUE(LineQueue, *lq, ptr) {
strncat(dest + idx, ptr->data, paraLength - idx);
idx += strlen(ptr->data);
if (ptr->next != NULL) dest[idx++] = ' ';
if (ptr->next != NULL) {
if (dest[idx-1] != '\n')
dest[idx++] = ' ';
} else {
if (dest[idx-1] == '\n')
idx--;
}
free(ptr->data);
}
FOREACH_QUEUE_END
@@ -45,6 +51,14 @@ char* lineQueueToString(LineQueue* lq) {
return dest;
}
char *lineWithBreak(const char *txt) {
size_t l = strlen(txt);
char* s = (char*)malloc(l+2);
strncpy(s, txt, l + 2);
s[l] = '\n';
return s;
}
void yyerror(Sermon*, const char*);
%}
@@ -126,7 +140,7 @@ block:
PARSEPRINT("Parsed paragraph:\n%s\n\n", p.paraText);
free(paraText);
}
| blockquote {
| blockquote_or_preserve {
SermonParagraph p = { .paraType = PARA_BLOCKQUOTE };
char* paraText = lineQueueToString(&lineQ);
FormatElement* paraContent = NULL;
@@ -142,10 +156,17 @@ para:
para LINE '\n' { APPEND_QUEUE(LineQueue, lineQ, $2); }
| LINE '\n' { DESTROY_QUEUE(LineQueue, lineQ); APPEND_QUEUE(LineQueue, lineQ, $1); }
;
blockquote_or_preserve:
blockquote
| blockpreserve
;
blockquote:
blockquote '>' LINE '\n' { APPEND_QUEUE(LineQueue, lineQ, $3); }
| '>' LINE '\n' { DESTROY_QUEUE(LineQueue, lineQ); APPEND_QUEUE(LineQueue, lineQ, $2); }
;
blockpreserve:
blockpreserve '|' LINE '\n' { char* s = lineWithBreak($3); APPEND_QUEUE(LineQueue, lineQ, s); free($3); }
| '|' LINE '\n' { char* s = lineWithBreak($2); DESTROY_QUEUE(LineQueue, lineQ); APPEND_QUEUE(LineQueue, lineQ, s); free($2); }
references:
references reference break
| /* empty */

View File

@@ -83,6 +83,8 @@ formatElementsToXML(
xmlNodePtr em = xmlNewNode(sermon_ns, "em");
formatElementsToXML(sermon_ns, em, a[i].elementContent.nestedContent, a[i].elementContentLength, numReferences, sermonReferencesPtr);
xmlAddChild(parentElement, em);
} else if (a[i].elementType == FORMAT_BR) {
xmlAddChild(parentElement, xmlNewNode(sermon_ns, "br"));
} else if (a[i].elementType == FORMAT_TEXT) {
xmlAddChild(parentElement, xmlNewText(a[i].elementContent.textContent));
} else if (a[i].elementType == FORMAT_GREEK) {

View File

@@ -1,4 +1,5 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libxml/tree.h>
#include <libxslt/xslt.h>