new files for 2.9
Marc Liyanage [Tue, 2 Sep 2003 13:35:55 +0000 (13:35 +0000)]
git-svn-id: svn+ssh://www.entropy.ch/Users/liyanage/Documents/svnroot/trunk/TestXSLT@57 153f8dbc-cef0-0310-8e0e-ba1d6c9f8c6b

XMLUtils.h [new file with mode: 0644]
XMLUtils.m [new file with mode: 0644]
ragel_xmldeclscanner.c [new file with mode: 0644]
ragel_xmldeclscanner.h [new file with mode: 0644]
ragel_xmldeclscanner_out.c [new file with mode: 0644]
ragel_xmlscanner.c [new file with mode: 0644]
ragel_xmlscanner.h [new file with mode: 0644]
ragel_xmlscanner_out.c [new file with mode: 0644]
test_ragel_xmlscanner.c [new file with mode: 0644]

diff --git a/XMLUtils.h b/XMLUtils.h
new file mode 100644 (file)
index 0000000..b11f21d
--- /dev/null
@@ -0,0 +1,23 @@
+//
+//  XMLUtils.h
+//  TestXSLT
+//
+//  Created by Marc Liyanage on Sun Aug 31 2003.
+//  Copyright (c) 2003 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#include "ragel_xmldeclscanner.h"
+
+
+@interface XMLUtils : NSObject {
+
+}
+
+
+
+
++ (NSString *)getStringWithEncodingFromFile:(NSString *)filename;
++ (NSData *)getDataWithEncodingFromString:(NSString *)text;
+
+@end
diff --git a/XMLUtils.m b/XMLUtils.m
new file mode 100644 (file)
index 0000000..8635b71
--- /dev/null
@@ -0,0 +1,67 @@
+//
+//  XMLUtils.m
+//  TestXSLT
+//
+//  Created by Marc Liyanage on Sun Aug 31 2003.
+//  Copyright (c) 2003 __MyCompanyName__. All rights reserved.
+//
+
+#import "XMLUtils.h"
+
+
+@implementation XMLUtils
+
+
++ (NSString *)getStringWithEncodingFromFile:(NSString *)filename {
+       
+       NSStringEncoding encoding;
+       NSString *fileString;
+       
+       NSData *fileContents = [NSData dataWithContentsOfFile:filename];
+       if (fileContents == nil) {
+               return @"Unable to load file!";
+       }
+       
+       encoding = getEncodingFromXmlDecl([fileContents bytes], [fileContents length]);
+       
+       if (encoding == 0)
+               encoding = [NSString defaultCStringEncoding];
+       
+       NSLog(@"picked encoding: %d", encoding);
+       
+       
+       fileString = [[[NSString alloc] initWithData:fileContents encoding:encoding] autorelease];
+       
+       if (fileString)
+               return fileString;
+       
+       if (encoding == NSUTF8StringEncoding) {
+               return @"Unable to load file, possibly invalid UTF-8 contents!";
+       } else {
+               return @"Unable to load file!";
+       }
+       
+}
+
+
+
++ (NSData *)getDataWithEncodingFromString:(NSString *)text {
+       
+       NSStringEncoding encoding;
+       
+       encoding = getEncodingFromXmlDecl([text UTF8String], [text length]);
+       
+       if (encoding == 0)
+               encoding = NSUTF8StringEncoding;
+       
+//     NSLog(@"Default output encoding UTF-8 used...");
+
+       return [text dataUsingEncoding:encoding allowLossyConversion:YES];
+
+}
+
+
+
+
+
+@end
diff --git a/ragel_xmldeclscanner.c b/ragel_xmldeclscanner.c
new file mode 100644 (file)
index 0000000..f271a00
--- /dev/null
@@ -0,0 +1,186 @@
+
+/*
+ * 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"
+
+
+%% XMLDeclScanner
+       struct {
+               char *mark_encodingstart;
+               char *mark_encodingend;
+       };
+
+%%
+
+%% XMLDeclScanner
+
+       init {
+               fsm->mark_encodingstart = fsm->mark_encodingend = NULL;
+       }
+
+
+       func encodingstart {
+               DEBUG && fprintf(stderr, "encodingstart: %d %c\n", p - data, *p);
+               fsm->mark_encodingstart = p;
+       }
+       
+       func encodingend {
+               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);
+       }
+
+
+
+       BOM = 0xef 0xbb 0xbf;
+
+#      Grammar taken straight from the XML 1.0 spec, except that EncodingDecl is not optional here
+
+       Eq = '=';
+
+       VersionNum = +(/[a-zA-Z0-9_.:]/ | '-');
+       VersionInfo = space 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"');
+
+       EncName = (/[A-Za-z]/ *(/[A-Za-z0-9._]/ | '-')) >encodingstart %encodingend;
+       EncodingDecl = space 'encoding' Eq ('"' EncName '"' | "'" EncName "'" );
+
+       SDDecl = space 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"'));
+       
+       XMLDecl = '<?xml' VersionInfo EncodingDecl ?SDDecl ?space '?>';
+
+
+       main = ?BOM XMLDecl /.*/;
+
+%%
+
+
+
+
+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;
+       
+}
+
+
+unsigned int getIANACharSetName(char *data, int len, char *destbuffer, int destbufferlen) {
+       
+       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;
+       
+       bzero(dstbuffer, dstbufferlen);
+       strncpy(dstbuffer, machine->mark_encodingstart, dstbufferlen-1 > encodinglength ? dstbufferlen-1 : encodinglength);
+       
+       
+       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;
+
+*/
+       
diff --git a/ragel_xmldeclscanner.h b/ragel_xmldeclscanner.h
new file mode 100644 (file)
index 0000000..c5e1793
--- /dev/null
@@ -0,0 +1,46 @@
+
+/*
+ * This is an input file for the "Ragel" finite state machine compiler utility.
+ *
+ * Written by Marc Liyanage <http://www.entropy.ch>
+ *
+ */
+
+/*
+ * ragel -o ragel_xmldeclscanner_out.c ragel_xmldeclscanner.c && env CFLAGS='-framework CoreFoundation' make ragel_xmldeclscanner_out
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <strings.h>
+
+
+#define DEBUG 0
+
+unsigned int getEncodingFromXmlDecl(char *data, int len);
+
+
+/*
+int main() {
+
+       char *data, *pos;
+
+       pos = data = (char *)malloc(40000);
+       int readlen = 0, i;
+       unsigned int result;
+
+
+       while ((readlen = read(0, pos, 40000)) > 0) {
+               pos += readlen;
+       }
+
+
+       result = getEncodingFromXmlDecl(data, pos - data);
+
+
+       printf("result: %d\n", result); 
+}
+*/
\ No newline at end of file
diff --git a/ragel_xmldeclscanner_out.c b/ragel_xmldeclscanner_out.c
new file mode 100644 (file)
index 0000000..266e39d
--- /dev/null
@@ -0,0 +1,508 @@
+/* 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;
+
+*/
+       
diff --git a/ragel_xmlscanner.c b/ragel_xmlscanner.c
new file mode 100644 (file)
index 0000000..962b446
--- /dev/null
@@ -0,0 +1,462 @@
+
+/*
+ * 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);  
+       }
+
+
+}
+
diff --git a/ragel_xmlscanner.h b/ragel_xmlscanner.h
new file mode 100644 (file)
index 0000000..fb1ea60
--- /dev/null
@@ -0,0 +1,69 @@
+
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <strings.h>
+
+#define STACKDEPTH 600
+#define MAXTAGLENGTH 500
+#define DEBUG 0
+
+
+/* bit field flags for the return value, to indicate state to the caller */
+enum {
+       ERROR_IN_TAG = 1,
+       ERROR_IN_COMMENT = 2,
+       ERROR_IN_CDATA = 4,
+       ERROR_NO_TAG = 8,
+       ERROR_ALREADY_BALANCED_HERE = 16
+};
+
+char (*findCompletion(char *data, int len, int cursor, int *result, int tagpositions[]))[];
+
+
+/*
+
+int main() {
+
+       char *data, *pos;
+
+       pos = data = (char *)malloc(40000);
+       int readlen = 0, i;
+       unsigned int result;
+       char (*resultstack)[MAXTAGLENGTH];
+
+       while ((readlen = read(0, pos, 40000)) > 0) {
+               pos += readlen;
+       }
+
+
+       resultstack = findCompletion(data, pos - data, 1382, &result);
+       for (i = 0; resultstack && *(resultstack[i]); i++) {
+               fprintf(stderr, "tag main (%p): '%s'\n", resultstack[i], resultstack[i]);
+       }
+
+       if (resultstack)
+               free(resultstack);
+
+       exit(0);
+
+}
+
+
+*/
+
diff --git a/ragel_xmlscanner_out.c b/ragel_xmlscanner_out.c
new file mode 100644 (file)
index 0000000..381f843
--- /dev/null
@@ -0,0 +1,909 @@
+/* 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);  
+       }
+
+
+}
+
diff --git a/test_ragel_xmlscanner.c b/test_ragel_xmlscanner.c
new file mode 100644 (file)
index 0000000..53bb0f9
--- /dev/null
@@ -0,0 +1,31 @@
+
+#include "ragel_xmlscanner.h"
+
+
+
+int main() {
+
+       char *data, *pos;
+
+       pos = data = (char *)malloc(40000);
+       int readlen = 0, i;
+       unsigned int result;
+       char (*resultstack)[MAXTAGLENGTH];
+
+       while ((readlen = read(0, pos, 40000)) > 0) {
+               pos += readlen;
+       }
+
+
+       resultstack = findCompletion(data, pos - data, 184, &result, NULL);
+       for (i = 0; resultstack && *(resultstack[i]); i++) {
+               fprintf(stderr, "tag main (%p): '%s'\n", resultstack[i], resultstack[i]);
+       }
+
+       if (resultstack)
+               free(resultstack);
+
+       exit(0);
+
+}
+