summaryrefslogtreecommitdiff
path: root/getdefs/gdemit.c
diff options
context:
space:
mode:
Diffstat (limited to 'getdefs/gdemit.c')
-rw-r--r--getdefs/gdemit.c602
1 files changed, 602 insertions, 0 deletions
diff --git a/getdefs/gdemit.c b/getdefs/gdemit.c
new file mode 100644
index 0000000..f6bfbbf
--- /dev/null
+++ b/getdefs/gdemit.c
@@ -0,0 +1,602 @@
+/**
+ * \file gdemit.c
+ *
+ * getdefs Copyright (c) 1999-2012 by Bruce Korb - all rights reserved
+ *
+ * Author: Bruce Korb <bkorb@gnu.org>
+ * Time-stamp: "2012-03-31 13:39:34 bkorb"
+ *
+ * This file is part of AutoGen.
+ * AutoGen copyright (c) 1992-2012 by Bruce Korb - all rights reserved
+ *
+ * AutoGen is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* = = = START-STATIC-FORWARD = = = */
+static void
+compress_def(char * pz);
+
+static char *
+list_attrib(char * pzText, char * pzOut);
+
+static char *
+emit_quote(char ** ppzText, char * pzOut);
+
+static void
+next_def_entry(char ** txt_pp, char const ** def_pp);
+
+static void
+emit_attribute(char const ** def_pp, char ** out_pp);
+
+static char*
+emit_subblock(char const * pzDefList, char * pzText, char * pzOut);
+
+static char*
+subblock_str(char ** ppzText, uint_t sepChar, char * pzOut);
+/* = = = END-STATIC-FORWARD = = = */
+
+/*
+ * compress_def
+ *
+ * Compress the definition text. Each input line has some prefix
+ * stuff to ensure it is a comment as seen by the normal processor
+ * of the file. In "C", the entire block is surrounded by the
+ * '/'-'*' and '*'-'/' pairs. In shell, every line would start
+ * with a hash character ('#'). Etc. To make life easy, we require
+ * that every line be prefixed with something that matches the
+ * pattern:
+ *
+ * "^[^*]*\*+"
+ *
+ * and any line that does not is ignored. So, here we strip off
+ * that prefix before we go ahead and try to parse it.
+ */
+static void
+compress_def(char * pz)
+{
+ char * pzStrt = pz;
+ char * pzDest = pz;
+ char * pzSrc = pz;
+ int nlCt = 0;
+
+ /*
+ * Search until we find a line that contains an asterisk
+ * and is followed by something other than whitespace.
+ */
+ nlCt = 0;
+
+skip_leading_space:
+ while (isspace( *pzSrc )) {
+ if (*(pzSrc++) == '\n') {
+ nlCt++;
+ for (;;) {
+ switch (*(pzSrc++)) {
+ case '*':
+ while (*pzSrc == '*') pzSrc++;
+ goto skip_leading_space;
+
+ case NUL:
+ *pzStrt = NUL;
+ return;
+
+ case '\n':
+ nlCt++;
+ }
+ }
+ }
+ }
+
+ if (*pzSrc == NUL) {
+ *pzStrt = NUL;
+ return;
+ }
+
+ /*
+ * FOR as long as we still have more text, ...
+ */
+ for (;;) {
+ /*
+ * IF we passed over one or more newlines while looking for
+ * an asterisk, then insert one extra newline into the output
+ */
+ if (nlCt > 0) {
+ *pzDest++ = '\n';
+ nlCt = 0;
+ }
+
+ /*
+ * FOR all the data on the current input line, ...
+ */
+ for (;;) {
+ /*
+ * Move the source to destination until we find
+ * either a new-line or a NUL.
+ */
+ switch (*pzDest++ = *pzSrc++) {
+ case '\n':
+ if (*pzSrc != NUL)
+ goto lineDone;
+
+ case NUL:
+ pzDest--;
+ goto compressDone;
+
+ default:
+ ;
+ }
+ } lineDone:;
+
+ /*
+ * Trim trailing white space off the end of the line.
+ */
+ if ((pzDest[-2] == ' ') || (pzDest[-2] == '\t')) {
+ do {
+ pzDest--;
+ } while ((pzDest[-2] == ' ') || (pzDest[-2] == '\t'));
+ pzDest[-1] = '\n';
+ }
+
+ /*
+ * We found a new-line. Skip forward to an asterisk.
+ */
+ foundNewline:
+ while (*pzSrc != '*') {
+ if (*pzSrc == NUL)
+ goto compressDone;
+ if (*pzSrc == '\n')
+ nlCt++;
+ pzSrc++;
+ }
+
+ /*
+ * Skip over the asterisk we found and all the ones that follow
+ */
+ while (*pzSrc == '*') pzSrc++;
+ while (isspace( *pzSrc )) {
+ /*
+ * IF we stumble into another newline,
+ * THEN we go back to look for an asterisk.
+ */
+ if (*pzSrc == '\n')
+ goto foundNewline;
+ pzSrc++;
+ }
+ } compressDone:;
+
+ /*
+ * Trim off all the trailing white space, including newlines
+ */
+ while ((pzDest > pzStrt) && isspace( pzDest[-1] )) pzDest--;
+ *pzDest = NUL;
+}
+
+
+/*
+ * emitDefinition
+ */
+LOCAL char*
+emitDefinition(char* pzDef, char* pzOut)
+{
+ char sep_char;
+ char zEntryName[ MAXNAMELEN ];
+
+ /*
+ * Indent attribute definitions four spaces
+ */
+ {
+ char* p = zEntryName;
+ *pzOut++ = ' '; *pzOut++ = ' '; *pzOut++ = ' '; *pzOut++ = ' ';
+
+ while (AG_NAME_CHAR(*pzDef))
+ *p++ = *pzOut++ = *pzDef++;
+
+ if (p >= zEntryName + sizeof(zEntryName))
+ die("names are constrained to %d bytes\n", MAXNAMELEN);
+
+ *p = NUL;
+ }
+
+ /*
+ * Strip the prefixes from all the definition lines
+ * (viz., the "^.*\*" text, except that it is a shortest match
+ * instead of longest match). Skip the ':' before starting.
+ */
+ compress_def(++pzDef);
+
+ if (HAVE_OPT( SUBBLOCK )) {
+ int ct = STACKCT_OPT( SUBBLOCK );
+ char const ** ppz = STACKLST_OPT( SUBBLOCK );
+
+ do {
+ char const * pz = *ppz++;
+ if (strcmp(pz, zEntryName) == 0)
+ return emit_subblock(pz, pzDef, pzOut);
+ } while (--ct > 0);
+ }
+
+ if (HAVE_OPT( LISTATTR )) {
+ int ct = STACKCT_OPT( LISTATTR );
+ char const ** ppz = STACKLST_OPT( LISTATTR );
+
+ do {
+ if (strcmp(*ppz++, zEntryName) == 0)
+ return list_attrib(pzDef, pzOut);
+ } while (--ct > 0);
+ }
+
+ if (isspace(*pzDef))
+ sep_char = *pzDef++;
+ else sep_char = ' ';
+
+ switch (*pzDef) {
+ case NUL:
+ *pzOut++ = ';'; *pzOut++ = '\n';
+ break;
+
+ case '"':
+ case '\'':
+ case '{':
+ /*
+ * Quoted entries or subblocks do their own stringification
+ * sprintf is safe because we are copying strings around
+ * and *always* making the result smaller than the original
+ */
+ pzOut += sprintf(pzOut, " =%c%s;\n", sep_char, pzDef);
+ break;
+
+ default:
+ *pzOut++ = ' '; *pzOut++ = '='; *pzOut++ = sep_char;
+ *pzOut++ = '\'';
+
+ for (;;) {
+ switch (*pzOut++ = *pzDef++) {
+ case '\\':
+ *pzOut++ = '\\';
+ break;
+
+ case '\'':
+ pzOut[-1] = '\\';
+ *pzOut++ = '\'';
+ break;
+
+ case NUL:
+ goto unquotedDone;
+ }
+ } unquotedDone:;
+ pzOut[-1] = '\''; *pzOut++ = ';'; *pzOut++ = '\n';
+ break;
+ }
+ return pzOut;
+}
+
+
+/*
+ * list_attrib
+ */
+static char *
+list_attrib(char * pzText, char * pzOut)
+{
+ static char const zStart[] = " = ";
+
+ uint_t sepChar = ',';
+ int FirstAttr = 1;
+
+ strcpy(pzOut, zStart);
+ pzOut += sizeof(zStart) - 1;
+
+ /*
+ * See if there is an alternate separator character.
+ * It must be a punctuation character that is not also
+ * a quote character.
+ */
+ if (ispunct(*pzText) && (*pzText != '"') && (*pzText != '\''))
+ sepChar = (uint_t)*(pzText++);
+ while (isspace(*pzText)) pzText++;
+
+ /*
+ * Loop for as long as we have text entries
+ */
+ while (*pzText != NUL) {
+
+ if (FirstAttr)
+ FirstAttr = 0;
+ else
+ *(pzOut++) = ',';
+
+ /*
+ * If the first thing we find is the separator char,
+ * then emit the empty string.
+ */
+ if ((uint_t)*pzText == sepChar) {
+ *(pzOut++) = '\''; *(pzOut++) = '\'';
+ pzText++;
+ continue;
+ }
+
+ /*
+ * Emit whatever we have. The call will consume any trailing
+ * separator character.
+ */
+ pzOut = subblock_str(&pzText, sepChar, pzOut);
+ }
+
+ /*
+ * IF there were no definitions, THEN emit an empty one
+ */
+ if (FirstAttr)
+ pzOut -= sizeof(zStart) - 1;
+
+ *(pzOut++) = ';';
+ *(pzOut++) = '\n';
+
+ return pzOut;
+}
+
+
+/*
+ * The text is quoted, so copy it as is, ensuring that escaped
+ * characters are not used to end the quoted text.
+ */
+static char *
+emit_quote(char ** ppzText, char * pzOut)
+{
+ char* pzText = *ppzText;
+ char svch = (*pzOut++ = *pzText++);
+
+ for (;;) {
+ switch (*pzOut++ = *pzText++) {
+
+ case '\\':
+ if ((*pzOut++ = *pzText++) != NUL)
+ break;
+
+ case NUL:
+ pzText--;
+ pzOut[-1] = svch;
+ svch = NUL;
+ /* FALLTHROUGH */
+
+ case '"':
+ case '\'':
+ if (pzOut[-1] == svch)
+ goto quoteDone;
+
+ break;
+ }
+ }
+
+quoteDone:
+ *ppzText = pzText;
+ return pzOut;
+}
+
+static void
+next_def_entry(char ** txt_pp, char const ** def_pp)
+{
+ char const * p = *def_pp;
+ (*txt_pp)++;
+ for (;;) {
+ switch (*++p) {
+ case ' ': p++; /* FALLTHROUGH */
+ case NUL:
+ *def_pp = p;
+ return;
+ }
+ }
+}
+
+static void
+emit_attribute(char const ** def_pp, char ** out_pp)
+{
+ static char const pfx[] = "\n ";
+
+ char * out = *out_pp;
+ char const * def = *def_pp;
+
+ memcpy(out, pfx, sizeof(pfx) - 1);
+ out += sizeof(pfx) - 1;
+
+ for (;;) {
+ *out++ = *def++;
+ switch (*def) {
+ case ' ': def++; /* FALLTHROUGH */
+ case NUL:
+ goto leave_emit_attribute;
+ }
+ }
+
+leave_emit_attribute:
+
+ *out++ = ';';
+ *out_pp = out;
+ *def_pp = def;
+}
+
+/*
+ * emit_subblock
+ */
+static char*
+emit_subblock(char const * pzDefList, char * pzText, char * pzOut)
+{
+ static char const zStart[] = " = {";
+ static char const zEnd[] = "\n };\n";
+
+ uint_t sepChar = ',';
+ int FirstAttr = 1;
+
+ /*
+ * Advance past subblock name to the entry name list
+ */
+ pzDefList += strlen(pzDefList) + 1;
+ strcpy(pzOut, zStart);
+ pzOut += sizeof(zStart) - 1;
+
+ /*
+ * See if there is an alternate separator character.
+ * It must be a punctuation character that is not also
+ * a quote character.
+ */
+ if (ispunct(*pzText) && (*pzText != '"') && (*pzText != '\''))
+ sepChar = (uint_t)*(pzText++);
+
+ /*
+ * Loop for as long as we have text entries and subblock
+ * attribute names, ...
+ */
+ do {
+ /*
+ * IF the first character is the separator,
+ * THEN this entry is skipped.
+ */
+ if ((uint_t)*pzText == sepChar) {
+ next_def_entry(&pzText, &pzDefList);
+ continue;
+ }
+
+ /*
+ * Skip leading white space in the attribute and check for done.
+ */
+ while (isspace(*pzText)) pzText++;
+ if (*pzText == NUL) {
+ /*
+ * IF there were no definitions, THEN emit one anyway
+ */
+ if (FirstAttr)
+ emit_attribute(&pzDefList, &pzOut);
+
+ break;
+ }
+
+ /*
+ * Copy out the attribute name
+ */
+ emit_attribute(&pzDefList, &pzOut);
+ FirstAttr = 0;
+
+ /*
+ * IF there are no data for this attribute,
+ * THEN we leave the definition empty.
+ */
+ if ((uint_t)*pzText == sepChar) {
+ pzText++;
+ continue;
+ }
+
+ /*
+ * Copy out the assignment operator and emit the string
+ */
+ pzOut[-1] = ' '; *pzOut++ = '='; *pzOut++ = ' ';
+ pzOut = subblock_str(&pzText, sepChar, pzOut);
+ *pzOut++ = ';';
+
+ } while (isalpha(*pzDefList));
+
+ memcpy(pzOut, zEnd, sizeof(zEnd));
+ return pzOut + sizeof(zEnd) - 1;
+}
+
+
+/*
+ * Emit a string in a fashion that autogen will be able to
+ * correctly reconstruct it.
+ */
+static char*
+subblock_str(char ** ppzText, uint_t sepChar, char * pzOut)
+{
+ char * pzText = *ppzText;
+ char * pcComma;
+ char * pcEnd;
+
+ /*
+ * Skip leading space
+ */
+ while (isspace(*pzText)) pzText++;
+
+ /*
+ * IF the text is already quoted,
+ * THEN call the quoted text emitting routine
+ */
+ if ((*pzText == '"') || (*pzText == '\'')) {
+ pzOut = emit_quote(&pzText, pzOut);
+
+ /*
+ * Make sure we strip off trailing white space and any
+ * separation character.
+ */
+ while (isspace(*pzText)) pzText++;
+ if ((uint_t)*pzText == sepChar) {
+ pzText++;
+ while (isspace(*pzText)) pzText++;
+ }
+ *ppzText = pzText;
+ return pzOut;
+ }
+
+ /*
+ * Look for the character that separates this entry text
+ * from the entry text for the next attribute. Leave 'pcComma'
+ * pointing to the character _before_ the character where we
+ * are to resume our text scan. (i.e. at the comma, or the
+ * last character in the string)
+ */
+ pcComma = strchr(pzText, (int)sepChar);
+ if (pcComma == (char*)NULL) {
+ pcEnd = pzText + strlen(pzText);
+ pcComma = pcEnd-1;
+ } else {
+ pcEnd = pcComma;
+ }
+
+ /*
+ * Clean off trailing white space.
+ */
+ while ((pcEnd > pzText) && isspace(pcEnd[-1])) pcEnd--;
+
+ /*
+ * Copy the text, surrounded by single quotes
+ */
+ *pzOut++ = '\'';
+ {
+ char svch = *pcEnd;
+ *pcEnd = NUL;
+ for (;;) {
+ char ch = *pzText++;
+ switch (ch) {
+ case '\'':
+ *pzOut++ = '\\';
+ default:
+ *pzOut++ = ch;
+ break;
+ case NUL:
+ goto copyDone;
+ }
+ } copyDone: ;
+
+ pzText = pcComma+1;
+ *pcEnd = svch;
+ }
+
+ *pzOut++ = '\'';
+ while (isspace(*pzText)) pzText++;
+ *ppzText = pzText;
+ return pzOut;
+}
+
+/* emacs
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of getdefs/gdemit.c */