root/06/libkombilo/sgfparser.cpp

Revision 225, 25.0 kB (checked in by ug, 1 year ago)

Fix some of the small FIXME\'s

Line 
1 // File: sgfparser.cpp
2 // part of libkombilo, http://www.u-go.net/kombilo/
3
4 // Copyright (c) 2006-7 Ulrich Goertz <u@g0ertz.de>
5
6 // Permission is hereby granted, free of charge, to any person obtaining a copy of
7 // this software and associated documentation files (the "Software"), to deal in
8 // the Software without restriction, including without limitation the rights to
9 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10 // of the Software, and to permit persons to whom the Software is furnished to do
11 // so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in all
14 // copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 // SOFTWARE.
23
24 #include "sgfparser.h"
25
26 using std::vector;
27 using std::stack;
28 using std::pair;
29 using std::map;
30 using std::string;
31
32 SGFError::SGFError() {}
33
34 ExtendedMoveNumber::ExtendedMoveNumber() {
35   length = 0;
36   data = 0;
37 }
38
39 ExtendedMoveNumber::ExtendedMoveNumber(int LENGTH, int* DATA) {
40   length = LENGTH;
41   if (length) data = new int[length];
42   else data = 0;
43   for(int i=0; i<length; i++) data[i] = DATA[i];
44 }
45
46 ExtendedMoveNumber::ExtendedMoveNumber(int D) {
47   length = 1;
48   data = new int[1];
49   data[0] = D;
50 }
51
52 ExtendedMoveNumber::ExtendedMoveNumber(const ExtendedMoveNumber& emn) {
53   length = emn.length;
54   if (length) data = new int[length];
55   else data = 0;
56   for(int i=0; i<length; i++) data[i] = emn.data[i];
57 }
58
59 ExtendedMoveNumber::~ExtendedMoveNumber() {
60   if (data) delete [] data;
61 }
62
63 ExtendedMoveNumber& ExtendedMoveNumber::operator=(const ExtendedMoveNumber& emn) {
64   if (this != &emn) {
65     length = emn.length;
66     if (data) delete [] data;
67     if (length) {
68       data = new int[length];
69       for(int i=0; i<length; i++) data[i] = emn.data[i];
70     } else data = 0;
71   }
72   return *this;
73 }
74
75 void ExtendedMoveNumber::next() {
76   data[length-1]++;
77 }
78
79 void ExtendedMoveNumber::down() throw(SGFError) {
80   if (length==0) throw SGFError();
81   else if (length==1) {
82     int* newdata = new int[3];
83     newdata[0] = data[0];
84     newdata[1] = 1;
85     newdata[2] = 0;
86     length = 3;
87     delete [] data;
88     data = newdata;
89   } else {
90     if (data[length-1]) {
91       int* newdata = new int[length+2];
92       for(int i=0; i<length; i++) newdata[i] = data[i];
93       newdata[length] = 1;
94       newdata[length+1] = 0;
95       length += 2;
96       delete [] data;
97       data = newdata;
98     } else data[length-2]++;
99   }
100 }
101
102 int ExtendedMoveNumber::total_move_num() {
103   int result = 0;
104   for(int i=0; i<(length+1)/2; i++) result += data[2*i];
105   return result;
106 }
107
108 char* SGFescape(const char* s) {
109   char* t = new char[2*strlen(s)+1];
110   int j = 0;
111   for(unsigned int i = 0; i<strlen(s); i++) {
112     if (s[i] == '\\' || s[i] == ']') t[j++]='\\';
113     t[j++] = s[i];
114   }
115            
116   t[j++] = 0;
117
118   char* result = new char[j];
119   strcpy(result, t);
120   delete t;
121   return result;
122 }
123
124 vector<string>* parseRootNode(Node* n, vector<string>* tags) throw(SGFError) {
125   vector<string>* results = new vector<string>(tags->size());
126   string s = n->SGFstring;
127   int lSGFstring = s.size();
128   int i = 0;
129
130   while (i < lSGFstring && s[i] != ';' && (s[i]==' ' || s[i]=='\n' || s[i]=='\r' || s[i]=='\t'))
131     i++;
132
133   if (i>=lSGFstring || s[i] != ';') throw SGFError();
134   i++;
135
136   while (i < lSGFstring) {
137     while (i < lSGFstring && (s[i]==' ' || s[i]=='\n' || s[i]=='\r' || s[i]=='\t'))
138       i++;
139
140     if (i >= lSGFstring) break;
141
142     char ID[30];
143     int IDindex = 0;
144
145     while (i < lSGFstring && s[i] != '[' && IDindex < 30) {
146       if (65 <= s[i] && s[i] <= 90) {
147         ID[IDindex++] = s[i];
148       } else if (!(97 <= s[i] && s[i] <= 122) && (!(s[i]==' ' || s[i]=='\n' || s[i]=='\r' || s[i]=='\t'))) {
149         throw SGFError();
150       }
151       i++;
152     }
153     i++;
154
155     if (i >= lSGFstring || IDindex >= 30 || !IDindex) {
156       throw SGFError();
157     }
158     ID[IDindex] = 0;
159
160     char* propValue = new char[lSGFstring+1];
161     int propValueIndex = 0;
162
163     while (i < lSGFstring) {
164
165       while (s[i] != ']') {
166         if (s[i] == '\t') {
167           propValue[propValueIndex++] = ' ';
168           i++;
169           continue;
170         }
171         if (s[i] == '\\') {
172           i++;
173           if ((s[i]=='\n' && s[i+1]=='\r') || (s[i]=='\r' && s[i+1]=='\n')) {
174             i += 2;
175             continue;
176           }
177           else if (s[i]=='\n' || s[i]=='\r') {
178             i++;
179             continue;
180           }
181         }
182         propValue[propValueIndex++] = s[i];
183         i++;
184
185         if (i >= lSGFstring) {
186           throw SGFError();
187         }
188       }
189
190       propValue[propValueIndex++] = ',';
191       propValue[propValueIndex++] = ' ';
192
193       i++;
194       while (i < lSGFstring && (s[i]==' ' || s[i]=='\n' || s[i]=='\r' || s[i]=='\t'))
195         i++;
196       if (i >= lSGFstring || s[i] != '[') {
197         propValue[propValueIndex-2] = 0;
198         string IDstring = ID;
199         int ctr = 0;
200         for(vector<string>::iterator it = tags->begin(); it != tags->end(); it++) {
201           if (IDstring == *it) {
202             (*results)[ctr] = propValue;
203             break;
204           }
205           ctr++;
206         }
207         delete [] propValue;
208         break;
209       }
210       else i++;
211     }
212   }
213   return results;
214 }
215
216 PropValue::PropValue(std::string IDC, std::vector<std::string>* PV) {
217   IDcomplete = IDC;
218   pv = PV;
219 }
220
221 PropValue::~PropValue() {
222   if (pv) delete pv;
223 }
224
225
226 Node::Node(Node* prev, char* SGFst, int lvl=0) throw(SGFError) {
227   next = NULL;
228   previous = prev;
229   up = NULL;
230   down = NULL;
231   numChildren = 0;
232   level = lvl;
233   parsed = 0;
234
235   if (SGFst) {
236     SGFstring = SGFst;
237     // parseNode();
238   } else SGFstring = "";
239   posyD = 0;
240 }
241        
242 Node::~Node() {
243 }
244
245 string remove_lowercase(string s) throw(SGFError) {
246   char ID[s.size()+1];
247   int IDindex = 0;
248   for(unsigned int i=0; i<s.size(); i++) {
249     if (65 <= s[i] && s[i] <= 90) ID[IDindex++] = s[i];
250     else if (!(97 <= s[i] && s[i] <= 122)) {
251       throw SGFError();
252     }
253   }
254   ID[IDindex] = 0;
255   return string(ID);
256 }
257
258 vector<string> Node::gpv(const string& prop) {
259   vector<string>* result = get_property_value(prop);
260   if (result) return *result;
261   else return vector<string>();
262 }
263
264 vector<string>* Node::get_property_value(const string& prop) {
265   if (!parsed) {
266     parseNode();
267   }
268   map<string, PropValue >::iterator result = data.find(remove_lowercase(prop));
269   if (result == data.end()) return 0;
270   else return result->second.pv;
271 }
272
273 ExtendedMoveNumber Node::get_move_number() {
274   vector<int> l;
275   Node* n = this;
276   l.push_back(0);
277
278   while (n->previous) {
279     if (n->level) l.push_back(n->level);
280     else l[l.size()-1]++;
281     n = n->previous;
282   }       
283
284   int* result = new int[l.size()];
285   for(int i = l.size()-1; i >= 0; i--) {
286     result[l.size()-i-1] = l[i];
287   }
288   ExtendedMoveNumber emn(l.size(), result);
289   delete [] result;
290   return emn;
291 }
292
293
294 void Node::parseNode() throw(SGFError) {
295   // printf("Parse node, %s\n", SGFstring);
296   if (!parsed) {
297     string s = SGFstring;
298     int lSGFstring = s.size();
299     int i = 0;
300
301     while (i < lSGFstring && s[i] != ';' && (s[i]==' ' || s[i]=='\n' || s[i]=='\r' || s[i]=='\t'))
302       i++;
303
304     if (i>=lSGFstring || s[i] != ';'throw SGFError();
305     i++;
306
307     while (i < lSGFstring) {
308       while (i < lSGFstring && (s[i]==' ' || s[i]=='\n' || s[i]=='\r' || s[i]=='\t'))
309         i++;
310
311       if (i >= lSGFstring) break;
312
313       char ID[30];
314       int IDindex = 0;
315       char IDcomplete[100]; // store long property name here
316       int IDcompleteIndex = 0;
317
318       while (i < lSGFstring && s[i] != '[' && IDindex < 30 && IDcompleteIndex < 100) {
319         if (65 <= s[i] && s[i] <= 90) {
320           ID[IDindex++] = s[i];
321           IDcomplete[IDcompleteIndex++] = s[i];
322         } else if (97 <= s[i] && s[i] <= 122) {
323           IDcomplete[IDcompleteIndex++] = s[i];
324         } else if (!(s[i]==' ' || s[i]=='\n' || s[i]=='\r' || s[i]=='\t')) {
325           throw SGFError();
326         }
327         i++;
328       }
329       i++;
330
331       if (i >= lSGFstring || IDindex >= 30 || !IDindex || IDcompleteIndex >= 100) {
332         throw SGFError();
333       }
334       ID[IDindex] = 0;
335       IDcomplete[IDcompleteIndex] = 0;
336       vector<string>* propValueList = new vector<string>;
337
338       while (i < lSGFstring) {
339         string propValue;
340
341         while (s[i] != ']') {
342           //printf("i, s[i]: %d, %c\n", i, s[i]);
343           if (s[i] == '\t') {
344             propValue += ' ';
345             i++;
346             continue;
347           }
348           if (s[i] == '\\') {
349             i++;
350
351             if ((s[i]=='\n' && s[i+1]=='\r') || (s[i]=='\r' && s[i+1]=='\n')) {
352               i += 2;
353               continue;
354             }
355             else if (s[i]=='\n' || s[i]=='\r') {
356               i++;
357               continue;
358             }
359           }
360           if (Node::sloppy && (s[i] == '\n' || s[i] == '\r') && \
361               (!strcmp(ID, "B") || !strcmp(ID, "W") || !strcmp(ID, "AW") || !strcmp(ID, "AB"))) {
362               i++;
363               continue;
364           }
365
366           propValue += s[i]; // building propValue in this way could be a performance problem
367                              // maybe we should use reserve before. FIXME
368           i++;
369
370           if (i >= lSGFstring) throw SGFError();
371         }
372         if ((!strcmp(ID,"B") || !strcmp(ID,"W")) && !(propValue.size() == 2 || (propValue.size() == 0))) {
373           throw SGFError();
374         }
375
376         propValueList->push_back(propValue);
377         i++;
378
379         while (i < lSGFstring && (s[i]==' ' || s[i]=='\n' || s[i]=='\r' || s[i]=='\t'))
380           i++;
381         if (i >= lSGFstring || s[i] != '[') break;
382         else i++;
383       }
384       data.insert(make_pair(string(ID), PropValue(string(IDcomplete), propValueList)));
385     }
386     parsed = 1;
387   }
388 }   
389
390 void Node::set_property_value(string& IDcomplete, vector<string>* propValue) throw(SGFError) {
391   string ID = remove_lowercase(IDcomplete);
392   map<string, PropValue >::iterator it = data.find(ID);
393   if (it == data.end()) data.insert(make_pair(ID, PropValue(IDcomplete, propValue)));
394   else it->second.pv->insert(it->second.pv->end(), propValue->begin(), propValue->end());
395   SGFstring = nodeToString(data);
396   parsed = 1;
397 }
398    
399
400 int Node::sloppy = 1;
401
402 Cursor::Cursor(const char* sgf, int sloppy) throw(SGFError) {
403   Node::sloppy = sloppy;
404
405   height = 0;
406   width = 0;
407   posx = 0;
408   posy = 0;
409
410   root = new Node(NULL, NULL, 0);
411   parse(sgf);
412
413   currentN = root->next;
414   setFlags();       
415 }
416
417 Cursor::~Cursor() {
418   deltree(root);
419 }
420
421 void Cursor::setFlags() {
422   if (currentN->next) atEnd = 0;
423   else atEnd = 1;
424   if (currentN->previous) atStart = 0;
425   else atStart = 1;
426 }
427
428 void Cursor::parse(const char* s) throw(SGFError) {
429
430   Node* curr = root;       
431   int p = -1;           // start of the currently parsed node
432   stack<Node* > c;       // stack of nodes from which variations started
433   stack<int> c_width;
434   stack<int> c_height;
435
436   int height_previous = 0;
437   int width_currentVar = 0;
438
439   char last = ')';      // type of last parsed bracked ('(' or ')')
440   bool inbrackets = false;   // are the currently parsed characters in []'s?
441
442   int i = 0;  // current parser position
443   int lSGFstring = strlen(s);
444
445   int found_par = 0;
446   while (i < lSGFstring) {
447     if (s[i]=='(') {
448       found_par = i+1;
449       i++;
450       continue;
451     }
452     if (found_par && s[i]==';') break;
453
454     if (found_par && !(s[i]==' ' || s[i]=='\n' || s[i]=='\r' || s[i]=='\t'))
455       found_par = 0;
456     i++;
457   }
458
459   if (i >= lSGFstring) throw SGFError();
460
461   i = found_par-1; // found beginning of SGF file
462
463   while (i < lSGFstring) {
464     while (i < lSGFstring && !(s[i]=='(' || s[i]==')' || s[i]=='[' || s[i]==']' || s[i]==';')) i++;
465     if (i >= lSGFstring) break;
466
467     if (inbrackets) {
468       if (s[i]==']') {
469         int numberBackslashes = 0;
470         int j = i-1;
471         while (s[j] == '\\') {
472           numberBackslashes++;
473           j--;
474         }
475         if (!(numberBackslashes % 2))
476           inbrackets = 0;
477       }
478       i++;
479       continue;
480     }
481
482     if (s[i] == '[') inbrackets = 1;
483
484     if (s[i] == '(') {
485       if (last != ')' && p != -1) curr->SGFstring = string(s+p, i-p);
486
487       Node* nn = new Node(0,0,0);
488       nn->previous = curr;
489
490       if (++width_currentVar > width) width = width_currentVar;
491       if (curr->next) {
492         Node* last = curr->next;
493         while (last->down) last = last->down;
494         nn->up = last;                                 
495         last->down = nn;
496         nn->level = last->level + 1;
497         height++;
498         nn->posyD = height - height_previous;
499       }
500       else {
501         curr->next = nn;
502         nn->posyD = 0;
503         height_previous = height;
504       }
505
506       curr->numChildren++;
507
508       c.push(curr);
509       c_width.push(width_currentVar-1);
510       c_height.push(height);
511
512       curr = nn;
513
514       p = -1;
515       last = '(';
516     }
517
518     if (s[i] == ')') {
519       if (last != ')' && p != -1) {
520         curr->SGFstring = string(s+p, i-p);       
521       }
522       if (c.size()) {
523         curr = c.top();
524         c.pop();
525         width_currentVar = c_width.top();
526         c_width.pop();
527         height_previous = c_height.top();
528         c_height.pop();
529       }
530       else throw SGFError();
531       last = ')';
532     }
533
534     if (s[i] == ';') {
535       if (p != -1) {
536         curr->SGFstring = string(s+p, i-p);       
537
538         Node* nn = new Node(0,0,0);
539         nn->previous = curr;
540
541         if (++width_currentVar > width) width = width_currentVar;
542         nn->posyD = 0;
543         curr->numChildren = 1;
544         curr->next = nn;
545         curr = nn;
546       }
547       p = i;
548     }
549
550     i++;
551   }
552
553   if (inbrackets || c.size()) throw SGFError();
554
555   Node* n = curr->next;
556   n->previous = NULL;
557   n->up = NULL;
558
559   while (n->down) {
560     n = n->down;
561     n->previous = NULL;
562   }
563 }
564
565 void Cursor::game(int n) throw(SGFError) {
566   if (n < root->numChildren) {
567     posx = 0;
568     posy = 0;
569     currentN = root->next;
570     for(int i=0; i<n; i++) currentN = currentN->down;
571     setFlags();
572   }
573   else throw SGFError();
574 }
575
576 void Cursor::deltree(Node* node) {
577   Node* n;
578   if (node->next) {
579     n = node->next;
580     while (n->down) {
581       n = n->down;
582       deltree(n->up);
583     }
584     deltree(n);
585   }
586   delete node;
587 }
588
589 void Cursor::delVariation(Node* c) {
590   if (c->previous) {
591     delVar(c);
592   }
593   else {
594     if (c->next) {
595       Node* node = c->next;
596       while (node->down) {
597         node = node->down;
598         delVar(node->up);
599       } 
600       delVar(node);
601     }
602     c->next = 0;
603   }
604
605   setFlags();
606 }
607
608 void Cursor::delVar(Node* node) {
609
610   if (node->up) node->up->down = node->down;
611   else {
612     node->previous->next = node->down;
613   }
614   if (node->down) {
615     node->down->up = node->up;
616     node->down->posyD = node->posyD;
617     Node* n = node->down;
618     while (n) {
619       n->level--;
620       n = n->down;
621     }
622   }
623
624   int h = 0;
625   Node* n = node;
626   while (n->next) {
627     n = n->next;
628     while (n->down) {
629       n = n->down;
630       h += n->posyD;
631     }
632   }
633
634   if (node->up || node->down) h++;
635
636   Node* p = node->previous;
637   p->numChildren--;
638
639   while (p) {
640     if (p->down) p->down->posyD -= h;
641     p = p->previous;
642   }
643
644   height -= h;
645
646   // p = node->down;
647   deltree(node);
648   // node = 0;
649 }
650
651
652 void Cursor::add(char* st) {
653
654   Node* node = new Node(currentN,st,0);
655
656   node->down = 0;
657   node->next = 0;
658   node->numChildren = 0;
659
660   if (!currentN->next) {
661     // printf("new %s at %s\n", node->SGFstring, currentN->SGFstring);
662     node->level = 0;
663     node->posyD = 0;
664     node->up = 0;
665
666     currentN->next = node;
667     currentN->numChildren = 1;
668   }
669   else {
670     // printf("adding %s at %s\n", node->SGFstring, currentN->SGFstring);
671     Node* n = currentN->next;
672     while (n->down) {
673       n = n->down;
674       posy+=n->posyD;
675     }
676
677     n->down = node;
678     node->up = n;
679     node->level = n->level + 1;
680     node->next= 0;
681     currentN->numChildren++;
682
683     node->posyD = 1;
684     while (n->next) {
685       n = n->next;
686       while (n->down) {
687         n = n->down;
688         node->posyD += n->posyD;
689       }
690     }
691     posy += node->posyD;
692
693     height++;
694
695     n = node;
696     while (n->previous) {
697       n = n->previous;
698       if (n->down) n->down->posyD++;
699     }
700
701   }
702
703   currentN = node;
704
705   posx++;
706   setFlags();
707
708   if (posx > width) width++;
709 }
710
711
712 void Cursor::next(int n) throw(SGFError) {
713
714   if (n >= currentN->numChildren) {
715     throw SGFError();
716   }
717   posx++;
718   currentN = currentN->next;
719   for (int i=0; i<n; i++) {
720     currentN = currentN->down;
721     posy += currentN->posyD;
722   }
723   setFlags();
724 }
725    
726 void Cursor::previous() throw(SGFError) {
727   if (currentN->previous) {
728     while (currentN->up) {
729       posy -= currentN->posyD;
730       currentN = currentN->up;
731     }
732     currentN = currentN->previous;
733     posx--;
734   }
735   else throw SGFError();
736   setFlags();
737 }
738
739 Node* Cursor::getRootNode(int n) throw(SGFError) {
740   if (!root) return 0;
741
742   if (n >= root->numChildren) throw SGFError();
743   Node* nn = root->next;
744   for(int i=0; i<n; i++) nn = nn->down; 
745  
746   if (!nn->parsed) nn->parseNode();
747   return nn;
748 }
749
750
751 // void Cursor::updateRootNode(PyObject* data, int n) throw(SGFError) {
752 //   if (n >= root->numChildren) throw SGFError();
753 //   Node* nn = root->next;
754 //   for(int i=0; i<n; i++) nn = nn->down;
755 //   delete[] nn->SGFstring;
756 //   nn->SGFstring = rootNodeToString(data);
757 //   Py_DECREF(nn->data);
758 //   if (!(nn->data=PyDict_New())) throw SGFError();
759 //   nn->parsed = 0;
760 //   nn->parseNode();
761 // }
762
763
764 // char* Cursor::rootNodeToString(PyObject* data) {
765 //   char result[10000]; // FIXME check whether this is exceeded, on the way
766 //   result[0] = 0;
767 //   strcat(result, ";");
768 //   
769 //   PyObject* item;
770 //   if ((item=PyDict_GetItem(data, PyString_FromString("GM")))) {
771 //     strcat(result, "GM[");
772 //     char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
773 //     strcat(result, t);
774 //     delete[] t;
775 //     strcat(result, "]\n");
776 //   }
777 //   if ((item=PyDict_GetItem(data, PyString_FromString("FF")))) {
778 //     strcat(result, "FF[");
779 //     char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
780 //     strcat(result, t);
781 //     delete[] t;
782 //     strcat(result, "]\n");
783 //   }
784 //   if ((item=PyDict_GetItem(data, PyString_FromString("SZ")))) {
785 //     strcat(result, "SZ[");
786 //     char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
787 //     strcat(result, t);
788 //     delete[] t;
789 //     strcat(result, "]\n");
790 //   }
791 //   if ((item=PyDict_GetItem(data, PyString_FromString("PW")))) {
792 //     strcat(result, "PW[");
793 //     char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
794 //     strcat(result, t);
795 //     delete[] t;
796 //     strcat(result, "]\n");
797 //   }
798 //   if ((item=PyDict_GetItem(data, PyString_FromString("WR")))) {
799 //     strcat(result, "WR[");
800 //     char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
801 //     strcat(result, t);
802 //     delete[] t;
803 //     strcat(result, "]\n");
804 //   }
805 //   if ((item=PyDict_GetItem(data, PyString_FromString("PB")))) {
806 //     strcat(result, "PB[");
807 //     char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
808 //     strcat(result, t);
809 //     delete[] t;
810 //     strcat(result, "]\n");
811 //   }
812 //   if ((item=PyDict_GetItem(data, PyString_FromString("BR")))) {
813 //     strcat(result, "BR[");
814 //     char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
815 //     strcat(result, t);
816 //     delete[] t;
817 //     strcat(result, "]\n");
818 //   }
819 //   if ((item=PyDict_GetItem(data, PyString_FromString("EV")))) {
820 //     strcat(result, "EV[");
821 //     char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
822 //     strcat(result, t);
823 //     delete[] t;
824 //     strcat(result, "]\n");
825 //   }
826 //   if ((item=PyDict_GetItem(data, PyString_FromString("RO")))) {
827 //     strcat(result, "RO[");
828 //     char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
829 //     strcat(result, t);
830 //     delete[] t;
831 //     strcat(result, "]\n");
832 //   }
833 //   if ((item=PyDict_GetItem(data, PyString_FromString("DT")))) {
834 //     strcat(result, "DT[");
835 //     char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
836 //     strcat(result, t);
837 //     delete[] t;
838 //     strcat(result, "]\n");
839 //   }
840 //   if ((item=PyDict_GetItem(data, PyString_FromString("PC")))) {
841 //     strcat(result, "PC[");
842 //     char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
843 //     strcat(result, t);
844 //     delete[] t;
845 //     strcat(result, "]\n");
846 //   }
847 //   if ((item=PyDict_GetItem(data, PyString_FromString("KM")))) {
848 //     strcat(result, "KM[");
849 //     char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
850 //     strcat(result, t);
851 //     delete[] t;
852 //     strcat(result, "]\n");
853 //   }
854 //   if ((item=PyDict_GetItem(data, PyString_FromString("RE")))) {
855 //     strcat(result, "RE[");
856 //     char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
857 //     strcat(result, t);
858 //     delete[] t;
859 //     strcat(result, "]\n");
860 //   }
861 //   if ((item=PyDict_GetItem(data, PyString_FromString("US")))) {
862 //     strcat(result, "US[");
863 //     char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
864 //     strcat(result, t);
865 //     delete[] t;
866 //     strcat(result, "]\n");
867 //   }
868 //   if ((item=PyDict_GetItem(data, PyString_FromString("GC")))) {
869 //     strcat(result, "GC[");
870 //     char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
871 //     strcat(result, t);
872 //     delete[] t;
873 //     strcat(result, "]\n");
874 //   }
875 //
876 //   int l = 0;
877 //
878 //   PyObject *key, *value;
879 //   int pos = 0;
880 //
881 //   while (PyDict_Next(data, &pos, &key, &value)) {
882 //     char* s = PyString_AsString(key);
883 //     if (strcmp(s, "GM") && strcmp(s, "FF") && strcmp(s, "SZ") && strcmp(s, "PW") && strcmp(s, "WR") &&
884 //   strcmp(s, "PB") && strcmp(s, "BR") && strcmp(s, "EV") && strcmp(s, "RO") && strcmp(s, "DT") &&
885 //   strcmp(s, "PC") && strcmp(s, "KM") && strcmp(s, "RE") && strcmp(s, "US") && strcmp(s, "GC")) {
886 //       
887 //       strcat(result, s);
888 //
889 //       for(int k = 0; k < PyList_Size(value); k++) {
890 //   PyObject* item = PyList_GetItem(value, k);
891 //   char* t = SGFescape(PyString_AsString(item));
892 //   strcat(result, "[");
893 //   strcat(result, t);
894 //   strcat(result, "]");
895 //   l += strlen(t) + 2;
896 //   delete[] t;
897 //   if (l>72) {
898 //     strcat(result, "\n");
899 //     l = 0;
900 //   }
901 //       }
902 //     }
903 //   }
904 //   strcat(result, "\n");
905 //   char* t = new char[strlen(result)+1];
906 //   strcpy(t, result);
907 //   return t;
908 // }
909  
910 string nodeToString(map<string, PropValue >& data) throw(SGFError) {
911   string result = ";";
912   int l = 0;
913   for(map<string, PropValue >::iterator kv = data.begin(); kv != data.end(); kv++) {
914     if (!kv->second.pv || !kv->second.pv->size()) continue;
915
916     result += kv->second.IDcomplete;
917     for(vector<string>::iterator it = kv->second.pv->begin(); it != kv->second.pv->end(); it++) {
918       char* t = SGFescape(it->c_str());
919       result += "[";
920       result += t;
921       result += "]";
922       l += strlen(t) + 2;
923       delete [] t;
924       if (l>72) {
925         result += '\n';
926         l = 0;
927       }
928     }
929   }
930   result += '\n';
931   return result;
932 }
933
934
935 char* Cursor::outputVar(Node* node) {
936   int s = 1000;
937   char* result = new char[s];
938   result[0] = 0;
939
940   if ((int)(node->SGFstring.size() + strlen(result)) >= s-5) {
941     s += 1000 + node->SGFstring.size();
942