--- /dev/null
+/* Automatically generated by Ragel from "ragel_xmldeclscanner.c".
+ *
+ * Parts of this file are copied from Ragel source covered by the GNU
+ * GPL. As a special exception, you may use the parts of this file copied
+ * from Ragel source without restriction. The remainder is derived from
+ * "ragel_xmldeclscanner.c" and inherits the copyright status of that file.
+ */
+
+# 1 "ragel_xmldeclscanner.c"
+
+/*
+ * This is an input file for the "Ragel" finite state machine compiler utility.
+ * It produces output code which implements a tag scanner for XML data.
+ * It is used for intelligent xml tag completion in an XML editor interface.
+ *
+ * See the Ragel online documentation for additional
+ * information about the format of this file.
+ *
+ * Written by Marc Liyanage <http://www.entropy.ch>
+ *
+ */
+
+
+#include <CoreFoundation/CoreFoundation.h>
+#include "ragel_xmldeclscanner.h"
+
+
+# 29 "ragel_xmldeclscanner_out.c"
+/* Forward dec state for the transition structure. */
+struct XMLDeclScannerStateStruct;
+
+/* Only non-static data: current state, acceptance indicator. */
+struct XMLDeclScannerStruct
+{
+ int *curState;
+ int accept;
+# 20 "ragel_xmldeclscanner.c"
+
+ char *mark_encodingstart;
+ char *mark_encodingend;
+
+# 43 "ragel_xmldeclscanner_out.c"
+};
+typedef struct XMLDeclScannerStruct XMLDeclScanner;
+
+/* Init the fsm. */
+void XMLDeclScannerInit( XMLDeclScanner *fsm );
+
+/* Execute some chunk of data. */
+void XMLDeclScannerExecute( XMLDeclScanner *fsm, char *data, int dlen );
+
+/* Indicate to the fsm tha there is no more data. */
+void XMLDeclScannerFinish( XMLDeclScanner *fsm );
+
+/* Did the machine accept? */
+int XMLDeclScannerAccept( XMLDeclScanner *fsm );
+
+# 25 "ragel_xmldeclscanner.c"
+
+
+# 62 "ragel_xmldeclscanner_out.c"
+#define f XMLDeclScanner_f
+#define s XMLDeclScanner_s
+#define k XMLDeclScanner_k
+#define i XMLDeclScanner_i
+#define t XMLDeclScanner_t
+
+#define SPEC_ANY_FLAT 0x01
+#define SPEC_ANY_SINGLE 0x02
+#define SPEC_ANY_RANGE 0x04
+#define SPEC_ANY_DEF 0x08
+#define SPEC_IS_FINAL 0x10
+#define SPEC_OUT_FUNC 0x20
+
+/* The array of functions. */
+static int XMLDeclScanner_f[] = {
+ 1, 0, 1, 1
+};
+
+/* The array of keys of transitions. */
+static char XMLDeclScanner_k[] = {
+ -69, -65, 60, -17, 60, 63, 120, 109,
+ 108, 32, 9, 13, 118, 101, 114, 115,
+ 105, 111, 110, 61, 34, 39, 45, 46,
+ 58, 95, 48, 57, 65, 90, 97, 122,
+ 39, 45, 46, 58, 95, 48, 57, 65,
+ 90, 97, 122, 39, 45, 46, 58, 95,
+ 48, 57, 65, 90, 97, 122, 39, 45,
+ 46, 58, 95, 48, 57, 65, 90, 97,
+ 122, 39, 45, 46, 58, 95, 48, 57,
+ 65, 90, 97, 122, 32, 9, 13, 45,
+ 46, 58, 95, 48, 57, 65, 90, 97,
+ 122, 34, 45, 46, 58, 95, 48, 57,
+ 65, 90, 97, 122, 34, 45, 46, 58,
+ 95, 48, 57, 65, 90, 97, 122, 34,
+ 45, 46, 58, 95, 48, 57, 65, 90,
+ 97, 122, 34, 45, 46, 58, 95, 48,
+ 57, 65, 90, 97, 122, 32, 9, 13,
+ 101, 110, 99, 111, 100, 105, 110, 103,
+ 61, 34, 39, 65, 90, 97, 122, 34,
+ 45, 46, 95, 48, 57, 65, 90, 97,
+ 122, 34, 45, 46, 95, 48, 57, 65,
+ 90, 97, 122, 34, 45, 46, 95, 48,
+ 57, 65, 90, 97, 122, 32, 63, 9,
+ 13, 65, 90, 97, 122, 39, 45, 46,
+ 95, 48, 57, 65, 90, 97, 122, 39,
+ 45, 46, 95, 48, 57, 65, 90, 97,
+ 122, 39, 45, 46, 95, 48, 57, 65,
+ 90, 97, 122, 32, 63, 9, 13, 116,
+ 97, 110, 100, 97, 108, 111, 110, 101,
+ 61, 34, 39, 110, 121, 101, 115, 39,
+ 111, 39, 32, 63, 9, 13, 110, 121,
+ 101, 115, 34, 111, 34, 32, 63, 9,
+ 13, 63, 63, 115, 62, 0
+};
+
+/* The array of indicies into the transition array. */
+static unsigned char XMLDeclScanner_i[] = {
+ 1, 2, 3, 4, 3, 5, 6, 7,
+ 8, 9, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 21,
+ 21, 21, 21, 21, 22, 23, 24, 24,
+ 24, 24, 24, 24, 22, 23, 24, 24,
+ 24, 24, 24, 24, 22, 23, 24, 24,
+ 24, 24, 24, 24, 22, 23, 24, 24,
+ 24, 24, 24, 24, 25, 25, 26, 27,
+ 27, 27, 27, 27, 27, 28, 29, 30,
+ 30, 30, 30, 30, 30, 28, 29, 30,
+ 30, 30, 30, 30, 30, 28, 29, 30,
+ 30, 30, 30, 30, 30, 28, 29, 30,
+ 30, 30, 30, 30, 30, 25, 25, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 42, 43, 44, 45, 45,
+ 45, 45, 45, 43, 44, 45, 45, 45,
+ 45, 45, 43, 44, 45, 45, 45, 45,
+ 45, 46, 47, 46, 48, 48, 49, 50,
+ 51, 51, 51, 51, 51, 49, 50, 51,
+ 51, 51, 51, 51, 49, 50, 51, 51,
+ 51, 51, 51, 46, 47, 46, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61,
+ 62, 63, 64, 65, 66, 67, 68, 69,
+ 68, 70, 47, 70, 71, 72, 73, 74,
+ 75, 76, 75, 70, 47, 70, 47, 47,
+ 77, 78, 79, 79, 0
+};
+
+/* The aray of states. */
+static int XMLDeclScanner_s[] = {
+ 768, 0, 0, 1026, 0, 0, 1, 1026,
+ 1, 1, 1, 1026, 2, 2, 1, 1026,
+ 3, 3, 2, 1026, 5, 5, 1, 1026,
+ 6, 6, 1, 1026, 7, 7, 1, 1026,
+ 8, 8, 1, 1286, 9, 9, 1, 1,
+ 1026, 12, 11, 1, 1026, 13, 12, 1,
+ 1026, 14, 13, 1, 1026, 15, 14, 1,
+ 1026, 16, 15, 1, 1026, 17, 16, 1,
+ 1026, 18, 17, 1, 1026, 19, 18, 1,
+ 1026, 20, 19, 2, 1286, 22, 21, 4,
+ 3, 1286, 32, 28, 5, 3, 1286, 43,
+ 36, 5, 3, 1286, 54, 44, 5, 3,
+ 1286, 65, 52, 5, 3, 1286, 76, 60,
+ 1, 1, 1286, 79, 62, 4, 3, 1286,
+ 89, 69, 5, 3, 1286, 100, 77, 5,
+ 3, 1286, 111, 85, 5, 3, 1286, 122,
+ 93, 5, 3, 1286, 133, 101, 1, 1,
+ 1026, 136, 103, 1, 1026, 137, 104, 1,
+ 1026, 138, 105, 1, 1026, 139, 106, 1,
+ 1026, 140, 107, 1, 1026, 141, 108, 1,
+ 1026, 142, 109, 1, 1026, 143, 110, 1,
+ 1026, 144, 111, 1, 1026, 145, 112, 2,
+ 1028, 147, 114, 2, 1286, 151, 116, 4,
+ 3, 1286, 161, 123, 4, 3, 1286, 171,
+ 130, 4, 3, 1286, 181, 137, 2, 1,
+ 1028, 185, 140, 2, 1286, 189, 142, 4,
+ 3, 1286, 199, 149, 4, 3, 1286, 209,
+ 156, 4, 3, 1286, 219, 163, 2, 1,
+ 1026, 223, 166, 1, 1026, 224, 167, 1,
+ 1026, 225, 168, 1, 1026, 226, 169, 1,
+ 1026, 227, 170, 1, 1026, 228, 171, 1,
+ 1026, 229, 172, 1, 1026, 230, 173, 1,
+ 1026, 231, 174, 1, 1026, 232, 175, 1,
+ 1026, 233, 176, 2, 1026, 235, 178, 2,
+ 1026, 237, 180, 1, 1026, 238, 181, 1,
+ 1026, 239, 182, 1, 1026, 240, 183, 1,
+ 1026, 241, 184, 1, 1286, 242, 185, 2,
+ 1, 1026, 246, 188, 2, 1026, 248, 190,
+ 1, 1026, 249, 191, 1, 1026, 250, 192,
+ 1, 1026, 251, 193, 1, 1026, 252, 194,
+ 1, 1286, 253, 195, 2, 1, 1026, 257,
+ 198, 1, 1026, 258, 199, 2, 1026, 260,
+ 201, 1, 792, 261, 202, 792, 261, 203,
+ 0
+};
+
+/* The array of transitions. */
+static int *XMLDeclScanner_t[] = {
+ 0, 0, s+7, 0, s+11, 0, s+19, 0,
+ s+3, 0, s+23, 0, s+27, 0, s+31, 0,
+ s+35, 0, s+40, 0, s+44, 0, s+48, 0,
+ s+52, 0, s+56, 0, s+60, 0, s+64, 0,
+ s+68, 0, s+72, 0, s+106, 0, s+76, 0,
+ s+86, 0, s+81, 0, s+101, 0, s+96, 0,
+ s+91, 0, s+136, 0, s+116, 0, s+111, 0,
+ s+131, 0, s+126, 0, s+121, 0, s+140, 0,
+ s+144, 0, s+148, 0, s+152, 0, s+156, 0,
+ s+160, 0, s+164, 0, s+168, 0, s+172, 0,
+ s+176, 0, s+200, 0, s+180, f+0, s+195, f+2,
+ s+190, 0, s+185, 0, s+330, 0, s+334, 0,
+ s+204, f+0, s+219, f+2, s+214, 0, s+209, 0,
+ s+228, 0, s+232, 0, s+236, 0, s+240, 0,
+ s+244, 0, s+248, 0, s+252, 0, s+256, 0,
+ s+260, 0, s+264, 0, s+297, 0, s+268, 0,
+ s+284, 0, s+272, 0, s+276, 0, s+280, 0,
+ s+292, 0, s+288, 0, s+326, 0, s+313, 0,
+ s+301, 0, s+305, 0, s+309, 0, s+321, 0,
+ s+317, 0, s+224, 0, s+338, 0, s+341, 0,
+ 0
+};
+
+/* The start state. */
+static int *XMLDeclScanner_start = s+15;
+
+/* Init the fsm to a runnable state. */
+void XMLDeclScannerInit( XMLDeclScanner *fsm )
+{
+ fsm->curState = XMLDeclScanner_start;
+ fsm->accept = 0;
+# 29 "ragel_xmldeclscanner.c"
+{
+ fsm->mark_encodingstart = fsm->mark_encodingend = NULL;
+ }
+# 233 "ragel_xmldeclscanner_out.c"
+}
+
+/* Did the fsm accept? */
+int XMLDeclScannerAccept( XMLDeclScanner *fsm )
+{
+ return fsm->accept;
+}
+
+/* Binary search an array of keys looking for a key. */
+static char *XMLDeclScannerBSearch( char c, char *keys, int len )
+{
+ char *lower = keys;
+ char *mid;
+ char *upper = keys + len - 1;
+ while (1) {
+ if ( upper < lower )
+ return 0;
+
+ /* Find the midpoint. */
+ mid = lower + ((upper-lower) >> 1);
+
+ if ( c < *mid )
+ upper = mid - 1;
+ else if ( c > *mid )
+ lower = mid + 1;
+ else
+ return mid;
+ }
+}
+
+/* Binary search an array of keys looking for a key. */
+static char *XMLDeclScannerRangeBSearch( char c, char *keys, int len )
+{
+ char *lower = keys;
+ char *mid;
+ char *upper = keys + len - 2;
+ while (1) {
+ if ( upper < lower )
+ return 0;
+
+ /* Find the midpoint. Be sure to settle on the
+ * lower end of a range. */
+ mid = lower + (((upper-lower) >> 1) & ~1);
+
+ if ( c < mid[0] )
+ upper = mid - 2;
+ else if ( c > mid[1] )
+ lower = mid + 2;
+ else {
+ /* The key was found in the range mid, return it. */
+ return mid;
+ }
+ }
+}
+
+/* Execute the fsm on some chunk of data. */
+void XMLDeclScannerExecute( XMLDeclScanner *fsm, char *data, int dlen )
+{
+ char *p = data;
+ int len = dlen;
+ int *cs = fsm->curState;
+ int specs, **trans, *funcs, nfuncs;
+ char *keys;
+ unsigned char *inds;
+
+ if ( data == 0 )
+ goto finishInput;
+
+again:
+ if ( cs == 0 || len == 0 )
+ goto out;
+
+ /* Get required data. */
+ specs = *cs++;
+ keys = k + *cs++;
+ inds = i + *cs++;
+
+ /* Try flat index. */
+ if ( specs & SPEC_ANY_FLAT ) {
+ int indsLen = *cs++;
+ keys += 2;
+ inds += indsLen;
+ }
+
+ /* Try binary search single. */
+ if ( specs & SPEC_ANY_SINGLE ) {
+ /* Try to find the key. */
+ int indsLen = *cs++;
+ char *match = XMLDeclScannerBSearch( *p, keys, indsLen );
+
+ if ( match != 0 ) {
+ trans = t + (inds[match - keys]<<1);
+ goto match;
+ }
+
+ /* Advance over the keys and indicies. */
+ keys += indsLen;
+ inds += indsLen;
+ }
+
+ /* Try binary search range. */
+ if ( specs & SPEC_ANY_RANGE ) {
+ /* Try to find the key. */
+ int indsLen = *cs++;
+ char *match = XMLDeclScannerRangeBSearch( *p, keys, (indsLen<<1) );
+
+ if ( match != 0 ) {
+ trans = t + (inds[(match - keys)>>1]<<1);
+ goto match;
+ }
+
+ /* Advance over the keys and indicies. */
+ keys += (indsLen<<1);
+ inds += indsLen;
+ }
+
+ /* Try the default transition. */
+ if ( specs & SPEC_ANY_DEF ) {
+ trans = t + ((*inds)<<1);
+ goto match;
+ }
+
+ /* No match. */
+ cs = 0;
+ goto out;
+
+match:
+ /* Move to the new state. */
+ cs = *trans++;
+
+ /* Check for functions. */
+ if ( (funcs=*trans) == 0 )
+ goto noFuncs;
+
+execFuncs:
+ nfuncs = *funcs++;
+ while ( nfuncs-- > 0 ) {
+ switch ( *funcs++ ) {
+ case 0:
+# 34 "ragel_xmldeclscanner.c"
+ {
+ DEBUG && fprintf(stderr, "encodingstart: %d %c\n", p - data, *p);
+ fsm->mark_encodingstart = p;
+ } break;
+ case 1:
+# 39 "ragel_xmldeclscanner.c"
+ {
+ fsm->mark_encodingend = p;
+ DEBUG && fprintf(stderr, "encodingend: %d %c\n", p - data, *p);
+ DEBUG && fprintf(stderr, "length: %d\n", fsm->mark_encodingend - fsm->mark_encodingstart);
+ } break;
+# 385 "ragel_xmldeclscanner_out.c"
+ }
+ }
+
+noFuncs:
+ p++, len--;
+ goto again;
+
+finishInput:
+ if ( cs != 0 && *cs & SPEC_IS_FINAL ) {
+ /* The machine accepts. */
+ fsm->accept = 1;
+ /* If finishing in a final state then execute the
+ * out functions for it. (if any). */
+ if ( *cs & SPEC_OUT_FUNC ) {
+ funcs = f+*(cs + (*cs>>8)-1);
+ len = 1;
+ goto execFuncs;
+ }
+ }
+ else {
+ /* If we are not in a final state then this
+ * is an error. Move to the error state. */
+ fsm->curState = 0;
+ }
+
+out:
+ fsm->curState = cs;
+}
+
+/* Indicate to the fsm that the input is done. Does cleanup tasks. */
+void XMLDeclScannerFinish( XMLDeclScanner *fsm )
+{
+ XMLDeclScannerExecute( fsm, 0, 0 );
+}
+
+#undef f
+#undef s
+#undef k
+#undef i
+#undef t
+#undef SPEC_ANY_FLAT
+#undef SPEC_ANY_SINGLE
+#undef SPEC_ANY_RANGE
+#undef SPEC_ANY_DEF
+#undef SPEC_IS_FINAL
+#undef SPEC_OUT_FUNC
+
+# 66 "ragel_xmldeclscanner.c"
+
+
+
+
+
+unsigned int getEncodingFromXmlDecl(char *data, int len) {
+
+ int encodinglength, result;
+ CFStringRef encodingString;
+
+ XMLDeclScanner scanner, *machine = &scanner;
+
+ XMLDeclScannerInit(machine);
+ XMLDeclScannerExecute(machine, data, len > 512 ? 512 : len);
+ XMLDeclScannerFinish(machine);
+
+ if (!XMLDeclScannerAccept(machine)) {
+ return 0;
+ }
+
+ encodinglength = machine->mark_encodingend - machine->mark_encodingstart;
+
+
+ encodingString = CFStringCreateWithBytes (
+ NULL,
+ machine->mark_encodingstart,
+ (CFIndex)encodinglength,
+ kCFStringEncodingASCII,
+ 0
+ );
+
+ result = CFStringConvertIANACharSetNameToEncoding(encodingString);
+ CFRelease(encodingString);
+
+ if (result == kCFStringEncodingInvalidId)
+ return 0;
+
+ result = CFStringConvertEncodingToNSStringEncoding(result);
+
+ if (result == kCFStringEncodingInvalidId)
+ return 0;
+
+ return result;
+
+}
+
+
+
+/* No longer used, now using CoreFoundation
+
+ struct encodingPair {
+ int encoding;
+ char *matchstring;
+ };
+
+ struct encodingPair encodingPairs[] = {
+ {NSUTF8StringEncoding, "UTF-8"},
+ {NSISOLatin1StringEncoding, "ISO-8859-1"},
+ {0, NULL},
+ };
+
+
+
+ for (i = 0; encodingPairs[i].matchstring; i++) {
+
+ testlength = strlen(encodingPairs[i].matchstring);
+ if (!strncasecmp(machine->mark_encodingstart, encodingPairs[i].matchstring, encodinglength < testlength ? encodinglength : testlength)) {
+ return encodingPairs[i].encoding;
+ }
+
+ }
+
+ return 0;
+
+*/
+
--- /dev/null
+
+/*
+ * This is an input file for the "Ragel" finite state machine compiler utility.
+ * It produces output code which implements a tag scanner for XML data.
+ * It is used for intelligent xml tag completion in an XML editor interface.
+ *
+ * See the Ragel online documentation for additional
+ * information about the format of this file.
+ *
+ * Written by Marc Liyanage <http://www.entropy.ch>
+ *
+ */
+
+
+#include "ragel_xmlscanner.h"
+
+
+/* modes for the scan, before or after cursor position */
+enum {
+ BEFORE = 1,
+ AFTER
+};
+
+
+%% XMLScanner
+ struct {
+ char *data;
+ char *stack[STACKDEPTH];
+ char *stack_e[STACKDEPTH];
+ int sp;
+ int sp_e;
+ char *mark_namestart;
+ char *mark_nameend;
+ char *mark_attrstart;
+ char *mark_attrend;
+ char *before_toptag;
+ int cursor;
+ int in_comment;
+ int in_cdata;
+ int in_tag;
+ int mode;
+ };
+%%
+
+
+
+void checkETag(XMLScanner *fsm);
+void popTag(XMLScanner *fsm);
+void pushTag(XMLScanner *fsm);
+
+%% XMLScanner
+
+ init {
+ fsm->sp = 0;
+ fsm->sp_e = 0;
+ fsm->mode = BEFORE;
+ fsm->in_comment = 0;
+ fsm->in_cdata = 0;
+ fsm->in_tag = 0;
+ }
+
+
+ func commentstart {
+ DEBUG && fprintf(stderr, "commentstart: %d %c\n", p - data, *p);
+ fsm->in_comment = 1;
+ }
+
+ func commentend {
+ DEBUG && fprintf(stderr, "commentend: %d %c\n", p - data, *p);
+ fsm->in_comment = 0;
+ fsm->in_tag = 0;
+ }
+
+ func cdatastart {
+ fsm->in_cdata = 1;
+ }
+
+ func cdataend {
+ fsm->in_cdata = 0;
+ fsm->in_tag = 0;
+ }
+
+ func enter_tag {
+ DEBUG && fprintf(stderr, "enter tag at %d\n", p - data);
+ fsm->in_tag = 1;
+ }
+
+ func exit_tag {
+ DEBUG && fprintf(stderr, "exit tag at %d\n", p - data);
+ fsm->in_tag = 0;
+ }
+
+ func name {
+// fprintf(stderr, "name: %d %c\n", p - data, *p);
+ }
+
+ func stag {
+ DEBUG && fprintf(stderr, "stag\n");
+ pushTag(fsm);
+ }
+
+ func etag {
+ DEBUG && fprintf(stderr, "stag: start: %d end: %d %c\n", fsm->mark_namestart - data, fsm->mark_nameend - data, *p);
+ checkETag(fsm);
+ }
+
+ func mark_namestart {
+ DEBUG && fprintf(stderr, "markstart: %d %c\n", p - data, *p);
+ fsm->mark_namestart = p;
+ }
+
+ func mark_nameend {
+ DEBUG && fprintf(stderr, "markend: %d %c\n", (p - 1) - data, *(p-1));
+ fsm->mark_nameend = p;
+ }
+
+ func mark_attrstart {
+// fprintf(stderr, "attrstart: %d %c\n", p - data, *p);
+ fsm->mark_attrstart = p;
+ }
+
+ func mark_attrend {
+// fprintf(stderr, "attrend: %d %c\n", (p - 1) - data, *(p - 1));
+ fsm->mark_attrend = p;
+ }
+
+
+func attr {
+ // fprintf(stderr, "attr: start: %d end: %d %c\n", fsm->mark_attrstart - data, fsm->mark_attrend - data, *p);
+}
+
+func pi {
+ DEBUG && fprintf(stderr, "pi: %d\n", p - data);
+}
+
+
+
+# // This is the actual grammar used by the scanner. It calls into the
+# // functions above at certain state transitions.
+
+ lt = '<' %enter_tag;
+ gt = '>' %exit_tag;
+
+ namechar = alpha | digit | '.' | '-' | '_' | ':';
+ nmtoken = +namechar;
+ name = (alpha | '_' | ':') *namechar;
+
+ attr = name >mark_attrstart '=' ((/'[^']*'/ | /"[^"]*"/) %mark_attrend) %attr;
+ stag = (lt (name >mark_namestart %mark_nameend) *(space | attr) gt) %stag;
+ etag = lt '/' (name >mark_namestart %mark_nameend) gt %etag;
+ emptytag = lt name *(space | attr) '/' gt;
+
+ commentstart = '<!--' %commentstart;
+ commentend = '-->' %commentend;
+ comment = commentstart ((!commentend commentend) | commentend);
+
+ cdatastart = '<![CDATA[[' %cdatastart;
+ cdataend = ']]>' %cdataend;
+ cdata = cdatastart ((!cdataend cdataend) | cdataend);
+
+ nonlt = /[^<]/;
+
+ main = *(comment %+1 | cdata %+1 | lt | stag | emptytag | etag | nonlt %-1);
+
+%%
+
+
+/*
+ * findCompletion does the actual work of driving the state machine
+ *
+ * Input:
+ * data - pointer to the buffer containing the XML data
+ * len - length of the data to analyze
+ * cursor - logical position of the editing cursor in the data buffer
+ * result - pointer to integer, which will contain a bit field with
+ * additional status information after completion:
+ *
+ * Completion rules:
+ *
+ * - The system builds a stack of tags which are open at the indicated cursor
+ * position in the XML data.
+ * - If completion is possible at that cursor position, the integer
+ * result will be 0. The caller should insert a closing tag for the
+ * topmost tag name on the stack.
+ * - It can use the other elements of the
+ * stack for GUI goodies, like display of a stack of currently open tags
+ * in the editor. However, it should not assume that the tag stack can be
+ * used to close all opening tags. Instead, it should re-evaluate
+ * the document with this routine after the tag has been inserted and the
+ * cursor position might have moved as a result.
+ *
+ * - There are a few cases where completion should not be performed, this
+ * will be indicated by the bitfield in the result int parameter:
+ * - ERROR_IN_TAG: The cursor is in the middle of a tag. No useful completion
+ * is possible (maybe look at the prefix and complete
+ * accordingly, sometime in the future...)
+ * - ERROR_IN_COMMENT: The cursor is inside a comment block.
+ * - ERROR_IN_CDATA: The cursor is inside a cdata section.
+ * - ERROR_NO_TAG: There's no unmatched opening tag to the left of the cursor
+ * - ERROR_ALREADY_BALANCED_HERE: The currently open tags to the left of the
+ * cursor are already balanced with an equal amount
+ * of closing tags to the right of the cursor.
+ *
+ * Results:
+ *
+ * Returns a pointer to an array of pointers to chars.
+ * Each array entry contains one tag from the stack
+ * tags open at the cursor position.
+ *
+ */
+char (*findCompletion(char *data, int len, int cursor, int *result, int tagpositions[STACKDEPTH]))[] {
+
+ XMLScanner scanner, *machine = &scanner;
+ char toptag[MAXTAGLENGTH];
+ unsigned int i, lower, upper, openingcount, closingcount, identical_opening, identical_closing;
+
+ char (*resultstack)[MAXTAGLENGTH] = NULL;
+ char (*resultstack_e)[MAXTAGLENGTH] = NULL;
+
+ *result = 0;
+
+ bzero(toptag, MAXTAGLENGTH);
+
+
+ // ------------ analyze data before cursor position ------------------
+
+ XMLScannerInit(machine);
+
+ machine->mode = BEFORE;
+ machine->cursor = cursor;
+
+ XMLScannerExecute(machine, data, cursor);
+ XMLScannerFinish(machine);
+
+
+ /* Any unbalanced open tags on the stack at all? If so, allocate memory
+ * and copy the tags over to a newly allocated buffer, into a stack.
+ */
+ if (machine->sp) {
+ resultstack = malloc(MAXTAGLENGTH * ((machine->sp/2) + 1));
+ } else {
+ /* Nothing useful to return to caller */
+ *result |= ERROR_NO_TAG;
+ return NULL;
+ }
+
+ openingcount = (machine->sp / 2);
+
+ for (i = 0; i < openingcount; i++) {
+ lower = i * 2;
+ upper = lower + 1;
+ bzero(resultstack[i], MAXTAGLENGTH);
+ strncpy(resultstack[i], machine->stack[lower], machine->stack[upper] - machine->stack[lower]);
+ if (tagpositions) {
+ tagpositions[lower] = machine->stack[lower] - data;
+ tagpositions[upper] = machine->stack[upper] - data;
+ }
+ }
+
+ /* make sure the slot after the topmost tag is zeroed out, so the
+ * calling code can use that as an end of stack marker.
+ */
+ bzero(resultstack[openingcount], MAXTAGLENGTH);
+
+ strncpy(toptag, resultstack[openingcount - 1], MAXTAGLENGTH);
+
+ if (machine->in_tag)
+ *result |= ERROR_IN_TAG;
+ if (machine->in_comment)
+ *result |= ERROR_IN_COMMENT;
+ if (machine->in_cdata)
+ *result |= ERROR_IN_CDATA;
+
+ if (*result) {
+ return resultstack;
+ }
+
+ // count identical opening tags on the top of the stack.
+ identical_opening = 1;
+ for (i = (openingcount - 2); i >= 0; i--) {
+ if (!strcmp(resultstack[i], resultstack[i + 1])) {
+ identical_opening++;
+ } else {
+ break;
+ }
+ }
+
+
+ // ------------ analyze data after cursor position ------------------
+
+// fprintf(stderr, "accept: %d\n", XMLScannerAccept(machine));
+
+
+ /* Reinitialize and reconfigure the scanner, and run it again, but this time with
+ * the data from the cursor position until the end.
+ */
+ XMLScannerInit(machine);
+ machine->mode = AFTER;
+ machine->cursor = cursor;
+ XMLScannerExecute(machine, data + cursor, len - cursor);
+ XMLScannerFinish(machine);
+
+ /* If there are no closing tags, we don't need to check if
+ * the topmost opening tags on the stacks are already balanced
+ */
+ closingcount = (machine->sp_e / 2);
+ if (!closingcount) {
+ return resultstack;
+ }
+
+ resultstack_e = malloc(MAXTAGLENGTH * ((machine->sp_e/2) + 1));
+
+ for (i = 0; i < closingcount; i++) {
+ lower = i * 2;
+ upper = lower + 1;
+
+ bzero(resultstack_e[i], MAXTAGLENGTH);
+ strncpy(resultstack_e[i], machine->stack_e[lower], machine->stack_e[upper] - machine->stack_e[lower]);
+
+ }
+
+ DEBUG && fprintf(stderr, "closing count %d\n", closingcount);
+
+ identical_closing = closingcount ? 1 : 0;
+ for (i = 0; i < closingcount - 1; i++) {
+ if (!strcmp(resultstack_e[i], resultstack_e[i + 1])) {
+ identical_closing++;
+ } else {
+ break;
+ }
+ }
+
+ DEBUG && fprintf(stderr, "identical_closing %d\n", identical_closing);
+
+ /* last opening tag before cursor and first closing tag after
+ * cursor have to be identical, otherwise we are not interested
+ * if they are balanced
+ */
+ if (strcmp(resultstack[openingcount - 1], resultstack_e[0])) {
+ free(resultstack_e);
+ return resultstack;
+ }
+
+ /* ok, the adjacent opening and closing tags are identical, so we
+ * need to know if there are less closing tags than opening tags,
+ * because only then does tag completion make any sense at this location
+ */
+
+ if (identical_opening <= identical_closing) {
+ *result |= ERROR_ALREADY_BALANCED_HERE;
+ }
+
+ free(resultstack_e);
+
+ // caller needs to free() this...
+ return resultstack;
+
+}
+
+
+
+
+
+/*
+ * Push the current start and end buffer position markers onto our
+ * stack of currently open tags. We're operating with position numbers
+ * only at this point, later there will be actual copies out of the buffer
+ */
+void pushTag(XMLScanner *fsm) {
+
+ char buffer[MAXTAGLENGTH];
+ bzero(buffer, sizeof(buffer));
+
+ if (fsm->sp + 2 >= (STACKDEPTH - 1)) {
+ fprintf(stderr, "pushTag: stack overflow at %d, can't push more!\n", STACKDEPTH);
+ return;
+ }
+
+ fsm->stack[fsm->sp++] = fsm->mark_namestart;
+ fsm->stack[fsm->sp++] = fsm->mark_nameend;
+
+ DEBUG && strncpy(buffer, fsm->stack[fsm->sp - 2], fsm->stack[fsm->sp - 1] - fsm->stack[fsm->sp - 2]);
+ DEBUG && fprintf(stderr, "pushTag: pushing '%s'\n", buffer);
+
+}
+
+
+
+/*
+ * Push the current start and end buffer position markers for
+ * *end* tags onto the stack.
+ */
+void pushETag(XMLScanner *fsm) {
+
+ char buffer[MAXTAGLENGTH];
+ bzero(buffer, sizeof(buffer));
+
+ if (fsm->sp_e + 2 >= (STACKDEPTH - 1)) {
+ fprintf(stderr, "pushETag: stack overflow at %d, can't push more!\n", STACKDEPTH);
+ return;
+ }
+
+ fsm->stack_e[fsm->sp_e++] = fsm->mark_namestart;
+ fsm->stack_e[fsm->sp_e++] = fsm->mark_nameend;
+
+ DEBUG && strncpy(buffer, fsm->stack_e[fsm->sp_e - 2], fsm->stack_e[fsm->sp_e - 1] - fsm->stack_e[fsm->sp_e - 2]);
+ DEBUG && fprintf(stderr, "pushETag: pushing '%s'\n", buffer);
+}
+
+
+
+/*
+ * pop the topmost element
+ */
+void popTag(XMLScanner *fsm) {
+
+ char buffer[MAXTAGLENGTH];
+ bzero(buffer, sizeof(buffer));
+
+ if (fsm->sp < 2) {
+ fprintf(stderr, "popTag: stack underflow, can't pop more!\n");
+ return;
+ }
+
+ fsm->sp -= 2;
+
+ DEBUG && strncpy(buffer, fsm->stack[fsm->sp], fsm->stack[fsm->sp + 1] - fsm->stack[fsm->sp]);
+ DEBUG && fprintf(stderr, "popTag: popping '%s'\n", buffer);
+}
+
+
+/*
+ * check out the end tag we just stumbled upon.
+ * the behavior is different here, according to the scan mode
+ * (before or after cursor position).
+ */
+void checkETag(XMLScanner *fsm) {
+
+ char buffer[MAXTAGLENGTH];
+ bzero(buffer, sizeof(buffer));
+ int len;
+
+ /* return if the stack is empty in BEFORE mode, we can't do anything interesting */
+ if (!fsm->sp && fsm->mode == BEFORE) {
+ return;
+ }
+
+ len = fsm->mark_nameend - fsm->mark_namestart;
+
+ strncpy(buffer, fsm->mark_namestart, len);
+
+ /* if topmost tag on stack is identical to current end tag, pop it */
+ if(fsm->sp && !strncmp(fsm->stack[fsm->sp - 2], buffer, len)) {
+// fprintf(stderr, "checkETag: popping, len = %d, etag = '%s'\n", len, buffer);
+ popTag(fsm);
+ } else {
+ pushETag(fsm);
+ }
+
+
+}
+
--- /dev/null
+/* Automatically generated by Ragel from "ragel_xmlscanner.c".
+ *
+ * Parts of this file are copied from Ragel source covered by the GNU
+ * GPL. As a special exception, you may use the parts of this file copied
+ * from Ragel source without restriction. The remainder is derived from
+ * "ragel_xmlscanner.c" and inherits the copyright status of that file.
+ */
+
+# 1 "ragel_xmlscanner.c"
+
+/*
+ * This is an input file for the "Ragel" finite state machine compiler utility.
+ * It produces output code which implements a tag scanner for XML data.
+ * It is used for intelligent xml tag completion in an XML editor interface.
+ *
+ * See the Ragel online documentation for additional
+ * information about the format of this file.
+ *
+ * Written by Marc Liyanage <http://www.entropy.ch>
+ *
+ */
+
+
+#include "ragel_xmlscanner.h"
+
+
+/* modes for the scan, before or after cursor position */
+enum {
+ BEFORE = 1,
+ AFTER
+};
+
+
+# 35 "ragel_xmlscanner_out.c"
+/* Forward dec state for the transition structure. */
+struct XMLScannerStateStruct;
+
+/* Only non-static data: current state, acceptance indicator. */
+struct XMLScannerStruct
+{
+ int *curState;
+ int accept;
+# 26 "ragel_xmlscanner.c"
+
+ char *data;
+ char *stack[STACKDEPTH];
+ char *stack_e[STACKDEPTH];
+ int sp;
+ int sp_e;
+ char *mark_namestart;
+ char *mark_nameend;
+ char *mark_attrstart;
+ char *mark_attrend;
+ char *before_toptag;
+ int cursor;
+ int in_comment;
+ int in_cdata;
+ int in_tag;
+ int mode;
+
+# 62 "ragel_xmlscanner_out.c"
+};
+typedef struct XMLScannerStruct XMLScanner;
+
+/* Init the fsm. */
+void XMLScannerInit( XMLScanner *fsm );
+
+/* Execute some chunk of data. */
+void XMLScannerExecute( XMLScanner *fsm, char *data, int dlen );
+
+/* Indicate to the fsm tha there is no more data. */
+void XMLScannerFinish( XMLScanner *fsm );
+
+/* Did the machine accept? */
+int XMLScannerAccept( XMLScanner *fsm );
+
+# 43 "ragel_xmlscanner.c"
+
+
+
+
+void checkETag(XMLScanner *fsm);
+void popTag(XMLScanner *fsm);
+void pushTag(XMLScanner *fsm);
+
+# 87 "ragel_xmlscanner_out.c"
+#define f XMLScanner_f
+#define s XMLScanner_s
+#define k XMLScanner_k
+#define i XMLScanner_i
+#define t XMLScanner_t
+
+#define SPEC_ANY_FLAT 0x01
+#define SPEC_ANY_SINGLE 0x02
+#define SPEC_ANY_RANGE 0x04
+#define SPEC_ANY_DEF 0x08
+#define SPEC_IS_FINAL 0x10
+#define SPEC_OUT_FUNC 0x20
+
+/* The array of functions. */
+static int XMLScanner_f[] = {
+ 1, 0, 1, 1, 1, 2, 1, 3,
+ 1, 4, 1, 5, 1, 9, 1, 10,
+ 2, 1, 1, 2, 3, 3, 2, 4,
+ 4, 2, 5, 7, 2, 5, 8, 2,
+ 11, 11, 2, 12, 13, 3, 10, 11,
+ 11, 4, 4, 4, 9, 4, 4, 12,
+ 13, 12, 13, 6, 12, 13, 11, 12,
+ 13, 11
+};
+
+/* The array of keys of transitions. */
+static char XMLScanner_k[] = {
+ 45, 45, 45, 45, 45, 62, 45, 60,
+ 45, 45, 62, 60, 67, 68, 65, 84,
+ 65, 91, 91, 93, 93, 93, 62, 93,
+ 60, 93, 93, 62, 93, 60, 60, 62,
+ 60, 32, 47, 58, 62, 95, 9, 13,
+ 65, 90, 97, 122, 32, 45, 46, 47,
+ 58, 62, 95, 9, 13, 48, 57, 65,
+ 90, 97, 122, 32, 45, 46, 47, 58,
+ 62, 95, 9, 13, 48, 57, 65, 90,
+ 97, 122, 32, 45, 46, 47, 58, 61,
+ 62, 95, 9, 13, 48, 57, 65, 90,
+ 97, 122, 32, 45, 46, 47, 58, 61,
+ 62, 95, 9, 13, 48, 57, 65, 90,
+ 97, 122, 32, 45, 46, 47, 58, 62,
+ 95, 9, 13, 48, 57, 65, 90, 97,
+ 122, 32, 45, 46, 47, 58, 61, 62,
+ 95, 9, 13, 48, 57, 65, 90, 97,
+ 122, 45, 46, 58, 61, 95, 48, 57,
+ 65, 90, 97, 122, 45, 46, 58, 61,
+ 95, 48, 57, 65, 90, 97, 122, 45,
+ 46, 58, 61, 95, 48, 57, 65, 90,
+ 97, 122, 32, 45, 46, 47, 58, 61,
+ 62, 95, 9, 13, 48, 57, 65, 90,
+ 97, 122, 32, 45, 46, 47, 58, 61,
+ 62, 95, 9, 13, 48, 57, 65, 90,
+ 97, 122, 32, 45, 46, 47, 58, 61,
+ 62, 95, 9, 13, 48, 57, 65, 90,
+ 97, 122, 34, 39, 32, 45, 46, 47,
+ 58, 61, 62, 95, 9, 13, 48, 57,
+ 65, 90, 97, 122, 32, 45, 46, 47,
+ 58, 61, 62, 95, 9, 13, 48, 57,
+ 65, 90, 97, 122, 32, 45, 46, 47,
+ 58, 61, 62, 95, 9, 13, 48, 57,
+ 65, 90, 97, 122, 45, 46, 58, 61,
+ 95, 48, 57, 65, 90, 97, 122, 45,
+ 46, 58, 61, 95, 48, 57, 65, 90,
+ 97, 122, 45, 46, 58, 61, 95, 48,
+ 57, 65, 90, 97, 122, 45, 46, 58,
+ 61, 95, 48, 57, 65, 90, 97, 122,
+ 45, 46, 58, 61, 95, 48, 57, 65,
+ 90, 97, 122, 45, 46, 58, 61, 95,
+ 48, 57, 65, 90, 97, 122, 34, 39,
+ 32, 47, 58, 62, 95, 9, 13, 65,
+ 90, 97, 122, 34, 32, 47, 58, 62,
+ 95, 9, 13, 65, 90, 97, 122, 39,
+ 45, 46, 58, 62, 95, 48, 57, 65,
+ 90, 97, 122, 45, 46, 58, 62, 95,
+ 48, 57, 65, 90, 97, 122, 45, 46,
+ 58, 62, 95, 48, 57, 65, 90, 97,
+ 122, 45, 46, 58, 62, 95, 48, 57,
+ 65, 90, 97, 122, 45, 46, 58, 62,
+ 95, 48, 57, 65, 90, 97, 122, 45,
+ 46, 58, 62, 95, 48, 57, 65, 90,
+ 97, 122, 45, 46, 58, 62, 95, 48,
+ 57, 65, 90, 97, 122, 45, 46, 58,
+ 62, 95, 48, 57, 65, 90, 97, 122,
+ 45, 46, 58, 62, 95, 48, 57, 65,
+ 90, 97, 122, 60, 33, 47, 58, 60,
+ 95, 65, 90, 97, 122, 60, 60, 45,
+ 60, 91, 58, 60, 95, 65, 90, 97,
+ 122, 32, 45, 46, 47, 58, 60, 62,
+ 95, 9, 13, 48, 57, 65, 90, 97,
+ 122, 32, 45, 46, 47, 58, 60, 62,
+ 95, 9, 13, 48, 57, 65, 90, 97,
+ 122, 32, 45, 46, 47, 58, 60, 62,
+ 95, 9, 13, 48, 57, 65, 90, 97,
+ 122, 0
+};
+
+/* The array of indicies into the transition array. */
+static unsigned char XMLScanner_i[] = {
+ 1, 2, 3, 4, 5, 6, 5, 6,
+ 7, 5, 8, 9, 8, 10, 5, 6,
+ 11, 5, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25,
+ 24, 26, 25, 24, 27, 28, 28, 29,
+ 24, 30, 25, 24, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42,
+ 38, 43, 43, 44, 45, 46, 39, 47,
+ 48, 49, 44, 50, 51, 51, 44, 45,
+ 46, 39, 47, 48, 49, 44, 50, 51,
+ 51, 44, 52, 53, 39, 54, 55, 48,
+ 56, 44, 57, 58, 58, 44, 52, 53,
+ 39, 54, 55, 48, 56, 44, 57, 58,
+ 58, 44, 45, 46, 39, 47, 48, 49,
+ 44, 50, 51, 51, 44, 52, 53, 39,
+ 54, 55, 48, 56, 44, 57, 58, 58,
+ 59, 60, 61, 55, 62, 63, 64, 64,
+ 59, 60, 61, 55, 62, 63, 64, 64,
+ 59, 60, 61, 55, 62, 63, 64, 64,
+ 44, 52, 53, 39, 54, 55, 48, 56,
+ 44, 57, 58, 58, 44, 52, 53, 39,
+ 54, 55, 48, 56, 44, 57, 58, 58,
+ 44, 52, 53, 39, 54, 55, 48, 56,
+ 44, 57, 58, 58, 65, 66, 44, 52,
+ 53, 39, 54, 55, 48, 56, 44, 57,
+ 58, 58, 44, 52, 53, 39, 54, 55,
+ 48, 56, 44, 57, 58, 58, 44, 52,
+ 53, 39, 54, 55, 48, 56, 44, 57,
+ 58, 58, 59, 60, 61, 55, 62, 63,
+ 64, 64, 59, 60, 61, 55, 62, 63,
+ 64, 64, 59, 60, 61, 55, 62, 63,
+ 64, 64, 59, 60, 61, 55, 62, 63,
+ 64, 64, 59, 60, 61, 55, 62, 63,
+ 64, 64, 59, 60, 61, 55, 62, 63,
+ 64, 64, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 71, 76, 76, 67, 68,
+ 71, 72, 73, 74, 75, 71, 76, 76,
+ 69, 70, 77, 78, 79, 80, 81, 82,
+ 83, 83, 77, 78, 79, 80, 81, 82,
+ 83, 83, 77, 78, 79, 80, 81, 82,
+ 83, 83, 77, 78, 79, 80, 81, 82,
+ 83, 83, 77, 78, 79, 80, 81, 82,
+ 83, 83, 77, 78, 79, 80, 81, 82,
+ 83, 83, 77, 78, 79, 80, 81, 82,
+ 83, 83, 77, 78, 79, 80, 81, 82,
+ 83, 83, 77, 78, 79, 80, 81, 82,
+ 83, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 91, 92, 93, 94, 93, 94,
+ 95, 93, 96, 94, 97, 93, 98, 99,
+ 99, 94, 44, 45, 46, 39, 47, 93,
+ 48, 49, 44, 50, 51, 51, 94, 44,
+ 45, 46, 39, 47, 93, 48, 49, 44,
+ 50, 51, 51, 94, 44, 45, 46, 39,
+ 47, 93, 48, 49, 44, 50, 51, 51,
+ 94, 0
+};
+
+/* The aray of states. */
+static int XMLScanner_s[] = {
+ 768, 0, 0, 1026, 0, 0, 1, 1034,
+ 1, 1, 1, 1034, 2, 3, 1, 1034,
+ 3, 5, 1, 1034, 4, 7, 2, 1338,
+ 6, 10, 2, 2, 1034, 8, 13, 1,
+ 1034, 9, 15, 2, 1338, 11, 18, 1,
+ 16, 1026, 12, 20, 1, 1026, 13, 21,
+ 1, 1026, 14, 22, 1, 1026, 15, 23,
+ 1, 1026, 16, 24, 1, 1026, 17, 25,
+ 1, 1026, 18, 26, 1, 1034, 19, 27,
+ 1, 1034, 20, 29, 1, 1034, 21, 31,
+ 1, 1034, 22, 33, 2, 1338, 24, 36,
+ 2, 6, 1034, 26, 39, 1, 1034, 27,
+ 41, 2, 1338, 29, 44, 1, 19, 1338,
+ 30, 46, 1, 25, 1026, 31, 48, 1,
+ 1338, 32, 49, 1, 10, 1286, 33, 51,
+ 5, 3, 1286, 44, 59, 7, 4, 1286,
+ 59, 70, 7, 4, 1286, 74, 81, 8,
+ 4, 1286, 90, 93, 8, 4, 1286, 106,
+ 105, 7, 4, 1286, 121, 116, 8, 4,
+ 1286, 137, 128, 5, 3, 1286, 148, 136,
+ 5, 3, 1286, 159, 144, 5, 3, 1286,
+ 170, 152, 8, 4, 1286, 186, 164, 8,
+ 4, 1286, 202, 176, 8, 4, 1026, 218,
+ 188, 2, 1286, 220, 190, 8, 4, 1286,
+ 236, 202, 8, 4, 1286, 252, 214, 8,
+ 4, 1286, 268, 226, 5, 3, 1286, 279,
+ 234, 5, 3, 1286, 290, 242, 5, 3,
+ 1286, 301, 250, 5, 3, 1286, 312, 258,
+ 5, 3, 1286, 323, 266, 5, 3, 1034,
+ 334, 274, 1, 1034, 335, 276, 1, 1286,
+ 336, 278, 5, 3, 1034, 347, 286, 1,
+ 1286, 348, 288, 5, 3, 1034, 359, 296,
+ 1, 1286, 360, 298, 5, 3, 1286, 371,
+ 306, 5, 3, 1286, 382, 314, 5, 3,
+ 1286, 393, 322, 5, 3, 1286, 404, 330,
+ 5, 3, 1286, 415, 338, 5, 3, 1286,
+ 426, 346, 5, 3, 1286, 437, 354, 5,
+ 3, 1286, 448, 362, 5, 3, 1338, 459,
+ 370, 1, 28, 1598, 460, 372, 5, 2,
+ 8, 1050, 469, 380, 1, 1050, 470, 382,
+ 1, 1050, 471, 384, 3, 1310, 474, 388,
+ 3, 2, 1310, 481, 394, 8, 4, 1310,
+ 497, 407, 8, 4, 1310, 513, 420, 8,
+ 4, 0
+};
+
+/* The array of transitions. */
+static int *XMLScanner_t[] = {
+ 0, 0, s+7, 0, s+28, f+0, s+11, f+0,
+ s+15, 0, s+11, 0, s+19, 0, s+23, 0,
+ s+313, f+2, s+307, f+2, s+32, 0, s+36, 0,
+ s+307, f+16, s+313, f+16, s+45, 0, s+49, 0,
+ s+53, 0, s+57, 0, s+61, 0, s+65, 0,
+ s+69, 0, s+90, f+4, s+73, f+4, s+77, 0,
+ s+73, 0, s+81, 0, s+85, 0, s+307, f+6,
+ s+313, f+6, s+94, 0, s+98, 0, s+307, f+19,
+ s+313, f+19, s+307, f+25, s+313, f+25, s+112, 0,
+ s+307, f+10, s+313, f+10, s+117, 0, s+108, 0,
+ s+152, f+31, s+103, 0, s+157, f+31, s+162, f+31,
+ s+117, f+14, s+122, 0, s+127, 0, s+132, f+37,
+ s+103, f+14, s+137, f+37, s+142, 0, s+147, f+37,
+ s+167, 0, s+172, 0, s+177, f+37, s+182, 0,
+ s+186, f+37, s+191, 0, s+196, f+37, s+201, 0,
+ s+206, 0, s+211, 0, s+216, 0, s+221, 0,
+ s+226, 0, s+231, 0, s+235, 0, s+239, 0,
+ s+244, 0, s+248, 0, s+253, 0, s+117, f+46,
+ s+108, f+34, s+152, f+51, s+103, f+34, s+157, f+51,
+ s+162, f+51, s+287, 0, s+282, 0, s+297, 0,
+ s+302, f+14, s+292, 0, s+277, 0, s+272, 0,
+ s+307, f+28, s+313, f+28, s+321, f+8, s+325, f+22,
+ s+330, f+41, s+307, f+8, s+335, f+41, s+340, f+41,
+ s+313, f+8, s+307, 0, s+313, 0, s+3, 0,
+ s+41, 0, s+267, f+12, s+262, f+12, s+257, f+12,
+ 0
+};
+
+/* The start state. */
+static int *XMLScanner_start = s+317;
+
+/* Init the fsm to a runnable state. */
+void XMLScannerInit( XMLScanner *fsm )
+{
+ fsm->curState = XMLScanner_start;
+ fsm->accept = 0;
+# 53 "ragel_xmlscanner.c"
+{
+ fsm->sp = 0;
+ fsm->sp_e = 0;
+ fsm->mode = BEFORE;
+ fsm->in_comment = 0;
+ fsm->in_cdata = 0;
+ fsm->in_tag = 0;
+ }
+# 338 "ragel_xmlscanner_out.c"
+}
+
+/* Did the fsm accept? */
+int XMLScannerAccept( XMLScanner *fsm )
+{
+ return fsm->accept;
+}
+
+/* Binary search an array of keys looking for a key. */
+static char *XMLScannerBSearch( char c, char *keys, int len )
+{
+ char *lower = keys;
+ char *mid;
+ char *upper = keys + len - 1;
+ while (1) {
+ if ( upper < lower )
+ return 0;
+
+ /* Find the midpoint. */
+ mid = lower + ((upper-lower) >> 1);
+
+ if ( c < *mid )
+ upper = mid - 1;
+ else if ( c > *mid )
+ lower = mid + 1;
+ else
+ return mid;
+ }
+}
+
+/* Binary search an array of keys looking for a key. */
+static char *XMLScannerRangeBSearch( char c, char *keys, int len )
+{
+ char *lower = keys;
+ char *mid;
+ char *upper = keys + len - 2;
+ while (1) {
+ if ( upper < lower )
+ return 0;
+
+ /* Find the midpoint. Be sure to settle on the
+ * lower end of a range. */
+ mid = lower + (((upper-lower) >> 1) & ~1);
+
+ if ( c < mid[0] )
+ upper = mid - 2;
+ else if ( c > mid[1] )
+ lower = mid + 2;
+ else {
+ /* The key was found in the range mid, return it. */
+ return mid;
+ }
+ }
+}
+
+/* Execute the fsm on some chunk of data. */
+void XMLScannerExecute( XMLScanner *fsm, char *data, int dlen )
+{
+ char *p = data;
+ int len = dlen;
+ int *cs = fsm->curState;
+ int specs, **trans, *funcs, nfuncs;
+ char *keys;
+ unsigned char *inds;
+
+ if ( data == 0 )
+ goto finishInput;
+
+again:
+ if ( cs == 0 || len == 0 )
+ goto out;
+
+ /* Get required data. */
+ specs = *cs++;
+ keys = k + *cs++;
+ inds = i + *cs++;
+
+ /* Try flat index. */
+ if ( specs & SPEC_ANY_FLAT ) {
+ int indsLen = *cs++;
+ keys += 2;
+ inds += indsLen;
+ }
+
+ /* Try binary search single. */
+ if ( specs & SPEC_ANY_SINGLE ) {
+ /* Try to find the key. */
+ int indsLen = *cs++;
+ char *match = XMLScannerBSearch( *p, keys, indsLen );
+
+ if ( match != 0 ) {
+ trans = t + (inds[match - keys]<<1);
+ goto match;
+ }
+
+ /* Advance over the keys and indicies. */
+ keys += indsLen;
+ inds += indsLen;
+ }
+
+ /* Try binary search range. */
+ if ( specs & SPEC_ANY_RANGE ) {
+ /* Try to find the key. */
+ int indsLen = *cs++;
+ char *match = XMLScannerRangeBSearch( *p, keys, (indsLen<<1) );
+
+ if ( match != 0 ) {
+ trans = t + (inds[(match - keys)>>1]<<1);
+ goto match;
+ }
+
+ /* Advance over the keys and indicies. */
+ keys += (indsLen<<1);
+ inds += indsLen;
+ }
+
+ /* Try the default transition. */
+ if ( specs & SPEC_ANY_DEF ) {
+ trans = t + ((*inds)<<1);
+ goto match;
+ }
+
+ /* No match. */
+ cs = 0;
+ goto out;
+
+match:
+ /* Move to the new state. */
+ cs = *trans++;
+
+ /* Check for functions. */
+ if ( (funcs=*trans) == 0 )
+ goto noFuncs;
+
+execFuncs:
+ nfuncs = *funcs++;
+ while ( nfuncs-- > 0 ) {
+ switch ( *funcs++ ) {
+ case 0:
+# 63 "ragel_xmlscanner.c"
+ {
+ DEBUG && fprintf(stderr, "commentstart: %d %c\n", p - data, *p);
+ fsm->in_comment = 1;
+ } break;
+ case 1:
+# 68 "ragel_xmlscanner.c"
+ {
+ DEBUG && fprintf(stderr, "commentend: %d %c\n", p - data, *p);
+ fsm->in_comment = 0;
+ fsm->in_tag = 0;
+ } break;
+ case 2:
+# 74 "ragel_xmlscanner.c"
+ {
+ fsm->in_cdata = 1;
+ } break;
+ case 3:
+# 78 "ragel_xmlscanner.c"
+ {
+ fsm->in_cdata = 0;
+ fsm->in_tag = 0;
+ } break;
+ case 4:
+# 83 "ragel_xmlscanner.c"
+ {
+ DEBUG && fprintf(stderr, "enter tag at %d\n", p - data);
+ fsm->in_tag = 1;
+ } break;
+ case 5:
+# 88 "ragel_xmlscanner.c"
+ {
+ DEBUG && fprintf(stderr, "exit tag at %d\n", p - data);
+ fsm->in_tag = 0;
+ } break;
+ case 6:
+# 93 "ragel_xmlscanner.c"
+ {
+// fprintf(stderr, "name: %d %c\n", p - data, *p);
+ } break;
+ case 7:
+# 97 "ragel_xmlscanner.c"
+ {
+ DEBUG && fprintf(stderr, "stag\n");
+ pushTag(fsm);
+ } break;
+ case 8:
+# 102 "ragel_xmlscanner.c"
+ {
+ DEBUG && fprintf(stderr, "stag: start: %d end: %d %c\n", fsm->mark_namestart - data, fsm->mark_nameend - data, *p);
+ checkETag(fsm);
+ } break;
+ case 9:
+# 107 "ragel_xmlscanner.c"
+ {
+ DEBUG && fprintf(stderr, "markstart: %d %c\n", p - data, *p);
+ fsm->mark_namestart = p;
+ } break;
+ case 10:
+# 112 "ragel_xmlscanner.c"
+ {
+ DEBUG && fprintf(stderr, "markend: %d %c\n", (p - 1) - data, *(p-1));
+ fsm->mark_nameend = p;
+ } break;
+ case 11:
+# 117 "ragel_xmlscanner.c"
+ {
+// fprintf(stderr, "attrstart: %d %c\n", p - data, *p);
+ fsm->mark_attrstart = p;
+ } break;
+ case 12:
+# 122 "ragel_xmlscanner.c"
+ {
+// fprintf(stderr, "attrend: %d %c\n", (p - 1) - data, *(p - 1));
+ fsm->mark_attrend = p;
+ } break;
+ case 13:
+# 128 "ragel_xmlscanner.c"
+ {
+ // fprintf(stderr, "attr: start: %d end: %d %c\n", fsm->mark_attrstart - data, fsm->mark_attrend - data, *p);
+} break;
+ case 14:
+# 132 "ragel_xmlscanner.c"
+ {
+ DEBUG && fprintf(stderr, "pi: %d\n", p - data);
+} break;
+# 564 "ragel_xmlscanner_out.c"
+ }
+ }
+
+noFuncs:
+ p++, len--;
+ goto again;
+
+finishInput:
+ if ( cs != 0 && *cs & SPEC_IS_FINAL ) {
+ /* The machine accepts. */
+ fsm->accept = 1;
+ /* If finishing in a final state then execute the
+ * out functions for it. (if any). */
+ if ( *cs & SPEC_OUT_FUNC ) {
+ funcs = f+*(cs + (*cs>>8)-1);
+ len = 1;
+ goto execFuncs;
+ }
+ }
+ else {
+ /* If we are not in a final state then this
+ * is an error. Move to the error state. */
+ fsm->curState = 0;
+ }
+
+out:
+ fsm->curState = cs;
+}
+
+/* Indicate to the fsm that the input is done. Does cleanup tasks. */
+void XMLScannerFinish( XMLScanner *fsm )
+{
+ XMLScannerExecute( fsm, 0, 0 );
+}
+
+#undef f
+#undef s
+#undef k
+#undef i
+#undef t
+#undef SPEC_ANY_FLAT
+#undef SPEC_ANY_SINGLE
+#undef SPEC_ANY_RANGE
+#undef SPEC_ANY_DEF
+#undef SPEC_IS_FINAL
+#undef SPEC_OUT_FUNC
+
+# 165 "ragel_xmlscanner.c"
+
+
+
+/*
+ * findCompletion does the actual work of driving the state machine
+ *
+ * Input:
+ * data - pointer to the buffer containing the XML data
+ * len - length of the data to analyze
+ * cursor - logical position of the editing cursor in the data buffer
+ * result - pointer to integer, which will contain a bit field with
+ * additional status information after completion:
+ *
+ * Completion rules:
+ *
+ * - The system builds a stack of tags which are open at the indicated cursor
+ * position in the XML data.
+ * - If completion is possible at that cursor position, the integer
+ * result will be 0. The caller should insert a closing tag for the
+ * topmost tag name on the stack.
+ * - It can use the other elements of the
+ * stack for GUI goodies, like display of a stack of currently open tags
+ * in the editor. However, it should not assume that the tag stack can be
+ * used to close all opening tags. Instead, it should re-evaluate
+ * the document with this routine after the tag has been inserted and the
+ * cursor position might have moved as a result.
+ *
+ * - There are a few cases where completion should not be performed, this
+ * will be indicated by the bitfield in the result int parameter:
+ * - ERROR_IN_TAG: The cursor is in the middle of a tag. No useful completion
+ * is possible (maybe look at the prefix and complete
+ * accordingly, sometime in the future...)
+ * - ERROR_IN_COMMENT: The cursor is inside a comment block.
+ * - ERROR_IN_CDATA: The cursor is inside a cdata section.
+ * - ERROR_NO_TAG: There's no unmatched opening tag to the left of the cursor
+ * - ERROR_ALREADY_BALANCED_HERE: The currently open tags to the left of the
+ * cursor are already balanced with an equal amount
+ * of closing tags to the right of the cursor.
+ *
+ * Results:
+ *
+ * Returns a pointer to an array of pointers to chars.
+ * Each array entry contains one tag from the stack
+ * tags open at the cursor position.
+ *
+ */
+char (*findCompletion(char *data, int len, int cursor, int *result, int tagpositions[]))[] {
+
+ XMLScanner scanner, *machine = &scanner;
+ char toptag[MAXTAGLENGTH];
+ unsigned int i, lower, upper, openingcount, closingcount, identical_opening, identical_closing;
+
+ char (*resultstack)[MAXTAGLENGTH] = NULL;
+ char (*resultstack_e)[MAXTAGLENGTH] = NULL;
+
+ *result = 0;
+
+ bzero(toptag, MAXTAGLENGTH);
+
+
+ // ------------ analyze data before cursor position ------------------
+
+ XMLScannerInit(machine);
+
+ machine->mode = BEFORE;
+ machine->cursor = cursor;
+
+ XMLScannerExecute(machine, data, cursor);
+ XMLScannerFinish(machine);
+
+
+ /* Any unbalanced open tags on the stack at all? If so, allocate memory
+ * and copy the tags over to a newly allocated buffer, into a stack.
+ */
+ if (machine->sp) {
+ resultstack = malloc(MAXTAGLENGTH * ((machine->sp/2) + 1));
+ } else {
+ /* Nothing useful to return to caller */
+ *result |= ERROR_NO_TAG;
+ return NULL;
+ }
+
+ openingcount = (machine->sp / 2);
+
+ for (i = 0; i < openingcount; i++) {
+ lower = i * 2;
+ upper = lower + 1;
+ bzero(resultstack[i], MAXTAGLENGTH);
+ strncpy(resultstack[i], machine->stack[lower], machine->stack[upper] - machine->stack[lower]);
+ if (tagpositions) {
+ tagpositions[lower] = machine->stack[lower] - data;
+ tagpositions[upper] = machine->stack[upper] - data;
+ }
+ }
+
+ /* make sure the slot after the topmost tag is zeroed out, so the
+ * calling code can use that as an end of stack marker.
+ */
+ bzero(resultstack[openingcount], MAXTAGLENGTH);
+
+ strncpy(toptag, resultstack[openingcount - 1], MAXTAGLENGTH);
+
+ if (machine->in_tag)
+ *result |= ERROR_IN_TAG;
+ if (machine->in_comment)
+ *result |= ERROR_IN_COMMENT;
+ if (machine->in_cdata)
+ *result |= ERROR_IN_CDATA;
+
+ if (*result) {
+ return resultstack;
+ }
+
+ // count identical opening tags on the top of the stack.
+ identical_opening = 1;
+ for (i = (openingcount - 2); i >= 0; i--) {
+ if (!strcmp(resultstack[i], resultstack[i + 1])) {
+ identical_opening++;
+ } else {
+ break;
+ }
+ }
+
+
+ // ------------ analyze data after cursor position ------------------
+
+// fprintf(stderr, "accept: %d\n", XMLScannerAccept(machine));
+
+
+ /* Reinitialize and reconfigure the scanner, and run it again, but this time with
+ * the data from the cursor position until the end.
+ */
+ XMLScannerInit(machine);
+ machine->mode = AFTER;
+ machine->cursor = cursor;
+ XMLScannerExecute(machine, data + cursor, len - cursor);
+ XMLScannerFinish(machine);
+
+ /* If there are no closing tags, we don't need to check if
+ * the topmost opening tags on the stacks are already balanced
+ */
+ closingcount = (machine->sp_e / 2);
+ if (!closingcount) {
+ return resultstack;
+ }
+
+ resultstack_e = malloc(MAXTAGLENGTH * ((machine->sp_e/2) + 1));
+
+ for (i = 0; i < closingcount; i++) {
+ lower = i * 2;
+ upper = lower + 1;
+
+ bzero(resultstack_e[i], MAXTAGLENGTH);
+ strncpy(resultstack_e[i], machine->stack_e[lower], machine->stack_e[upper] - machine->stack_e[lower]);
+
+ }
+
+ DEBUG && fprintf(stderr, "closing count %d\n", closingcount);
+
+ identical_closing = closingcount ? 1 : 0;
+ for (i = 0; i < closingcount - 1; i++) {
+ if (!strcmp(resultstack_e[i], resultstack_e[i + 1])) {
+ identical_closing++;
+ } else {
+ break;
+ }
+ }
+
+ DEBUG && fprintf(stderr, "identical_closing %d\n", identical_closing);
+
+ /* last opening tag before cursor and first closing tag after
+ * cursor have to be identical, otherwise we are not interested
+ * if they are balanced
+ */
+ if (strcmp(resultstack[openingcount - 1], resultstack_e[0])) {
+ free(resultstack_e);
+ return resultstack;
+ }
+
+ /* ok, the adjacent opening and closing tags are identical, so we
+ * need to know if there are less closing tags than opening tags,
+ * because only then does tag completion make any sense at this location
+ */
+
+ if (identical_opening <= identical_closing) {
+ *result |= ERROR_ALREADY_BALANCED_HERE;
+ }
+
+ free(resultstack_e);
+
+ // caller needs to free() this...
+ return resultstack;
+
+}
+
+
+
+
+
+/*
+ * Push the current start and end buffer position markers onto our
+ * stack of currently open tags. We're operating with position numbers
+ * only at this point, later there will be actual copies out of the buffer
+ */
+void pushTag(XMLScanner *fsm) {
+
+ char buffer[MAXTAGLENGTH];
+ bzero(buffer, sizeof(buffer));
+
+ if (fsm->sp + 2 >= (STACKDEPTH - 1)) {
+ fprintf(stderr, "pushTag: stack overflow at %d, can't push more!\n", STACKDEPTH);
+ return;
+ }
+
+ fsm->stack[fsm->sp++] = fsm->mark_namestart;
+ fsm->stack[fsm->sp++] = fsm->mark_nameend;
+
+ DEBUG && strncpy(buffer, fsm->stack[fsm->sp - 2], fsm->stack[fsm->sp - 1] - fsm->stack[fsm->sp - 2]);
+ DEBUG && fprintf(stderr, "pushTag: pushing '%s'\n", buffer);
+
+}
+
+
+
+/*
+ * Push the current start and end buffer position markers for
+ * *end* tags onto the stack.
+ */
+void pushETag(XMLScanner *fsm) {
+
+ char buffer[MAXTAGLENGTH];
+ bzero(buffer, sizeof(buffer));
+
+ if (fsm->sp_e + 2 >= (STACKDEPTH - 1)) {
+ fprintf(stderr, "pushETag: stack overflow at %d, can't push more!\n", STACKDEPTH);
+ return;
+ }
+
+ fsm->stack_e[fsm->sp_e++] = fsm->mark_namestart;
+ fsm->stack_e[fsm->sp_e++] = fsm->mark_nameend;
+
+ DEBUG && strncpy(buffer, fsm->stack_e[fsm->sp_e - 2], fsm->stack_e[fsm->sp_e - 1] - fsm->stack_e[fsm->sp_e - 2]);
+ DEBUG && fprintf(stderr, "pushETag: pushing '%s'\n", buffer);
+}
+
+
+
+/*
+ * pop the topmost element
+ */
+void popTag(XMLScanner *fsm) {
+
+ char buffer[MAXTAGLENGTH];
+ bzero(buffer, sizeof(buffer));
+
+ if (fsm->sp < 2) {
+ fprintf(stderr, "popTag: stack underflow, can't pop more!\n");
+ return;
+ }
+
+ fsm->sp -= 2;
+
+ DEBUG && strncpy(buffer, fsm->stack[fsm->sp], fsm->stack[fsm->sp + 1] - fsm->stack[fsm->sp]);
+ DEBUG && fprintf(stderr, "popTag: popping '%s'\n", buffer);
+}
+
+
+/*
+ * check out the end tag we just stumbled upon.
+ * the behavior is different here, according to the scan mode
+ * (before or after cursor position).
+ */
+void checkETag(XMLScanner *fsm) {
+
+ char buffer[MAXTAGLENGTH];
+ bzero(buffer, sizeof(buffer));
+ int len;
+
+ /* return if the stack is empty in BEFORE mode, we can't do anything interesting */
+ if (!fsm->sp && fsm->mode == BEFORE) {
+ return;
+ }
+
+ len = fsm->mark_nameend - fsm->mark_namestart;
+
+ strncpy(buffer, fsm->mark_namestart, len);
+
+ /* if topmost tag on stack is identical to current end tag, pop it */
+ if(fsm->sp && !strncmp(fsm->stack[fsm->sp - 2], buffer, len)) {
+// fprintf(stderr, "checkETag: popping, len = %d, etag = '%s'\n", len, buffer);
+ popTag(fsm);
+ } else {
+ pushETag(fsm);
+ }
+
+
+}
+