remove references to BKE_utildefines where its not needed.
[blender-staging.git] / source / blender / blenkernel / intern / text.c
1 /* text.c
2  *
3  *
4  * $Id$
5  *
6  * ***** BEGIN GPL LICENSE BLOCK *****
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL LICENSE BLOCK *****
30  */
31
32 #include <string.h> /* strstr */
33 #include <sys/types.h>
34 #include <sys/stat.h>
35
36 #include "MEM_guardedalloc.h"
37
38 #include "BLI_blenlib.h"
39 #include "BLI_utildefines.h"
40
41 #include "DNA_constraint_types.h"
42 #include "DNA_controller_types.h"
43 #include "DNA_scene_types.h"
44 #include "DNA_screen_types.h"
45 #include "DNA_space_types.h"
46 #include "DNA_text_types.h"
47 #include "DNA_userdef_types.h"
48 #include "DNA_object_types.h"
49
50 #include "BKE_depsgraph.h"
51 #include "BKE_global.h"
52 #include "BKE_library.h"
53 #include "BKE_main.h"
54 #include "BKE_text.h"
55
56
57 #ifdef WITH_PYTHON
58 #include "BPY_extern.h"
59 #endif
60
61 /***************/ /*
62
63 How Texts should work
64 --
65 A text should relate to a file as follows -
66 (Text *)->name should be the place where the 
67         file will or has been saved.
68         
69 (Text *)->flags has the following bits
70         TXT_ISDIRTY - should always be set if the file in mem. differs from
71                                         the file on disk, or if there is no file on disk.
72         TXT_ISMEM - should always be set if the Text has not been mapped to
73                                         a file, in which case (Text *)->name may be NULL or garbage.                    
74         TXT_ISEXT - should always be set if the Text is not to be written into
75                                         the .blend
76         TXT_ISSCRIPT - should be set if the user has designated the text
77                                         as a script. (NEW: this was unused, but now it is needed by
78                                         space handler script links (see header_view3d.c, for example)
79
80 ->>> see also: /makesdna/DNA_text_types.h
81
82 Display
83 --
84 The st->top determines at what line the top of the text is displayed.
85 If the user moves the cursor the st containing that cursor should
86 be popped ... other st's retain their own top location.
87
88 Markers
89 --
90 The mrk->flags define the behaviour and relationships between markers. The
91 upper two bytes are used to hold a group ID, the lower two are normal flags. If
92 TMARK_EDITALL is set the group ID defines which other markers should be edited.
93
94 The mrk->clr field is used to visually group markers where the flags may not
95 match. A template system, for example, may allow editing of repeating tokens
96 (in one group) but include other marked positions (in another group) all in the
97 same template with the same color.
98
99 Undo
100 --
101 Undo/Redo works by storing
102 events in a queue, and a pointer
103 to the current position in the
104 queue...
105
106 Events are stored using an
107 arbitrary op-code system
108 to keep track of
109 a) the two cursors (normal and selected)
110 b) input (visible and control (ie backspace))
111
112 input data is stored as its
113 ASCII value, the opcodes are
114 then selected to not conflict.
115
116 opcodes with data in between are
117 written at the beginning and end
118 of the data to allow undo and redo
119 to simply check the code at the current
120 undo position
121
122 */ /***************/
123
124 /***/
125
126 static void txt_pop_first(Text *text);
127 static void txt_pop_last(Text *text);
128 static void txt_undo_add_op(Text *text, int op);
129 static void txt_undo_add_block(Text *text, int op, const char *buf);
130 static void txt_delete_line(Text *text, TextLine *line);
131 static void txt_delete_sel (Text *text);
132 static void txt_make_dirty (Text *text);
133
134 /***/
135
136 static unsigned char undoing;
137
138 /* allow to switch off undoing externally */
139 void txt_set_undostate(int u)
140 {
141         undoing = u;
142 }
143
144 int txt_get_undostate(void)
145 {
146         return undoing;
147 }
148
149 static void init_undo_text(Text *text)
150 {
151         text->undo_pos= -1;
152         text->undo_len= TXT_INIT_UNDO;
153         text->undo_buf= MEM_mallocN(text->undo_len, "undo buf");
154 }
155
156 void free_text(Text *text)
157 {
158         TextLine *tmp;
159
160         for (tmp= text->lines.first; tmp; tmp= tmp->next) {
161                 MEM_freeN(tmp->line);
162                 if (tmp->format)
163                   MEM_freeN(tmp->format);
164         }
165         
166         BLI_freelistN(&text->lines);
167         BLI_freelistN(&text->markers);
168
169         if(text->name) MEM_freeN(text->name);
170         MEM_freeN(text->undo_buf);
171 #ifdef WITH_PYTHON
172         if (text->compiled) BPY_text_free_code(text);
173 #endif
174 }
175
176 Text *add_empty_text(const char *name) 
177 {
178         Main *bmain= G.main;
179         Text *ta;
180         TextLine *tmp;
181         
182         ta= alloc_libblock(&bmain->text, ID_TXT, name);
183         ta->id.us= 1;
184         
185         ta->name= NULL;
186
187         init_undo_text(ta);
188
189         ta->nlines=1;
190         ta->flags= TXT_ISDIRTY | TXT_ISMEM;
191         if((U.flag & USER_TXT_TABSTOSPACES_DISABLE)==0)
192                 ta->flags |= TXT_TABSTOSPACES;
193
194         ta->lines.first= ta->lines.last= NULL;
195         ta->markers.first= ta->markers.last= NULL;
196
197         tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
198         tmp->line= (char*) MEM_mallocN(1, "textline_string");
199         tmp->format= NULL;
200         
201         tmp->line[0]=0;
202         tmp->len= 0;
203                                 
204         tmp->next= NULL;
205         tmp->prev= NULL;
206                                 
207         BLI_addhead(&ta->lines, tmp);
208         
209         ta->curl= ta->lines.first;
210         ta->curc= 0;
211         ta->sell= ta->lines.first;
212         ta->selc= 0;
213
214         return ta;
215 }
216
217 // this function removes any control characters from
218 // a textline
219
220 static void cleanup_textline(TextLine * tl)
221 {
222         int i;
223
224         for (i = 0; i < tl->len; i++ ) {
225                 if (tl->line[i] < ' ' && tl->line[i] != '\t') {
226                         memmove(tl->line + i, tl->line + i + 1, tl->len - i);
227                         tl->len--;
228                         i--;
229                 }
230         }
231 }
232
233 int reopen_text(Text *text)
234 {
235         FILE *fp;
236         int i, llen, len, res;
237         unsigned char *buffer;
238         TextLine *tmp;
239         char str[FILE_MAXDIR+FILE_MAXFILE];
240         struct stat st;
241
242         if (!text || !text->name) return 0;
243         
244         BLI_strncpy(str, text->name, FILE_MAXDIR+FILE_MAXFILE);
245         BLI_path_abs(str, G.main->name);
246         
247         fp= fopen(str, "r");
248         if(fp==NULL) return 0;
249
250         /* free memory: */
251
252         for (tmp= text->lines.first; tmp; tmp= tmp->next) {
253                 MEM_freeN(tmp->line);
254                 if (tmp->format) MEM_freeN(tmp->format);
255         }
256         
257         BLI_freelistN(&text->lines);
258
259         text->lines.first= text->lines.last= NULL;
260         text->curl= text->sell= NULL;
261
262         /* clear undo buffer */
263         MEM_freeN(text->undo_buf);
264         init_undo_text(text);
265         
266         fseek(fp, 0L, SEEK_END);
267         len= ftell(fp);
268         fseek(fp, 0L, SEEK_SET);        
269
270         text->undo_pos= -1;
271         
272         buffer= MEM_mallocN(len, "text_buffer");
273         // under windows fread can return less then len bytes because
274         // of CR stripping
275         len = fread(buffer, 1, len, fp);
276
277         fclose(fp);
278
279         res= stat(str, &st);
280         text->mtime= st.st_mtime;
281         
282         text->nlines=0;
283         i=0;
284         llen=0;
285         for(i=0; i<len; i++) {
286                 if (buffer[i]=='\n') {
287                         tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
288                         tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
289                         tmp->format= NULL;
290                         
291                         if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
292                         tmp->line[llen]=0;
293                         tmp->len= llen;
294                                 
295                         cleanup_textline(tmp);
296
297                         BLI_addtail(&text->lines, tmp);
298                         text->nlines++;
299                                 
300                         llen=0;
301                         continue;
302                 }
303                 llen++;
304         }
305
306         if (llen!=0 || text->nlines==0) {
307                 tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
308                 tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
309                 tmp->format= NULL;
310                 
311                 if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
312
313                 tmp->line[llen]=0;
314                 tmp->len= llen;
315                 
316                 cleanup_textline(tmp);
317
318                 BLI_addtail(&text->lines, tmp);
319                 text->nlines++;
320         }
321         
322         text->curl= text->sell= text->lines.first;
323         text->curc= text->selc= 0;
324         
325         MEM_freeN(buffer);      
326         return 1;
327 }
328
329 Text *add_text(const char *file, const char *relpath) 
330 {
331         Main *bmain= G.main;
332         FILE *fp;
333         int i, llen, len, res;
334         unsigned char *buffer;
335         TextLine *tmp;
336         Text *ta;
337         char str[FILE_MAXDIR+FILE_MAXFILE];
338         struct stat st;
339
340         BLI_strncpy(str, file, FILE_MAXDIR+FILE_MAXFILE);
341         if (relpath) /* can be NULL (bg mode) */
342                 BLI_path_abs(str, relpath);
343         
344         fp= fopen(str, "r");
345         if(fp==NULL) return NULL;
346         
347         ta= alloc_libblock(&bmain->text, ID_TXT, BLI_path_basename(str));
348         ta->id.us= 1;
349
350         ta->lines.first= ta->lines.last= NULL;
351         ta->markers.first= ta->markers.last= NULL;
352         ta->curl= ta->sell= NULL;
353
354         if((U.flag & USER_TXT_TABSTOSPACES_DISABLE)==0)
355                 ta->flags= TXT_TABSTOSPACES;
356
357         fseek(fp, 0L, SEEK_END);
358         len= ftell(fp);
359         fseek(fp, 0L, SEEK_SET);        
360
361         ta->name= MEM_mallocN(strlen(file)+1, "text_name");
362         strcpy(ta->name, file);
363
364         init_undo_text(ta);
365         
366         buffer= MEM_mallocN(len, "text_buffer");
367         // under windows fread can return less then len bytes because
368         // of CR stripping
369         len = fread(buffer, 1, len, fp);
370
371         fclose(fp);
372
373         res= stat(str, &st);
374         ta->mtime= st.st_mtime;
375         
376         ta->nlines=0;
377         i=0;
378         llen=0;
379         for(i=0; i<len; i++) {
380                 if (buffer[i]=='\n') {
381                         tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
382                         tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
383                         tmp->format= NULL;
384                         
385                         if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
386                         tmp->line[llen]=0;
387                         tmp->len= llen;
388                         
389                         cleanup_textline(tmp);
390
391                         BLI_addtail(&ta->lines, tmp);
392                         ta->nlines++;
393                                 
394                         llen=0;
395                         continue;
396                 }
397                 llen++;
398         }
399
400         if (llen!=0 || ta->nlines==0) {
401                 tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
402                 tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
403                 tmp->format= NULL;
404                 
405                 if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
406
407                 tmp->line[llen]=0;
408                 tmp->len= llen;
409                 
410                 cleanup_textline(tmp);
411
412                 BLI_addtail(&ta->lines, tmp);
413                 ta->nlines++;
414         }
415         
416         ta->curl= ta->sell= ta->lines.first;
417         ta->curc= ta->selc= 0;
418         
419         MEM_freeN(buffer);      
420
421         return ta;
422 }
423
424 Text *copy_text(Text *ta)
425 {
426         Text *tan;
427         TextLine *line, *tmp;
428         
429         tan= copy_libblock(ta);
430         
431         /* file name can be NULL */
432         if(ta->name) {
433                 tan->name= MEM_mallocN(strlen(ta->name)+1, "text_name");
434                 strcpy(tan->name, ta->name);
435         }
436         else {
437                 tan->name= NULL;
438         }
439
440         tan->flags = ta->flags | TXT_ISDIRTY;
441         
442         tan->lines.first= tan->lines.last= NULL;
443         tan->markers.first= tan->markers.last= NULL;
444         tan->curl= tan->sell= NULL;
445         
446         tan->nlines= ta->nlines;
447
448         line= ta->lines.first;  
449         /* Walk down, reconstructing */
450         while (line) {
451                 tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
452                 tmp->line= MEM_mallocN(line->len+1, "textline_string");
453                 tmp->format= NULL;
454                 
455                 strcpy(tmp->line, line->line);
456
457                 tmp->len= line->len;
458                 
459                 BLI_addtail(&tan->lines, tmp);
460                 
461                 line= line->next;
462         }
463
464         tan->curl= tan->sell= tan->lines.first;
465         tan->curc= tan->selc= 0;
466
467         init_undo_text(tan);
468
469         return tan;
470 }
471
472 void unlink_text(Main *bmain, Text *text)
473 {
474         bScreen *scr;
475         ScrArea *area;
476         SpaceLink *sl;
477         Scene *scene;
478         Object *ob;
479         bController *cont;
480         bConstraint *con;
481         short update;
482
483         /* dome */
484         for(scene=bmain->scene.first; scene; scene=scene->id.next)
485                 if(scene->r.dometext == text)
486                         scene->r.dometext = NULL;
487
488         for(ob=bmain->object.first; ob; ob=ob->id.next) {
489                 /* game controllers */
490                 for(cont=ob->controllers.first; cont; cont=cont->next) {
491                         if(cont->type==CONT_PYTHON) {
492                                 bPythonCont *pc;
493                                 
494                                 pc= cont->data;
495                                 if(pc->text==text) pc->text= NULL;
496                         }
497                 }
498
499                 /* pyconstraints */
500                 update = 0;
501
502                 if(ob->type==OB_ARMATURE && ob->pose) {
503                         bPoseChannel *pchan;
504                         for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
505                                 for(con = pchan->constraints.first; con; con=con->next) {
506                                         if(con->type==CONSTRAINT_TYPE_PYTHON) {
507                                                 bPythonConstraint *data = con->data;
508                                                 if (data->text==text) data->text = NULL;
509                                                 update = 1;
510                                                 
511                                         }
512                                 }
513                         }
514                 }
515
516                 for(con = ob->constraints.first; con; con=con->next) {
517                         if(con->type==CONSTRAINT_TYPE_PYTHON) {
518                                 bPythonConstraint *data = con->data;
519                                 if (data->text==text) data->text = NULL;
520                                 update = 1;
521                         }
522                 }
523                 
524                 if(update)
525                         DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
526         }
527
528         /* pynodes */
529         // XXX nodeDynamicUnlinkText(&text->id);
530         
531         /* text space */
532         for(scr= bmain->screen.first; scr; scr= scr->id.next) {
533                 for(area= scr->areabase.first; area; area= area->next) {
534                         for(sl= area->spacedata.first; sl; sl= sl->next) {
535                                 if(sl->spacetype==SPACE_TEXT) {
536                                         SpaceText *st= (SpaceText*) sl;
537                                         
538                                         if(st->text==text) {
539                                                 st->text= NULL;
540                                                 st->top= 0;
541                                         }
542                                 }
543                         }
544                 }
545         }
546
547         text->id.us= 0;
548 }
549
550 void clear_text(Text *text) /* called directly from rna */
551 {
552         int oldstate;
553
554         oldstate = txt_get_undostate(  );
555         txt_set_undostate( 1 );
556         txt_sel_all( text );
557         txt_delete_sel(text);
558         txt_set_undostate( oldstate );
559
560         txt_make_dirty(text);
561 }
562
563 void write_text(Text *text, const char *str) /* called directly from rna */
564 {
565         int oldstate;
566
567         oldstate = txt_get_undostate(  );
568         txt_insert_buf( text, str );
569         txt_move_eof( text, 0 );
570         txt_set_undostate( oldstate );
571
572         txt_make_dirty(text);
573 }
574
575 /*****************************/
576 /* Editing utility functions */
577 /*****************************/
578
579 static void make_new_line (TextLine *line, char *newline) 
580 {
581         if (line->line) MEM_freeN(line->line);
582         if (line->format) MEM_freeN(line->format);
583         
584         line->line= newline;
585         line->len= strlen(newline);
586         line->format= NULL;
587 }
588
589 static TextLine *txt_new_line(const char *str)
590 {
591         TextLine *tmp;
592
593         if(!str) str= "";
594         
595         tmp= (TextLine *) MEM_mallocN(sizeof(TextLine), "textline");
596         tmp->line= MEM_mallocN(strlen(str)+1, "textline_string");
597         tmp->format= NULL;
598         
599         strcpy(tmp->line, str);
600         
601         tmp->len= strlen(str);
602         tmp->next= tmp->prev= NULL;
603         
604         return tmp;
605 }
606
607 static TextLine *txt_new_linen(const char *str, int n)
608 {
609         TextLine *tmp;
610
611         tmp= (TextLine *) MEM_mallocN(sizeof(TextLine), "textline");
612         tmp->line= MEM_mallocN(n+1, "textline_string");
613         tmp->format= NULL;
614         
615         BLI_strncpy(tmp->line, (str)? str: "", n+1);
616         
617         tmp->len= strlen(tmp->line);
618         tmp->next= tmp->prev= NULL;
619         
620         return tmp;
621 }
622
623 void txt_clean_text (Text *text) 
624 {       
625         TextLine **top, **bot;
626         
627         if (!text) return;
628         
629         if (!text->lines.first) {
630                 if (text->lines.last) text->lines.first= text->lines.last;
631                 else text->lines.first= text->lines.last= txt_new_line(NULL);
632         } 
633         
634         if (!text->lines.last) text->lines.last= text->lines.first;
635
636         top= (TextLine **) &text->lines.first;
637         bot= (TextLine **) &text->lines.last;
638         
639         while ((*top)->prev) *top= (*top)->prev;
640         while ((*bot)->next) *bot= (*bot)->next;
641
642         if(!text->curl) {
643                 if(text->sell) text->curl= text->sell;
644                 else text->curl= text->lines.first;
645                 text->curc= 0;
646         }
647
648         if(!text->sell) {
649                 text->sell= text->curl;
650                 text->selc= 0;
651         }
652 }
653
654 int txt_get_span (TextLine *from, TextLine *to)
655 {
656         int ret=0;
657         TextLine *tmp= from;
658
659         if (!to || !from) return 0;
660         if (from==to) return 0;
661
662         /* Look forwards */
663         while (tmp) {
664                 if (tmp == to) return ret;
665                 ret++;
666                 tmp= tmp->next;
667         }
668
669         /* Look backwards */
670         if (!tmp) {
671                 tmp= from;
672                 ret=0;
673                 while(tmp) {
674                         if (tmp == to) break;
675                         ret--;
676                         tmp= tmp->prev;
677                 }
678                 if(!tmp) ret=0;
679         }
680
681         return ret;     
682 }
683
684 static void txt_make_dirty (Text *text)
685 {
686         text->flags |= TXT_ISDIRTY;
687 #ifdef WITH_PYTHON
688         if (text->compiled) BPY_text_free_code(text);
689 #endif
690 }
691
692 /* 0:whitespace, 1:punct, 2:alphanumeric */
693 static short txt_char_type (char ch)
694 {
695         if (ch <= ' ') return 0; /* 32 */
696         if (ch <= '/') return 1; /* 47 */
697         if (ch <= '9') return 2; /* 57 */
698         if (ch <= '@') return 1; /* 64 */
699         if (ch <= 'Z') return 2; /* 90 */
700         if (ch == '_') return 2; /* 95, dont delimit '_' */
701         if (ch <= '`') return 1; /* 96 */
702         if (ch <= 'z') return 2; /* 122 */
703         return 1;
704 }
705
706 /****************************/
707 /* Cursor utility functions */
708 /****************************/
709
710 static void txt_curs_cur (Text *text, TextLine ***linep, int **charp)
711 {
712         *linep= &text->curl; *charp= &text->curc;
713 }
714
715 static void txt_curs_sel (Text *text, TextLine ***linep, int **charp)
716 {
717         *linep= &text->sell; *charp= &text->selc;
718 }
719
720 static void txt_curs_first (Text *text, TextLine **linep, int *charp)
721 {
722         if (text->curl==text->sell) {
723                 *linep= text->curl;
724                 if (text->curc<text->selc) *charp= text->curc;
725                 else *charp= text->selc;
726         } else if (txt_get_span(text->lines.first, text->curl)<txt_get_span(text->lines.first, text->sell)) {
727                 *linep= text->curl;
728                 *charp= text->curc;
729         } else {
730                 *linep= text->sell;
731                 *charp= text->selc;             
732         }
733 }
734
735 /****************************/
736 /* Cursor movement functions */
737 /****************************/
738
739 void txt_move_up(Text *text, short sel)
740 {
741         TextLine **linep;
742         int *charp, old;
743         
744         if (!text) return;
745         if(sel) txt_curs_sel(text, &linep, &charp);
746         else { txt_pop_first(text); txt_curs_cur(text, &linep, &charp); }
747         if (!*linep) return;
748         old= *charp;
749
750         if((*linep)->prev) {
751                 *linep= (*linep)->prev;
752                 if (*charp > (*linep)->len) {
753                         *charp= (*linep)->len;
754                         if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, (*linep)->next), old, txt_get_span(text->lines.first, *linep), (unsigned short) *charp);
755                 } else {
756                         if(!undoing) txt_undo_add_op(text, sel?UNDO_SUP:UNDO_CUP);
757                 }
758         } else {
759                 txt_move_bol(text, sel);
760         }
761
762         if(!sel) txt_pop_sel(text);
763 }
764
765 void txt_move_down(Text *text, short sel) 
766 {
767         TextLine **linep;
768         int *charp, old;
769         
770         if (!text) return;
771         if(sel) txt_curs_sel(text, &linep, &charp);
772         else { txt_pop_last(text); txt_curs_cur(text, &linep, &charp); }
773         if (!*linep) return;
774         old= *charp;
775
776         if((*linep)->next) {
777                 *linep= (*linep)->next;
778                 if (*charp > (*linep)->len) {
779                         *charp= (*linep)->len;
780                         if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, (*linep)->prev), old, txt_get_span(text->lines.first, *linep), (unsigned short)*charp);
781                 } else
782                         if(!undoing) txt_undo_add_op(text, sel?UNDO_SDOWN:UNDO_CDOWN);  
783         } else {
784                 txt_move_eol(text, sel);
785         }
786
787         if(!sel) txt_pop_sel(text);
788 }
789
790 void txt_move_left(Text *text, short sel) 
791 {
792         TextLine **linep;
793         int *charp, oundoing= undoing;
794         
795         if (!text) return;
796         if(sel) txt_curs_sel(text, &linep, &charp);
797         else { txt_pop_first(text); txt_curs_cur(text, &linep, &charp); }
798         if (!*linep) return;
799
800         undoing= 1;
801         if (*charp== 0) {
802                 if ((*linep)->prev) {
803                         txt_move_up(text, sel);
804                         *charp= (*linep)->len;
805                 }
806         } else {
807                 (*charp)--;
808         }
809         undoing= oundoing;
810         if(!undoing) txt_undo_add_op(text, sel?UNDO_SLEFT:UNDO_CLEFT);
811         
812         if(!sel) txt_pop_sel(text);
813 }
814
815 void txt_move_right(Text *text, short sel) 
816 {
817         TextLine **linep;
818         int *charp, oundoing= undoing;
819         
820         if (!text) return;
821         if(sel) txt_curs_sel(text, &linep, &charp);
822         else { txt_pop_last(text); txt_curs_cur(text, &linep, &charp); }
823         if (!*linep) return;
824
825         undoing= 1;
826         if (*charp== (*linep)->len) {
827                 if ((*linep)->next) {
828                         txt_move_down(text, sel);
829                         *charp= 0;
830                 }
831         } else {
832                 (*charp)++;
833         }
834         undoing= oundoing;
835         if(!undoing) txt_undo_add_op(text, sel?UNDO_SRIGHT:UNDO_CRIGHT);
836
837         if(!sel) txt_pop_sel(text);
838 }
839
840 void txt_jump_left(Text *text, short sel)
841 {
842         TextLine **linep, *oldl;
843         int *charp, oldc, count, i;
844         unsigned char oldu;
845
846         if (!text) return;
847         if(sel) txt_curs_sel(text, &linep, &charp);
848         else { txt_pop_first(text); txt_curs_cur(text, &linep, &charp); }
849         if (!*linep) return;
850
851         oldl= *linep;
852         oldc= *charp;
853         oldu= undoing;
854         undoing= 1; /* Don't push individual moves to undo stack */
855
856         count= 0;
857         for (i=0; i<3; i++) {
858                 if (count < 2) {
859                         while (*charp>0 && txt_char_type((*linep)->line[*charp-1])==i) {
860                                 txt_move_left(text, sel);
861                                 count++;
862                         }
863                 }
864         }
865         if (count==0) txt_move_left(text, sel);
866
867         undoing= oldu;
868         if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, oldl), oldc, txt_get_span(text->lines.first, *linep), (unsigned short)*charp);
869 }
870
871 void txt_jump_right(Text *text, short sel)
872 {
873         TextLine **linep, *oldl;
874         int *charp, oldc, count, i;
875         unsigned char oldu;
876
877         if (!text) return;
878         if(sel) txt_curs_sel(text, &linep, &charp);
879         else { txt_pop_last(text); txt_curs_cur(text, &linep, &charp); }
880         if (!*linep) return;
881
882         oldl= *linep;
883         oldc= *charp;
884         oldu= undoing;
885         undoing= 1; /* Don't push individual moves to undo stack */
886
887         count= 0;
888         for (i=0; i<3; i++) {
889                 if (count < 2) {
890                         while (*charp<(*linep)->len && txt_char_type((*linep)->line[*charp])==i) {
891                                 txt_move_right(text, sel);
892                                 count++;
893                         }
894                 }
895         }
896         if (count==0) txt_move_right(text, sel);
897
898         undoing= oldu;
899         if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, oldl), oldc, txt_get_span(text->lines.first, *linep), (unsigned short)*charp);
900 }
901
902 void txt_move_bol (Text *text, short sel) 
903 {
904         TextLine **linep;
905         int *charp, old;
906         
907         if (!text) return;
908         if(sel) txt_curs_sel(text, &linep, &charp);
909         else txt_curs_cur(text, &linep, &charp);
910         if (!*linep) return;
911         old= *charp;
912         
913         *charp= 0;
914
915         if(!sel) txt_pop_sel(text);
916         if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, *linep), old, txt_get_span(text->lines.first, *linep), (unsigned short)*charp);
917 }
918
919 void txt_move_eol (Text *text, short sel) 
920 {
921         TextLine **linep;
922         int *charp, old;
923         
924         if (!text) return;
925         if(sel) txt_curs_sel(text, &linep, &charp);
926         else txt_curs_cur(text, &linep, &charp);
927         if (!*linep) return;
928         old= *charp;
929                 
930         *charp= (*linep)->len;
931
932         if(!sel) txt_pop_sel(text);
933         if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, *linep), old, txt_get_span(text->lines.first, *linep), (unsigned short)*charp);
934 }
935
936 void txt_move_bof (Text *text, short sel)
937 {
938         TextLine **linep;
939         int *charp, old;
940         
941         if (!text) return;
942         if(sel) txt_curs_sel(text, &linep, &charp);
943         else txt_curs_cur(text, &linep, &charp);
944         if (!*linep) return;
945         old= *charp;
946
947         *linep= text->lines.first;
948         *charp= 0;
949
950         if(!sel) txt_pop_sel(text);
951         if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, *linep), old, txt_get_span(text->lines.first, *linep), (unsigned short)*charp);
952 }
953
954 void txt_move_eof (Text *text, short sel)
955 {
956         TextLine **linep;
957         int *charp, old;
958         
959         if (!text) return;
960         if(sel) txt_curs_sel(text, &linep, &charp);
961         else txt_curs_cur(text, &linep, &charp);
962         if (!*linep) return;
963         old= *charp;
964
965         *linep= text->lines.last;
966         *charp= (*linep)->len;
967
968         if(!sel) txt_pop_sel(text);
969         if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, *linep), old, txt_get_span(text->lines.first, *linep), (unsigned short)*charp);     
970 }
971
972 void txt_move_toline (Text *text, unsigned int line, short sel)
973 {
974         txt_move_to(text, line, 0, sel);
975 }
976
977 void txt_move_to (Text *text, unsigned int line, unsigned int ch, short sel)
978 {
979         TextLine **linep, *oldl;
980         int *charp, oldc;
981         unsigned int i;
982         
983         if (!text) return;
984         if(sel) txt_curs_sel(text, &linep, &charp);
985         else txt_curs_cur(text, &linep, &charp);
986         if (!*linep) return;
987         oldc= *charp;
988         oldl= *linep;
989         
990         *linep= text->lines.first;
991         for (i=0; i<line; i++) {
992                 if ((*linep)->next) *linep= (*linep)->next;
993                 else break;
994         }
995         if (ch>(unsigned int)((*linep)->len))
996                 ch= (unsigned int)((*linep)->len);
997         *charp= ch;
998         
999         if(!sel) txt_pop_sel(text);
1000         if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, oldl), oldc, txt_get_span(text->lines.first, *linep), (unsigned short)*charp);
1001 }
1002
1003 /****************************/
1004 /* Text selection functions */
1005 /****************************/
1006
1007 static void txt_curs_swap (Text *text)
1008 {
1009         TextLine *tmpl;
1010         int tmpc;
1011                 
1012         tmpl= text->curl;
1013         text->curl= text->sell;
1014         text->sell= tmpl;
1015         
1016         tmpc= text->curc;
1017         text->curc= text->selc;
1018         text->selc= tmpc;
1019         
1020         if(!undoing) txt_undo_add_op(text, UNDO_SWAP);
1021 }
1022
1023 static void txt_pop_first (Text *text)
1024 {
1025                         
1026         if (txt_get_span(text->curl, text->sell)<0 ||
1027                 (text->curl==text->sell && text->curc>text->selc)) {    
1028                 txt_curs_swap(text);
1029         }
1030
1031         if(!undoing) txt_undo_add_toop(text, UNDO_STO,
1032                 txt_get_span(text->lines.first, text->sell), 
1033                 text->selc, 
1034                 txt_get_span(text->lines.first, text->curl), 
1035                 text->curc);            
1036         
1037         txt_pop_sel(text);
1038 }
1039
1040 static void txt_pop_last (Text *text)
1041 {
1042         if (txt_get_span(text->curl, text->sell)>0 ||
1043                 (text->curl==text->sell && text->curc<text->selc)) {
1044                 txt_curs_swap(text);
1045         }
1046
1047         if(!undoing) txt_undo_add_toop(text, UNDO_STO,
1048                 txt_get_span(text->lines.first, text->sell), 
1049                 text->selc, 
1050                 txt_get_span(text->lines.first, text->curl), 
1051                 text->curc);            
1052         
1053         txt_pop_sel(text);
1054 }
1055
1056 /* never used: CVS 1.19 */
1057 /*  static void txt_pop_selr (Text *text) */
1058
1059 void txt_pop_sel (Text *text)
1060 {
1061         text->sell= text->curl;
1062         text->selc= text->curc; 
1063 }
1064
1065 void txt_order_cursors(Text *text)
1066 {
1067         if (!text) return;
1068         if (!text->curl) return;
1069         if (!text->sell) return;
1070         
1071                 /* Flip so text->curl is before text->sell */
1072         if (txt_get_span(text->curl, text->sell)<0 ||
1073                         (text->curl==text->sell && text->curc>text->selc))
1074                 txt_curs_swap(text);
1075 }
1076
1077 int txt_has_sel(Text *text)
1078 {
1079         return ((text->curl!=text->sell) || (text->curc!=text->selc));
1080 }
1081
1082 static void txt_delete_sel (Text *text)
1083 {
1084         TextLine *tmpl;
1085         TextMarker *mrk;
1086         char *buf;
1087         int move, lineno;
1088         
1089         if (!text) return;
1090         if (!text->curl) return;
1091         if (!text->sell) return;
1092
1093         if (!txt_has_sel(text)) return;
1094         
1095         txt_order_cursors(text);
1096
1097         if(!undoing) {
1098                 buf= txt_sel_to_buf(text);
1099                 txt_undo_add_block(text, UNDO_DBLOCK, buf);
1100                 MEM_freeN(buf);
1101         }
1102
1103         buf= MEM_mallocN(text->curc+(text->sell->len - text->selc)+1, "textline_string");
1104         
1105         if (text->curl != text->sell) {
1106                 txt_clear_marker_region(text, text->curl, text->curc, text->curl->len, 0, 0);
1107                 move= txt_get_span(text->curl, text->sell);
1108         } else {
1109                 mrk= txt_find_marker_region(text, text->curl, text->curc, text->selc, 0, 0);
1110                 if (mrk && (mrk->start > text->curc || mrk->end < text->selc))
1111                         txt_clear_marker_region(text, text->curl, text->curc, text->selc, 0, 0);
1112                 move= 0;
1113         }
1114
1115         mrk= txt_find_marker_region(text, text->sell, text->selc-1, text->sell->len, 0, 0);
1116         if (mrk) {
1117                 lineno= mrk->lineno;
1118                 do {
1119                         mrk->lineno -= move;
1120                         if (mrk->start > text->curc) mrk->start -= text->selc - text->curc;
1121                         mrk->end -= text->selc - text->curc;
1122                         mrk= mrk->next;
1123                 } while (mrk && mrk->lineno==lineno);
1124         }
1125
1126         strncpy(buf, text->curl->line, text->curc);
1127         strcpy(buf+text->curc, text->sell->line + text->selc);
1128         buf[text->curc+(text->sell->len - text->selc)]=0;
1129
1130         make_new_line(text->curl, buf);
1131         
1132         tmpl= text->sell;
1133         while (tmpl != text->curl) {
1134                 tmpl= tmpl->prev;
1135                 if (!tmpl) break;
1136                 
1137                 txt_delete_line(text, tmpl->next);
1138         }
1139         
1140         text->sell= text->curl;
1141         text->selc= text->curc;
1142 }
1143
1144 void txt_sel_all (Text *text)
1145 {
1146         if (!text) return;
1147
1148         text->curl= text->lines.first;
1149         text->curc= 0;
1150         
1151         text->sell= text->lines.last;
1152         text->selc= text->sell->len;
1153 }
1154
1155 void txt_sel_line (Text *text)
1156 {
1157         if (!text) return;
1158         if (!text->curl) return;
1159         
1160         text->curc= 0;
1161         text->sell= text->curl;
1162         text->selc= text->sell->len;
1163 }
1164
1165 /***************************/
1166 /* Cut and paste functions */
1167 /***************************/
1168
1169 char *txt_to_buf (Text *text)
1170 {
1171         int length;
1172         TextLine *tmp, *linef, *linel;
1173         int charf, charl;
1174         char *buf;
1175         
1176         if (!text) return NULL;
1177         if (!text->curl) return NULL;
1178         if (!text->sell) return NULL;
1179         if (!text->lines.first) return NULL;
1180
1181         linef= text->lines.first;
1182         charf= 0;
1183                 
1184         linel= text->lines.last;
1185         charl= linel->len;
1186
1187         if (linef == text->lines.last) {
1188                 length= charl-charf;
1189
1190                 buf= MEM_mallocN(length+2, "text buffer");
1191                 
1192                 BLI_strncpy(buf, linef->line + charf, length+1);
1193                 buf[length]=0;
1194         } else {
1195                 length= linef->len - charf;
1196                 length+= charl;
1197                 length+= 2; /* For the 2 '\n' */
1198                 
1199                 tmp= linef->next;
1200                 while (tmp && tmp!= linel) {
1201                         length+= tmp->len+1;
1202                         tmp= tmp->next;
1203                 }
1204                 
1205                 buf= MEM_mallocN(length+1, "cut buffer");
1206
1207                 strncpy(buf, linef->line + charf, linef->len-charf);
1208                 length= linef->len - charf;
1209                 
1210                 buf[length++]='\n';
1211                 
1212                 tmp= linef->next;
1213                 while (tmp && tmp!=linel) {
1214                         strncpy(buf+length, tmp->line, tmp->len);
1215                         length+= tmp->len;
1216                         
1217                         buf[length++]='\n';                     
1218                         
1219                         tmp= tmp->next;
1220                 }
1221                 strncpy(buf+length, linel->line, charl);
1222                 length+= charl;
1223                 
1224                 /* python compiler wants an empty end line */
1225                 buf[length++]='\n';                     
1226                 buf[length]=0;
1227         }
1228         
1229         return buf;
1230 }
1231
1232 int txt_find_string(Text *text, char *findstr, int wrap)
1233 {
1234         TextLine *tl, *startl;
1235         char *s= NULL;
1236         int oldcl, oldsl, oldcc, oldsc;
1237
1238         if (!text || !text->curl || !text->sell) return 0;
1239         
1240         txt_order_cursors(text);
1241
1242         oldcl= txt_get_span(text->lines.first, text->curl);
1243         oldsl= txt_get_span(text->lines.first, text->sell);
1244         tl= startl= text->sell;
1245         oldcc= text->curc;
1246         oldsc= text->selc;
1247         
1248         s= strstr(&tl->line[text->selc], findstr);
1249         while (!s) {
1250                 tl= tl->next;
1251                 if (!tl) {
1252                         if (wrap)
1253                                 tl= text->lines.first;
1254                         else
1255                                 break;
1256                 }
1257
1258                 s= strstr(tl->line, findstr);
1259                 if (tl==startl)
1260                         break;
1261         }
1262         
1263         if (s) {
1264                 int newl= txt_get_span(text->lines.first, tl);
1265                 int newc= (int)(s-tl->line);
1266                 txt_move_to(text, newl, newc, 0);
1267                 txt_move_to(text, newl, newc + strlen(findstr), 1);
1268                 return 1;                               
1269         } else
1270                 return 0;
1271 }
1272
1273 char *txt_sel_to_buf (Text *text)
1274 {
1275         char *buf;
1276         int length=0;
1277         TextLine *tmp, *linef, *linel;
1278         int charf, charl;
1279         
1280         if (!text) return NULL;
1281         if (!text->curl) return NULL;
1282         if (!text->sell) return NULL;
1283         
1284         if (text->curl==text->sell) {
1285                 linef= linel= text->curl;
1286                 
1287                 if (text->curc < text->selc) {
1288                         charf= text->curc;
1289                         charl= text->selc;
1290                 } else{
1291                         charf= text->selc;
1292                         charl= text->curc;
1293                 }
1294         } else if (txt_get_span(text->curl, text->sell)<0) {
1295                 linef= text->sell;
1296                 linel= text->curl;
1297
1298                 charf= text->selc;              
1299                 charl= text->curc;
1300         } else {
1301                 linef= text->curl;
1302                 linel= text->sell;
1303                 
1304                 charf= text->curc;
1305                 charl= text->selc;
1306         }
1307
1308         if (linef == linel) {
1309                 length= charl-charf;
1310
1311                 buf= MEM_mallocN(length+1, "sel buffer");
1312                 
1313                 BLI_strncpy(buf, linef->line + charf, length+1);
1314         } else {
1315                 length+= linef->len - charf;
1316                 length+= charl;
1317                 length++; /* For the '\n' */
1318                 
1319                 tmp= linef->next;
1320                 while (tmp && tmp!= linel) {
1321                         length+= tmp->len+1;
1322                         tmp= tmp->next;
1323                 }
1324                 
1325                 buf= MEM_mallocN(length+1, "sel buffer");
1326                 
1327                 strncpy(buf, linef->line+ charf, linef->len-charf);
1328                 length= linef->len-charf;
1329                 
1330                 buf[length++]='\n';
1331                 
1332                 tmp= linef->next;
1333                 while (tmp && tmp!=linel) {
1334                         strncpy(buf+length, tmp->line, tmp->len);
1335                         length+= tmp->len;
1336                         
1337                         buf[length++]='\n';                     
1338                         
1339                         tmp= tmp->next;
1340                 }
1341                 strncpy(buf+length, linel->line, charl);
1342                 length+= charl;
1343                 
1344                 buf[length]=0;
1345         }       
1346
1347         return buf;
1348 }
1349
1350 void txt_insert_buf(Text *text, const char *in_buffer)
1351 {
1352         int i=0, l=0, j, u, len;
1353         TextLine *add;
1354
1355         if (!text) return;
1356         if (!in_buffer) return;
1357
1358         txt_delete_sel(text);
1359         
1360         if(!undoing) txt_undo_add_block (text, UNDO_IBLOCK, in_buffer);         
1361
1362         u= undoing;
1363         undoing= 1;
1364
1365         /* Read the first line (or as close as possible */
1366         while (in_buffer[i] && in_buffer[i]!='\n') {
1367                 txt_add_char(text, in_buffer[i]);
1368                 i++;
1369         }
1370         
1371         if (in_buffer[i]=='\n') txt_split_curline(text);
1372         else { undoing = u; return; }
1373         i++;
1374
1375         /* Read as many full lines as we can */
1376         len= strlen(in_buffer);
1377
1378         while (i<len) {
1379                 l=0;
1380
1381                 while (in_buffer[i] && in_buffer[i]!='\n') {
1382                         i++; l++;
1383                 }
1384         
1385                 if(in_buffer[i]=='\n') {
1386                         add= txt_new_linen(in_buffer +(i-l), l);
1387                         BLI_insertlinkbefore(&text->lines, text->curl, add);
1388                         i++;
1389                 } else {
1390                         for (j= i-l; j<i && j<(int)strlen(in_buffer); j++) {
1391                                 txt_add_char(text, in_buffer[j]);
1392                         }
1393                         break;
1394                 }
1395         }
1396         
1397         undoing= u;
1398 }
1399
1400 /******************/
1401 /* Undo functions */
1402 /******************/
1403
1404 static int max_undo_test(Text *text, int x)
1405 {
1406         while (text->undo_pos+x >= text->undo_len) {
1407                 if(text->undo_len*2 > TXT_MAX_UNDO) {
1408                         /* XXX error("Undo limit reached, buffer cleared\n"); */
1409                         MEM_freeN(text->undo_buf);
1410                         init_undo_text(text);
1411                         return 0;
1412                 } else {
1413                         void *tmp= text->undo_buf;
1414                         text->undo_buf= MEM_callocN(text->undo_len*2, "undo buf");
1415                         memcpy(text->undo_buf, tmp, text->undo_len);
1416                         text->undo_len*=2;
1417                         MEM_freeN(tmp);
1418                 }
1419         }
1420
1421         return 1;
1422 }
1423
1424 static void dump_buffer(Text *text) 
1425 {
1426         int i= 0;
1427         
1428         while (i++<text->undo_pos) printf("%d: %d %c\n", i, text->undo_buf[i], text->undo_buf[i]);
1429 }
1430
1431 void txt_print_undo(Text *text)
1432 {
1433         int i= 0;
1434         int op;
1435         const char *ops;
1436         int linep, charp;
1437         
1438         dump_buffer(text);
1439         
1440         printf ("---< Undo Buffer >---\n");
1441         
1442         printf ("UndoPosition is %d\n", text->undo_pos);
1443         
1444         while (i<=text->undo_pos) {
1445                 op= text->undo_buf[i];
1446                 
1447                 if (op==UNDO_CLEFT) {
1448                         ops= "Cursor left";
1449                 } else if (op==UNDO_CRIGHT) {
1450                         ops= "Cursor right";
1451                 } else if (op==UNDO_CUP) {
1452                         ops= "Cursor up";
1453                 } else if (op==UNDO_CDOWN) {
1454                         ops= "Cursor down";
1455                 } else if (op==UNDO_SLEFT) {
1456                         ops= "Selection left";
1457                 } else if (op==UNDO_SRIGHT) {
1458                         ops= "Selection right";
1459                 } else if (op==UNDO_SUP) {
1460                         ops= "Selection up";
1461                 } else if (op==UNDO_SDOWN) {
1462                         ops= "Selection down";
1463                 } else if (op==UNDO_STO) {
1464                         ops= "Selection ";
1465                 } else if (op==UNDO_CTO) {
1466                         ops= "Cursor ";
1467                 } else if (op==UNDO_INSERT) {
1468                         ops= "Insert";
1469                 } else if (op==UNDO_BS) {
1470                         ops= "Backspace";
1471                 } else if (op==UNDO_DEL) {
1472                         ops= "Delete";
1473                 } else if (op==UNDO_SWAP) {
1474                         ops= "Cursor swap";
1475                 } else if (op==UNDO_DBLOCK) {
1476                         ops= "Delete text block";
1477                 } else if (op==UNDO_IBLOCK) {
1478                         ops= "Insert text block";
1479                 } else if (op==UNDO_INDENT) {
1480                         ops= "Indent ";
1481                 } else if (op==UNDO_UNINDENT) {
1482                         ops= "Unindent ";
1483                 } else if (op==UNDO_COMMENT) {
1484                         ops= "Comment ";
1485                 } else if (op==UNDO_UNCOMMENT) {
1486                         ops= "Uncomment ";
1487                 } else {
1488                         ops= "Unknown";
1489                 }
1490                 
1491                 printf ("Op (%o) at %d = %s", op, i, ops);
1492                 if (op==UNDO_INSERT || op==UNDO_BS || op==UNDO_DEL) {
1493                         i++;
1494                         printf (" - Char is %c", text->undo_buf[i]);  
1495                         i++;
1496                 } else if (op==UNDO_STO || op==UNDO_CTO) {
1497                         i++;
1498
1499                         charp= text->undo_buf[i]; i++;
1500                         charp= charp+(text->undo_buf[i]<<8); i++;
1501
1502                         linep= text->undo_buf[i]; i++;
1503                         linep= linep+(text->undo_buf[i]<<8); i++;
1504                         linep= linep+(text->undo_buf[i]<<16); i++;
1505                         linep= linep+(text->undo_buf[i]<<24); i++;
1506                         
1507                         printf ("to <%d, %d> ", linep, charp);
1508
1509                         charp= text->undo_buf[i]; i++;
1510                         charp= charp+(text->undo_buf[i]<<8); i++;
1511
1512                         linep= text->undo_buf[i]; i++;
1513                         linep= linep+(text->undo_buf[i]<<8); i++;
1514                         linep= linep+(text->undo_buf[i]<<16); i++;
1515                         linep= linep+(text->undo_buf[i]<<24); i++;
1516                         
1517                         printf ("from <%d, %d>", linep, charp);
1518                 } else if (op==UNDO_DBLOCK || op==UNDO_IBLOCK) {
1519                         i++;
1520
1521                         linep= text->undo_buf[i]; i++;
1522                         linep= linep+(text->undo_buf[i]<<8); i++;
1523                         linep= linep+(text->undo_buf[i]<<16); i++;
1524                         linep= linep+(text->undo_buf[i]<<24); i++;
1525                         
1526                         printf (" (length %d) <", linep);
1527                         
1528                         while (linep>0) {
1529                                 putchar(text->undo_buf[i]);
1530                                 linep--; i++;
1531                         }
1532                         
1533                         linep= text->undo_buf[i]; i++;
1534                         linep= linep+(text->undo_buf[i]<<8); i++;
1535                         linep= linep+(text->undo_buf[i]<<16); i++;
1536                         linep= linep+(text->undo_buf[i]<<24); i++;
1537                         printf ("> (%d)", linep);
1538                 } else if (op==UNDO_INDENT || op==UNDO_UNINDENT) {
1539                         i++;
1540
1541                         charp= text->undo_buf[i]; i++;
1542                         charp= charp+(text->undo_buf[i]<<8); i++;
1543
1544                         linep= text->undo_buf[i]; i++;
1545                         linep= linep+(text->undo_buf[i]<<8); i++;
1546                         linep= linep+(text->undo_buf[i]<<16); i++;
1547                         linep= linep+(text->undo_buf[i]<<24); i++;
1548                         
1549                         printf ("to <%d, %d> ", linep, charp);
1550
1551                         charp= text->undo_buf[i]; i++;
1552                         charp= charp+(text->undo_buf[i]<<8); i++;
1553
1554                         linep= text->undo_buf[i]; i++;
1555                         linep= linep+(text->undo_buf[i]<<8); i++;
1556                         linep= linep+(text->undo_buf[i]<<16); i++;
1557                         linep= linep+(text->undo_buf[i]<<24); i++;
1558                         
1559                         printf ("from <%d, %d>", linep, charp);
1560                 }
1561                 
1562                 printf (" %d\n",  i);
1563                 i++;
1564         }
1565 }
1566
1567 static void txt_undo_add_op(Text *text, int op)
1568 {
1569         if(!max_undo_test(text, 2))
1570                 return;
1571         
1572         text->undo_pos++;
1573         text->undo_buf[text->undo_pos]= op;
1574         text->undo_buf[text->undo_pos+1]= 0;
1575 }
1576
1577 static void txt_undo_add_block(Text *text, int op, const char *buf)
1578 {
1579         int length;
1580         
1581         length= strlen(buf);
1582         
1583         if(!max_undo_test(text, length+11))
1584                 return;
1585
1586         text->undo_pos++;
1587         text->undo_buf[text->undo_pos]= op;
1588         
1589         text->undo_pos++;
1590         text->undo_buf[text->undo_pos]= (length)&0xff;
1591         text->undo_pos++;
1592         text->undo_buf[text->undo_pos]= (length>>8)&0xff;
1593         text->undo_pos++;
1594         text->undo_buf[text->undo_pos]= (length>>16)&0xff;
1595         text->undo_pos++;
1596         text->undo_buf[text->undo_pos]= (length>>24)&0xff;
1597
1598         text->undo_pos++;
1599         strncpy(text->undo_buf+text->undo_pos, buf, length);
1600         text->undo_pos+=length;
1601
1602         text->undo_buf[text->undo_pos]= (length)&0xff;
1603         text->undo_pos++;
1604         text->undo_buf[text->undo_pos]= (length>>8)&0xff;
1605         text->undo_pos++;
1606         text->undo_buf[text->undo_pos]= (length>>16)&0xff;
1607         text->undo_pos++;
1608         text->undo_buf[text->undo_pos]= (length>>24)&0xff;
1609
1610         text->undo_pos++;
1611         text->undo_buf[text->undo_pos]= op;
1612         
1613         text->undo_buf[text->undo_pos+1]= 0;
1614 }
1615
1616 void txt_undo_add_toop(Text *text, int op, unsigned int froml, unsigned short fromc, unsigned int tol, unsigned short toc)
1617 {
1618         if(!max_undo_test(text, 15))
1619                 return;
1620
1621         if (froml==tol && fromc==toc) return;
1622
1623         text->undo_pos++;
1624         text->undo_buf[text->undo_pos]= op;
1625
1626         text->undo_pos++;
1627         text->undo_buf[text->undo_pos]= (fromc)&0xff;
1628         text->undo_pos++;
1629         text->undo_buf[text->undo_pos]= (fromc>>8)&0xff;
1630
1631         text->undo_pos++;
1632         text->undo_buf[text->undo_pos]= (froml)&0xff;
1633         text->undo_pos++;
1634         text->undo_buf[text->undo_pos]= (froml>>8)&0xff;
1635         text->undo_pos++;
1636         text->undo_buf[text->undo_pos]= (froml>>16)&0xff;
1637         text->undo_pos++;
1638         text->undo_buf[text->undo_pos]= (froml>>24)&0xff;
1639
1640         text->undo_pos++;
1641         text->undo_buf[text->undo_pos]= (toc)&0xff;
1642         text->undo_pos++;
1643         text->undo_buf[text->undo_pos]= (toc>>8)&0xff;
1644
1645         text->undo_pos++;
1646         text->undo_buf[text->undo_pos]= (tol)&0xff;
1647         text->undo_pos++;
1648         text->undo_buf[text->undo_pos]= (tol>>8)&0xff;
1649         text->undo_pos++;
1650         text->undo_buf[text->undo_pos]= (tol>>16)&0xff;
1651         text->undo_pos++;
1652         text->undo_buf[text->undo_pos]= (tol>>24)&0xff;
1653
1654         text->undo_pos++;
1655         text->undo_buf[text->undo_pos]= op;
1656
1657         text->undo_buf[text->undo_pos+1]= 0;
1658 }
1659
1660 static void txt_undo_add_charop(Text *text, int op, char c)
1661 {
1662         if(!max_undo_test(text, 4))
1663                 return;
1664
1665         text->undo_pos++;
1666         text->undo_buf[text->undo_pos]= op;
1667         text->undo_pos++;
1668         text->undo_buf[text->undo_pos]= c;
1669         text->undo_pos++;
1670         text->undo_buf[text->undo_pos]= op;
1671         text->undo_buf[text->undo_pos+1]= 0;
1672 }
1673
1674 void txt_do_undo(Text *text)
1675 {
1676         int op= text->undo_buf[text->undo_pos];
1677         unsigned int linep, i;
1678         unsigned short charp;
1679         TextLine *holdl;
1680         int holdc, holdln;
1681         char *buf;
1682         
1683         if (text->undo_pos<0) {
1684                 return;
1685         }
1686
1687         text->undo_pos--;
1688
1689         undoing= 1;
1690         
1691         switch(op) {
1692                 case UNDO_CLEFT:
1693                         txt_move_right(text, 0);
1694                         break;
1695                         
1696                 case UNDO_CRIGHT:
1697                         txt_move_left(text, 0);
1698                         break;
1699                         
1700                 case UNDO_CUP:
1701                         txt_move_down(text, 0);
1702                         break;
1703                         
1704                 case UNDO_CDOWN:
1705                         txt_move_up(text, 0);
1706                         break;
1707
1708                 case UNDO_SLEFT:
1709                         txt_move_right(text, 1);
1710                         break;
1711
1712                 case UNDO_SRIGHT:
1713                         txt_move_left(text, 1);
1714                         break;
1715
1716                 case UNDO_SUP:
1717                         txt_move_down(text, 1);
1718                         break;
1719
1720                 case UNDO_SDOWN:
1721                         txt_move_up(text, 1);
1722                         break;
1723                 
1724                 case UNDO_CTO:
1725                 case UNDO_STO:
1726                         text->undo_pos--;
1727                         text->undo_pos--;
1728                         text->undo_pos--;
1729                         text->undo_pos--;
1730                 
1731                         text->undo_pos--;
1732                         text->undo_pos--;
1733                 
1734                         linep= text->undo_buf[text->undo_pos]; text->undo_pos--;
1735                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1736                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1737                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1738
1739                         charp= text->undo_buf[text->undo_pos]; text->undo_pos--;
1740                         charp= (charp<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1741                         
1742                         if (op==UNDO_CTO) {
1743                                 txt_move_toline(text, linep, 0);
1744                                 text->curc= charp;
1745                                 txt_pop_sel(text);
1746                         } else {
1747                                 txt_move_toline(text, linep, 1);
1748                                 text->selc= charp;
1749                         }
1750                         
1751                         text->undo_pos--;
1752                         break;
1753                         
1754                 case UNDO_INSERT:
1755                         txt_backspace_char(text);
1756                         text->undo_pos--;
1757                         text->undo_pos--;
1758                         break;
1759
1760                 case UNDO_BS:
1761                         txt_add_char(text, text->undo_buf[text->undo_pos]);
1762                         text->undo_pos--;
1763                         text->undo_pos--;
1764                         break;
1765
1766                 case UNDO_DEL:
1767                         txt_add_char(text, text->undo_buf[text->undo_pos]);
1768                         txt_move_left(text, 0);
1769                         text->undo_pos--;
1770                         text->undo_pos--;
1771                         break;
1772
1773                 case UNDO_SWAP:
1774                         txt_curs_swap(text);
1775                         break;
1776
1777                 case UNDO_DBLOCK:
1778                         linep= text->undo_buf[text->undo_pos]; text->undo_pos--;
1779                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1780                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1781                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1782
1783                         buf= MEM_mallocN(linep+1, "dblock buffer");
1784                         for (i=0; i < linep; i++){
1785                                 buf[(linep-1)-i]= text->undo_buf[text->undo_pos]; 
1786                                 text->undo_pos--;
1787                         }
1788                         buf[i]= 0;
1789                         
1790                         txt_curs_first(text, &holdl, &holdc);
1791                         holdln= txt_get_span(text->lines.first, holdl);
1792                         
1793                         txt_insert_buf(text, buf);                      
1794                         MEM_freeN(buf);
1795
1796                         text->curl= text->lines.first;
1797                         while (holdln>0) {
1798                                 if(text->curl->next)
1799                                         text->curl= text->curl->next;
1800                                         
1801                                 holdln--;
1802                         }
1803                         text->curc= holdc;
1804
1805                         linep= text->undo_buf[text->undo_pos]; text->undo_pos--;
1806                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1807                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1808                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1809
1810                         text->undo_pos--;
1811                         
1812                         break;
1813
1814                 case UNDO_IBLOCK:
1815                         linep= text->undo_buf[text->undo_pos]; text->undo_pos--;
1816                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1817                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1818                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1819
1820                         txt_delete_sel(text);
1821                         while (linep>0) {
1822                                 txt_backspace_char(text);
1823                                 text->undo_pos--;
1824                                 linep--;
1825                         }
1826
1827                         text->undo_pos--;
1828                         text->undo_pos--;
1829                         text->undo_pos--; 
1830                         text->undo_pos--;
1831                         
1832                         text->undo_pos--;
1833
1834                         break;
1835                 case UNDO_INDENT:
1836                 case UNDO_UNINDENT:
1837                 case UNDO_COMMENT:
1838                 case UNDO_UNCOMMENT:
1839                         linep= text->undo_buf[text->undo_pos]; text->undo_pos--;
1840                         linep = (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1841                         linep = (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1842                         linep = (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1843                         //linep is now the end line of the selection
1844                         
1845                         charp = text->undo_buf[text->undo_pos]; text->undo_pos--;
1846                         charp = (charp<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1847                         //charp is the last char selected or text->line->len
1848                         //set the selcetion for this now
1849                         text->selc = charp;
1850                         text->sell = text->lines.first;
1851                         for (i= 0; i < linep; i++) {
1852                                 text->sell = text->sell->next;
1853                         }
1854
1855                         linep= text->undo_buf[text->undo_pos]; text->undo_pos--;
1856                         linep = (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1857                         linep = (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1858                         linep = (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1859                         //first line to be selected
1860                         
1861                         charp = text->undo_buf[text->undo_pos]; text->undo_pos--;
1862                         charp = (charp<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1863                         //first postion to be selected
1864                         text->curc = charp;
1865                         text->curl = text->lines.first;
1866                         for (i = 0; i < linep; i++) {
1867                                 text->curl = text->curl->next;
1868                         }
1869
1870                         
1871                         if (op==UNDO_INDENT) {
1872                                 unindent(text);
1873                         } else if (op== UNDO_UNINDENT) {
1874                                 indent(text);
1875                         } else if (op == UNDO_COMMENT) {
1876                                 uncomment(text);
1877                         } else if (op == UNDO_UNCOMMENT) {
1878                                 comment(text);
1879                         }
1880
1881                         text->undo_pos--;
1882                         break;
1883                 default:
1884                         //XXX error("Undo buffer error - resetting");
1885                         text->undo_pos= -1;
1886                         
1887                         break;
1888         }
1889
1890         /* next undo step may need evaluating */
1891         if (text->undo_pos>=0) {
1892                 switch (text->undo_buf[text->undo_pos]) {
1893                         case UNDO_STO:
1894                                 txt_do_undo(text);
1895                                 txt_do_redo(text); /* selections need restoring */
1896                                 break;
1897                         case UNDO_SWAP:
1898                                 txt_do_undo(text); /* swaps should appear transparent */
1899                                 break;
1900                 }
1901         }
1902         
1903         undoing= 0;     
1904 }
1905
1906 void txt_do_redo(Text *text)
1907 {
1908         char op;
1909         unsigned int linep, i;
1910         unsigned short charp;
1911         char *buf;
1912         
1913         text->undo_pos++;       
1914         op= text->undo_buf[text->undo_pos];
1915         
1916         if (!op) {
1917                 text->undo_pos--;
1918                 return;
1919         }
1920         
1921         undoing= 1;
1922
1923         switch(op) {
1924                 case UNDO_CLEFT:
1925                         txt_move_left(text, 0);
1926                         break;
1927                         
1928                 case UNDO_CRIGHT:
1929                         txt_move_right(text, 0);
1930                         break;
1931                         
1932                 case UNDO_CUP:
1933                         txt_move_up(text, 0);
1934                         break;
1935                         
1936                 case UNDO_CDOWN:
1937                         txt_move_down(text, 0);
1938                         break;
1939
1940                 case UNDO_SLEFT:
1941                         txt_move_left(text, 1);
1942                         break;
1943
1944                 case UNDO_SRIGHT:
1945                         txt_move_right(text, 1);
1946                         break;
1947
1948                 case UNDO_SUP:
1949                         txt_move_up(text, 1);
1950                         break;
1951
1952                 case UNDO_SDOWN:
1953                         txt_move_down(text, 1);
1954                         break;
1955                 
1956                 case UNDO_INSERT:
1957                         text->undo_pos++;
1958                         txt_add_char(text, text->undo_buf[text->undo_pos]);
1959                         text->undo_pos++;
1960                         break;
1961
1962                 case UNDO_BS:
1963                         text->undo_pos++;
1964                         txt_backspace_char(text);
1965                         text->undo_pos++;
1966                         break;
1967
1968                 case UNDO_DEL:
1969                         text->undo_pos++;
1970                         txt_delete_char(text);
1971                         text->undo_pos++;
1972                         break;
1973
1974                 case UNDO_SWAP:
1975                         txt_curs_swap(text);
1976                         txt_do_redo(text); /* swaps should appear transparent a*/
1977                         break;
1978                         
1979                 case UNDO_CTO:
1980                 case UNDO_STO:
1981                         text->undo_pos++;
1982                         text->undo_pos++;
1983
1984                         text->undo_pos++;
1985                         text->undo_pos++;
1986                         text->undo_pos++;
1987                         text->undo_pos++;
1988
1989                         text->undo_pos++;
1990
1991                         charp= text->undo_buf[text->undo_pos];
1992                         text->undo_pos++;
1993                         charp= charp+(text->undo_buf[text->undo_pos]<<8);
1994
1995                         text->undo_pos++;
1996                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
1997                         linep= linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
1998                         linep= linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
1999                         linep= linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2000                         
2001                         if (op==UNDO_CTO) {
2002                                 txt_move_toline(text, linep, 0);
2003                                 text->curc= charp;
2004                                 txt_pop_sel(text);
2005                         } else {
2006                                 txt_move_toline(text, linep, 1);
2007                                 text->selc= charp;
2008                         }
2009
2010                         break;
2011
2012                 case UNDO_DBLOCK:
2013                         text->undo_pos++;
2014                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2015                         linep= linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2016                         linep= linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2017                         linep= linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2018
2019                         txt_delete_sel(text);
2020                         text->undo_pos+=linep;
2021
2022                         text->undo_pos++;
2023                         text->undo_pos++;
2024                         text->undo_pos++; 
2025                         text->undo_pos++;
2026                         
2027                         break;
2028
2029                 case UNDO_IBLOCK:
2030                         text->undo_pos++;
2031                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2032                         linep= linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2033                         linep= linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2034                         linep= linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2035
2036                         buf= MEM_mallocN(linep+1, "iblock buffer");
2037                         memcpy (buf, &text->undo_buf[text->undo_pos], linep);
2038                         text->undo_pos+= linep;
2039                         buf[linep]= 0;
2040                         
2041                         txt_insert_buf(text, buf);                      
2042                         MEM_freeN(buf);
2043
2044                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2045                         linep= linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2046                         linep= linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2047                         linep= linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2048
2049                         break;
2050                 case UNDO_INDENT:
2051                 case UNDO_UNINDENT:
2052                 case UNDO_COMMENT:
2053                 case UNDO_UNCOMMENT:
2054                         text->undo_pos++;
2055                         charp = text->undo_buf[text->undo_pos]; text->undo_pos++;
2056                         charp = charp+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2057                         //charp is the first char selected or 0
2058                         
2059                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2060                         linep = linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2061                         linep = linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2062                         linep = linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2063                         //linep is now the first line of the selection                  
2064                         //set the selcetion for this now
2065                         text->curc = charp;
2066                         text->curl = text->lines.first;
2067                         for (i= 0; i < linep; i++) {
2068                                 text->curl = text->curl->next;
2069                         }
2070                         
2071                         charp = text->undo_buf[text->undo_pos]; text->undo_pos++;
2072                         charp = charp+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2073                         //last postion to be selected
2074                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2075                         linep = linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2076                         linep = linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2077                         linep = linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2078                         //Last line to be selected
2079                         
2080                         text->selc = charp;
2081                         text->sell = text->lines.first;
2082                         for (i = 0; i < linep; i++) {
2083                                 text->sell = text->sell->next;
2084                         }
2085
2086                         if (op==UNDO_INDENT) {
2087                                 indent(text);
2088                         } else if (op== UNDO_UNINDENT) {
2089                                 unindent(text);
2090                         } else if (op == UNDO_COMMENT) {
2091                                 comment(text);
2092                         } else if (op == UNDO_UNCOMMENT) {
2093                                 uncomment(text);
2094                         }
2095                         break;
2096                 default:
2097                         //XXX error("Undo buffer error - resetting");
2098                         text->undo_pos= -1;
2099
2100                         break;
2101         }
2102         
2103         undoing= 0;     
2104 }
2105
2106 /**************************/
2107 /* Line editing functions */ 
2108 /**************************/
2109
2110 void txt_split_curline (Text *text) 
2111 {
2112         TextLine *ins;
2113         TextMarker *mrk;
2114         char *left, *right;
2115         int lineno= -1;
2116         
2117         if (!text) return;
2118         if (!text->curl) return;
2119
2120         txt_delete_sel(text);
2121
2122         /* Move markers */
2123
2124         lineno= txt_get_span(text->lines.first, text->curl);
2125         mrk= text->markers.first;
2126         while (mrk) {
2127                 if (mrk->lineno==lineno && mrk->start>text->curc) {
2128                         mrk->lineno++;
2129                         mrk->start -= text->curc;
2130                         mrk->end -= text->curc;
2131                 } else if (mrk->lineno > lineno) {
2132                         mrk->lineno++;
2133                 }
2134                 mrk= mrk->next;
2135         }
2136
2137         /* Make the two half strings */
2138
2139         left= MEM_mallocN(text->curc+1, "textline_string");
2140         if (text->curc) memcpy(left, text->curl->line, text->curc);
2141         left[text->curc]=0;
2142         
2143         right= MEM_mallocN(text->curl->len - text->curc+1, "textline_string");
2144         if (text->curl->len - text->curc) memcpy(right, text->curl->line+text->curc, text->curl->len-text->curc);
2145         right[text->curl->len - text->curc]=0;
2146
2147         MEM_freeN(text->curl->line);
2148         if (text->curl->format) MEM_freeN(text->curl->format);
2149
2150         /* Make the new TextLine */
2151         
2152         ins= MEM_mallocN(sizeof(TextLine), "textline");
2153         ins->line= left;
2154         ins->format= NULL;
2155         ins->len= text->curc;
2156         
2157         text->curl->line= right;
2158         text->curl->format= NULL;
2159         text->curl->len= text->curl->len - text->curc;
2160         
2161         BLI_insertlinkbefore(&text->lines, text->curl, ins);    
2162         
2163         text->curc=0;
2164         
2165         txt_make_dirty(text);
2166         txt_clean_text(text);
2167         
2168         txt_pop_sel(text);
2169         if(!undoing) txt_undo_add_charop(text, UNDO_INSERT, '\n');
2170 }
2171
2172 static void txt_delete_line (Text *text, TextLine *line) 
2173 {
2174         TextMarker *mrk=NULL, *nxt;
2175         int lineno= -1;
2176
2177         if (!text) return;
2178         if (!text->curl) return;
2179
2180         lineno= txt_get_span(text->lines.first, line);
2181         mrk= text->markers.first;
2182         while (mrk) {
2183                 nxt= mrk->next;
2184                 if (mrk->lineno==lineno)
2185                         BLI_freelinkN(&text->markers, mrk);
2186                 else if (mrk->lineno > lineno)
2187                         mrk->lineno--;
2188                 mrk= nxt;
2189         }
2190
2191         BLI_remlink (&text->lines, line);
2192         
2193         if (line->line) MEM_freeN(line->line);
2194         if (line->format) MEM_freeN(line->format);
2195
2196         MEM_freeN(line);
2197
2198         txt_make_dirty(text);
2199         txt_clean_text(text);
2200 }
2201
2202 static void txt_combine_lines (Text *text, TextLine *linea, TextLine *lineb)
2203 {
2204         char *tmp;
2205         TextMarker *mrk= NULL;
2206         int lineno=-1;
2207         
2208         if (!text) return;
2209         
2210         if(!linea || !lineb) return;
2211
2212         mrk= txt_find_marker_region(text, lineb, 0, lineb->len, 0, 0);
2213         if (mrk) {
2214                 lineno= mrk->lineno;
2215                 do {
2216                         mrk->lineno--;
2217                         mrk->start += linea->len;
2218                         mrk->end += linea->len;
2219                         mrk= mrk->next;
2220                 } while (mrk && mrk->lineno==lineno);
2221         }
2222         if (lineno==-1) lineno= txt_get_span(text->lines.first, lineb);
2223         if (!mrk) mrk= text->markers.first;
2224         
2225         tmp= MEM_mallocN(linea->len+lineb->len+1, "textline_string");
2226         
2227         strcpy(tmp, linea->line);
2228         strcat(tmp, lineb->line);
2229
2230         make_new_line(linea, tmp);
2231         
2232         txt_delete_line(text, lineb);
2233         
2234         txt_make_dirty(text);
2235         txt_clean_text(text);
2236 }
2237
2238 void txt_delete_char (Text *text) 
2239 {
2240         char c='\n';
2241         
2242         if (!text) return;
2243         if (!text->curl) return;
2244
2245         if (txt_has_sel(text)) { /* deleting a selection */
2246                 txt_delete_sel(text);
2247                 txt_make_dirty(text);
2248                 return;
2249         }
2250         else if (text->curc== text->curl->len) { /* Appending two lines */
2251                 if (text->curl->next) {
2252                         txt_combine_lines(text, text->curl, text->curl->next);
2253                         txt_pop_sel(text);
2254                 }
2255         } else { /* Just deleting a char */
2256                 int i= text->curc;
2257
2258                 TextMarker *mrk= txt_find_marker_region(text, text->curl, i-1, text->curl->len, 0, 0);
2259                 if (mrk) {
2260                         int lineno= mrk->lineno;
2261                         if (mrk->end==i) {
2262                                 if ((mrk->flags & TMARK_TEMP) && !(mrk->flags & TMARK_EDITALL)) {
2263                                         txt_clear_markers(text, mrk->group, TMARK_TEMP);
2264                                 } else {
2265                                         BLI_freelinkN(&text->markers, mrk);
2266                                 }
2267                                 return;
2268                         }
2269                         do {
2270                                 if (mrk->start>i) mrk->start--;
2271                                 mrk->end--;
2272                                 mrk= mrk->next;
2273                         } while (mrk && mrk->lineno==lineno);
2274                 }
2275                 
2276                 c= text->curl->line[i];
2277                 while(i< text->curl->len) {
2278                         text->curl->line[i]= text->curl->line[i+1];
2279                         i++;
2280                 }
2281                 text->curl->len--;
2282
2283                 txt_pop_sel(text);
2284         }
2285
2286         txt_make_dirty(text);
2287         txt_clean_text(text);
2288         
2289         if(!undoing) txt_undo_add_charop(text, UNDO_DEL, c);
2290 }
2291
2292 void txt_delete_word (Text *text) 
2293 {
2294         txt_jump_right(text, 1);
2295         txt_delete_sel(text);
2296 }
2297
2298 void txt_backspace_char (Text *text) 
2299 {
2300         char c='\n';
2301         
2302         if (!text) return;
2303         if (!text->curl) return;
2304         
2305         if (txt_has_sel(text)) { /* deleting a selection */
2306                 txt_delete_sel(text);
2307                 txt_make_dirty(text);
2308                 return;
2309         }
2310         else if (text->curc==0) { /* Appending two lines */
2311                 if (!text->curl->prev) return;
2312                 
2313                 text->curl= text->curl->prev;
2314                 text->curc= text->curl->len;
2315                 
2316                 txt_combine_lines(text, text->curl, text->curl->next);
2317                 txt_pop_sel(text);
2318         }
2319         else { /* Just backspacing a char */
2320                 int i= text->curc-1;
2321
2322                 TextMarker *mrk= txt_find_marker_region(text, text->curl, i, text->curl->len, 0, 0);
2323                 if (mrk) {
2324                         int lineno= mrk->lineno;
2325                         if (mrk->start==i+1) {
2326                                 if ((mrk->flags & TMARK_TEMP) && !(mrk->flags & TMARK_EDITALL)) {
2327                                         txt_clear_markers(text, mrk->group, TMARK_TEMP);
2328                                 } else {
2329                                         BLI_freelinkN(&text->markers, mrk);
2330                                 }
2331                                 return;
2332                         }
2333                         do {
2334                                 if (mrk->start>i) mrk->start--;
2335                                 mrk->end--;
2336                                 mrk= mrk->next;
2337                         } while (mrk && mrk->lineno==lineno);
2338                 }
2339                 
2340                 c= text->curl->line[i];
2341                 while(i< text->curl->len) {
2342                         text->curl->line[i]= text->curl->line[i+1];
2343                         i++;
2344                 }
2345                 text->curl->len--;
2346                 text->curc--;
2347
2348                 txt_pop_sel(text);
2349         }
2350
2351         txt_make_dirty(text);
2352         txt_clean_text(text);
2353         
2354         if(!undoing) txt_undo_add_charop(text, UNDO_BS, c);
2355 }
2356
2357 void txt_backspace_word (Text *text) 
2358 {
2359         txt_jump_left(text, 1);
2360         txt_delete_sel(text);
2361 }
2362
2363 /* Max spaces to replace a tab with, currently hardcoded to TXT_TABSIZE = 4.
2364  * Used by txt_convert_tab_to_spaces, indent and unintent.
2365  * Remember to change this string according to max tab size */
2366 static char tab_to_spaces[] = "    ";
2367
2368 static void txt_convert_tab_to_spaces (Text *text)
2369 {
2370         /* sb aims to pad adjust the tab-width needed so that the right number of spaces
2371          * is added so that the indention of the line is the right width (i.e. aligned
2372          * to multiples of TXT_TABSIZE)
2373          */
2374         char *sb = &tab_to_spaces[text->curc % TXT_TABSIZE];
2375         txt_insert_buf(text, sb);
2376 }
2377
2378 int txt_add_char (Text *text, char add) 
2379 {
2380         int len, lineno;
2381         char *tmp;
2382         TextMarker *mrk;
2383         
2384         if (!text) return 0;
2385         if (!text->curl) return 0;
2386
2387         if (add=='\n') {
2388                 txt_split_curline(text);
2389                 return 1;
2390         }
2391         
2392         /* insert spaces rather then tabs */
2393         if (add == '\t' && text->flags & TXT_TABSTOSPACES) {
2394                 txt_convert_tab_to_spaces(text);
2395                 return 1;
2396         }
2397
2398         txt_delete_sel(text);
2399         
2400         mrk= txt_find_marker_region(text, text->curl, text->curc-1, text->curl->len, 0, 0);
2401         if (mrk) {
2402                 lineno= mrk->lineno;
2403                 do {
2404                         if (mrk->start>text->curc) mrk->start++;
2405                         mrk->end++;
2406                         mrk= mrk->next;
2407                 } while (mrk && mrk->lineno==lineno);
2408         }
2409         
2410         tmp= MEM_mallocN(text->curl->len+2, "textline_string");
2411         
2412         if(text->curc) memcpy(tmp, text->curl->line, text->curc);
2413         tmp[text->curc]= add;
2414         
2415         len= text->curl->len - text->curc;
2416         if(len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len);
2417         tmp[text->curl->len+1]=0;
2418         make_new_line(text->curl, tmp);
2419                 
2420         text->curc++;
2421
2422         txt_pop_sel(text);
2423         
2424         txt_make_dirty(text);
2425         txt_clean_text(text);
2426
2427         if(!undoing) txt_undo_add_charop(text, UNDO_INSERT, add);
2428         return 1;
2429 }
2430
2431 void txt_delete_selected(Text *text)
2432 {
2433         txt_delete_sel(text);
2434         txt_make_dirty(text);
2435 }
2436
2437 int txt_replace_char (Text *text, char add)
2438 {
2439         char del;
2440         
2441         if (!text) return 0;
2442         if (!text->curl) return 0;
2443
2444         /* If text is selected or we're at the end of the line just use txt_add_char */
2445         if (text->curc==text->curl->len || txt_has_sel(text) || add=='\n') {
2446                 TextMarker *mrk;
2447                 int i= txt_add_char(text, add);
2448                 mrk= txt_find_marker(text, text->curl, text->curc, 0, 0);
2449                 if (mrk && mrk->end==text->curc) mrk->end--;
2450                 return i;
2451         }
2452         
2453         del= text->curl->line[text->curc];
2454         text->curl->line[text->curc]= (unsigned char) add;
2455         text->curc++;
2456         txt_pop_sel(text);
2457         
2458         txt_make_dirty(text);
2459         txt_clean_text(text);
2460
2461         /* Should probably create a new op for this */
2462         if(!undoing) {
2463                 txt_undo_add_charop(text, UNDO_DEL, del);
2464                 txt_undo_add_charop(text, UNDO_INSERT, add);
2465         }
2466         return 1;
2467 }
2468
2469 void indent(Text *text)
2470 {
2471         int len, num;
2472         char *tmp;
2473
2474         const char *add = "\t";
2475         int indentlen = 1;
2476         
2477         /* hardcoded: TXT_TABSIZE = 4 spaces: */
2478         int spaceslen = TXT_TABSIZE;
2479
2480         /* insert spaces rather then tabs */
2481         if (text->flags & TXT_TABSTOSPACES){
2482                 add = tab_to_spaces;
2483                 indentlen = spaceslen;
2484         }
2485         
2486         if (!text) return;
2487         if (!text->curl) return;
2488         if (!text->sell) return;
2489
2490         num = 0;
2491         while (TRUE)
2492         {
2493                 tmp= MEM_mallocN(text->curl->len+indentlen+1, "textline_string");
2494                 
2495                 text->curc = 0; 
2496                 if(text->curc) memcpy(tmp, text->curl->line, text->curc); /* XXX never true, check prev line */
2497                 memcpy(tmp+text->curc, add, indentlen);
2498                 
2499                 len= text->curl->len - text->curc;
2500                 if(len>0) memcpy(tmp+text->curc+indentlen, text->curl->line+text->curc, len);
2501                 tmp[text->curl->len+indentlen]= 0;
2502
2503                 make_new_line(text->curl, tmp);
2504                         
2505                 text->curc+= indentlen;
2506                 
2507                 txt_make_dirty(text);
2508                 txt_clean_text(text);
2509                 
2510                 if(text->curl == text->sell) 
2511                 {
2512                         text->selc = text->sell->len;
2513                         break;
2514                 } else {
2515                         text->curl = text->curl->next;
2516                         num++;
2517                 }
2518         }
2519         text->curc = 0;
2520         while( num > 0 )
2521         {
2522                 text->curl = text->curl->prev;
2523                 num--;
2524         }
2525         
2526         if(!undoing) 
2527         {
2528                 txt_undo_add_toop(text, UNDO_INDENT, txt_get_span(text->lines.first, text->curl), text->curc, txt_get_span(text->lines.first, text->sell), text->selc);
2529         }
2530 }
2531
2532 void unindent(Text *text)
2533 {
2534         int num = 0;
2535         const char *remove = "\t";
2536         int indent = 1;
2537         
2538         /* hardcoded: TXT_TABSIZE = 4 spaces: */
2539         int spaceslen = TXT_TABSIZE;
2540
2541         /* insert spaces rather then tabs */
2542         if (text->flags & TXT_TABSTOSPACES){
2543                 remove = tab_to_spaces;
2544                 indent = spaceslen;
2545         }
2546
2547         if (!text) return;
2548         if (!text->curl) return;
2549         if (!text->sell) return;
2550
2551         while(TRUE)
2552         {
2553                 int i = 0;
2554                 
2555                 if (BLI_strncasecmp(text->curl->line, remove, indent) == 0)
2556                 {
2557                         while(i< text->curl->len) {
2558                                 text->curl->line[i]= text->curl->line[i+indent];
2559                                 i++;
2560                         }
2561                         text->curl->len-= indent;
2562                 }
2563         
2564                 txt_make_dirty(text);
2565                 txt_clean_text(text);
2566                 
2567                 if(text->curl == text->sell) 
2568                 {
2569                         text->selc = text->sell->len;
2570                         break;
2571                 } else {
2572                         text->curl = text->curl->next;
2573                         num++;
2574                 }
2575                 
2576         }
2577         text->curc = 0;
2578         while( num > 0 )
2579         {
2580                 text->curl = text->curl->prev;
2581                 num--;
2582         }
2583         
2584         if(!undoing) 
2585         {
2586                 txt_undo_add_toop(text, UNDO_UNINDENT, txt_get_span(text->lines.first, text->curl), text->curc, txt_get_span(text->lines.first, text->sell), text->selc);
2587         }
2588 }
2589
2590 void comment(Text *text)
2591 {
2592         int len, num;
2593         char *tmp;
2594         char add = '#';
2595         
2596         if (!text) return;
2597         if (!text->curl) return;
2598         if (!text->sell) return;// Need to change this need to check if only one line is selected ot more then one
2599
2600         num = 0;
2601         while (TRUE)
2602         {
2603                 tmp= MEM_mallocN(text->curl->len+2, "textline_string");
2604                 
2605                 text->curc = 0; 
2606                 if(text->curc) memcpy(tmp, text->curl->line, text->curc);
2607                 tmp[text->curc]= add;
2608                 
2609                 len= text->curl->len - text->curc;
2610                 if(len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len);
2611                 tmp[text->curl->len+1]=0;
2612
2613                 make_new_line(text->curl, tmp);
2614                         
2615                 text->curc++;
2616                 
2617                 txt_make_dirty(text);
2618                 txt_clean_text(text);
2619                 
2620                 if(text->curl == text->sell) 
2621                 {
2622                         text->selc = text->sell->len;
2623                         break;
2624                 } else {
2625                         text->curl = text->curl->next;
2626                         num++;
2627                 }
2628         }
2629         text->curc = 0;
2630         while( num > 0 )
2631         {
2632                 text->curl = text->curl->prev;
2633                 num--;
2634         }
2635         
2636         if(!undoing) 
2637         {
2638                 txt_undo_add_toop(text, UNDO_COMMENT, txt_get_span(text->lines.first, text->curl), text->curc, txt_get_span(text->lines.first, text->sell), text->selc);
2639         }
2640 }
2641
2642 void uncomment(Text *text)
2643 {
2644         int num = 0;
2645         char remove = '#';
2646         
2647         if (!text) return;
2648         if (!text->curl) return;
2649         if (!text->sell) return;
2650
2651         while(TRUE)
2652         {
2653                 int i = 0;
2654                 
2655                 if (text->curl->line[i] == remove)
2656                 {
2657                         while(i< text->curl->len) {
2658                                 text->curl->line[i]= text->curl->line[i+1];
2659                                 i++;
2660                         }
2661                         text->curl->len--;
2662                 }
2663                          
2664         
2665                 txt_make_dirty(text);
2666                 txt_clean_text(text);
2667                 
2668                 if(text->curl == text->sell) 
2669                 {
2670                         text->selc = text->sell->len;
2671                         break;
2672                 } else {
2673                         text->curl = text->curl->next;
2674                         num++;
2675                 }
2676                 
2677         }
2678         text->curc = 0;
2679         while( num > 0 )
2680         {
2681                 text->curl = text->curl->prev;
2682                 num--;
2683         }
2684         
2685         if(!undoing) 
2686         {
2687                 txt_undo_add_toop(text, UNDO_UNCOMMENT, txt_get_span(text->lines.first, text->curl), text->curc, txt_get_span(text->lines.first, text->sell), text->selc);
2688         }
2689 }
2690
2691 int setcurr_tab_spaces (Text *text, int space)
2692 {
2693         int i = 0;
2694         int test = 0;
2695         const char *word = ":";
2696         const char *comm = "#";
2697         const char indent= (text->flags & TXT_TABSTOSPACES) ? ' ' : '\t';
2698         static const char *back_words[]= {"return", "break", "continue", "pass", "yield", NULL};
2699         if (!text) return 0;
2700         if (!text->curl) return 0;
2701
2702         while (text->curl->line[i] == indent)
2703         {
2704                 //we only count those tabs/spaces that are before any text or before the curs;
2705                 if (i == text->curc)
2706                 {
2707                         return i;
2708                 } else {
2709                         i++;
2710                 }
2711         }
2712         if(strstr(text->curl->line, word))
2713         {
2714                 /* if we find a ':' on this line, then add a tab but not if it is:
2715                  *      1) in a comment
2716                  *      2) within an identifier
2717                  *      3) after the cursor (text->curc), i.e. when creating space before a function def [#25414] 
2718                  */
2719                 int a, indent = 0;
2720                 for(a=0; (a < text->curc) && (text->curl->line[a] != '\0'); a++)
2721                 {
2722                         if (text->curl->line[a]=='#') {
2723                                 break;
2724                         } else if (text->curl->line[a]==':') {
2725                                 indent = 1;
2726                         } else if (text->curl->line[a]==']') {
2727                                 indent = 0;
2728                         }
2729                 }
2730                 if (indent) {
2731                         i += space;
2732                 }
2733         }
2734
2735         for(test=0; back_words[test]; test++)
2736         {
2737                 /* if there are these key words then remove a tab because we are done with the block */
2738                 if(strstr(text->curl->line, back_words[test]) && i > 0)
2739                 {
2740                         if(strcspn(text->curl->line, back_words[test]) < strcspn(text->curl->line, comm))
2741                         {
2742                                 i -= space;
2743                         }
2744                 }
2745         }
2746         return i;
2747 }
2748
2749 /*********************************/
2750 /* Text marker utility functions */
2751 /*********************************/
2752
2753 /* Creates and adds a marker to the list maintaining sorted order */
2754 void txt_add_marker(Text *text, TextLine *line, int start, int end, const unsigned char color[4], int group, int flags) {
2755         TextMarker *tmp, *marker;
2756
2757         marker= MEM_mallocN(sizeof(TextMarker), "text_marker");
2758         
2759         marker->lineno= txt_get_span(text->lines.first, line);
2760         marker->start= MIN2(start, end);
2761         marker->end= MAX2(start, end);
2762         marker->group= group;
2763         marker->flags= flags;
2764
2765         marker->color[0]= color[0];
2766         marker->color[1]= color[1];
2767         marker->color[2]= color[2];
2768         marker->color[3]= color[3];
2769
2770         for (tmp=text->markers.last; tmp; tmp=tmp->prev)
2771                 if (tmp->lineno < marker->lineno || (tmp->lineno==marker->lineno && tmp->start < marker->start))
2772                         break;
2773
2774         if (tmp) BLI_insertlinkafter(&text->markers, tmp, marker);
2775         else BLI_addhead(&text->markers, marker);
2776 }
2777
2778 /* Returns the first matching marker on the specified line between two points.
2779    If the group or flags fields are non-zero the returned flag must be in the
2780    specified group and have at least the specified flags set. */
2781 TextMarker *txt_find_marker_region(Text *text, TextLine *line, int start, int end, int group, int flags) {
2782         TextMarker *marker, *next;
2783         int lineno= txt_get_span(text->lines.first, line);
2784         
2785         for (marker=text->markers.first; marker; marker=next) {
2786                 next= marker->next;
2787
2788                 if (group && marker->group != group) continue;
2789                 else if ((marker->flags & flags) != flags) continue;
2790                 else if (marker->lineno < lineno) continue;
2791                 else if (marker->lineno > lineno) break;
2792
2793                 if ((marker->start==marker->end && start<=marker->start && marker->start<=end) ||
2794                                 (marker->start<end && marker->end>start))
2795                         return marker;
2796         }
2797         return NULL;
2798 }
2799
2800 /* Clears all markers on the specified line between two points. If the group or
2801    flags fields are non-zero the returned flag must be in the specified group
2802    and have at least the specified flags set. */
2803 short txt_clear_marker_region(Text *text, TextLine *line, int start, int end, int group, int flags) {
2804         TextMarker *marker, *next;
2805         int lineno= txt_get_span(text->lines.first, line);
2806         short cleared= 0;
2807         
2808         for (marker=text->markers.first; marker; marker=next) {
2809                 next= marker->next;
2810
2811                 if (group && marker->group != group) continue;
2812                 else if ((marker->flags & flags) != flags) continue;
2813                 else if (marker->lineno < lineno) continue;
2814                 else if (marker->lineno > lineno) break;
2815
2816                 if ((marker->start==marker->end && start<=marker->start && marker->start<=end) ||
2817                         (marker->start<end && marker->end>start)) {
2818                         BLI_freelinkN(&text->markers, marker);
2819                         cleared= 1;
2820                 }
2821         }
2822         return cleared;
2823 }
2824
2825 /* Clears all markers in the specified group (if given) with at least the
2826    specified flags set. Useful for clearing temporary markers (group=0,
2827    flags=TMARK_TEMP) */
2828 short txt_clear_markers(Text *text, int group, int flags) {
2829         TextMarker *marker, *next;
2830         short cleared= 0;
2831         
2832         for (marker=text->markers.first; marker; marker=next) {
2833                 next= marker->next;
2834
2835                 if ((!group || marker->group==group) &&
2836                                 (marker->flags & flags) == flags) {
2837                         BLI_freelinkN(&text->markers, marker);
2838                         cleared= 1;
2839                 }
2840         }
2841         return cleared;
2842 }
2843
2844 /* Finds the marker at the specified line and cursor position with at least the
2845    specified flags set in the given group (if non-zero). */
2846 TextMarker *txt_find_marker(Text *text, TextLine *line, int curs, int group, int flags) {
2847         TextMarker *marker;
2848         int lineno= txt_get_span(text->lines.first, line);
2849         
2850         for (marker=text->markers.first; marker; marker=marker->next) {
2851                 if (group && marker->group != group) continue;
2852                 else if ((marker->flags & flags) != flags) continue;
2853                 else if (marker->lineno < lineno) continue;
2854                 else if (marker->lineno > lineno) break;
2855
2856                 if (marker->start <= curs && curs <= marker->end)
2857                         return marker;
2858         }
2859         return NULL;
2860 }
2861
2862 /* Finds the previous marker in the same group. If no other is found, the same
2863    marker will be returned */
2864 TextMarker *txt_prev_marker(Text *text, TextMarker *marker) {
2865         TextMarker *tmp= marker;
2866         while (tmp) {
2867                 if (tmp->prev) tmp= tmp->prev;
2868                 else tmp= text->markers.last;
2869                 if (tmp->group == marker->group)
2870                         return tmp;
2871         }
2872         return NULL; /* Only if marker==NULL */
2873 }
2874
2875 /* Finds the next marker in the same group. If no other is found, the same
2876    marker will be returned */
2877 TextMarker *txt_next_marker(Text *text, TextMarker *marker) {
2878         TextMarker *tmp= marker;
2879         while (tmp) {
2880                 if (tmp->next) tmp= tmp->next;
2881                 else tmp= text->markers.first;
2882                 if (tmp->group == marker->group)
2883                         return tmp;
2884         }
2885         return NULL; /* Only if marker==NULL */
2886 }
2887
2888
2889 /*******************************/
2890 /* Character utility functions */
2891 /*******************************/
2892
2893 int text_check_bracket(char ch)
2894 {
2895         int a;
2896         char opens[] = "([{";
2897         char close[] = ")]}";
2898
2899         for(a=0; a<(sizeof(opens)-1); a++) {
2900                 if(ch==opens[a])
2901                         return a+1;
2902                 else if(ch==close[a])
2903                         return -(a+1);
2904         }
2905         return 0;
2906 }
2907
2908 int text_check_delim(char ch)
2909 {
2910         int a;
2911         char delims[] = "():\"\' ~!%^&*-+=[]{};/<>|.#\t,";
2912
2913         for(a=0; a<(sizeof(delims)-1); a++) {
2914                 if(ch==delims[a])
2915                         return 1;
2916         }
2917         return 0;
2918 }
2919
2920 int text_check_digit(char ch)
2921 {
2922         if(ch < '0') return 0;
2923         if(ch <= '9') return 1;
2924         return 0;
2925 }
2926
2927 int text_check_identifier(char ch)
2928 {
2929         if(ch < '0') return 0;
2930         if(ch <= '9') return 1;
2931         if(ch < 'A') return 0;
2932         if(ch <= 'Z' || ch == '_') return 1;
2933         if(ch < 'a') return 0;
2934         if(ch <= 'z') return 1;
2935         return 0;
2936 }
2937
2938 int text_check_whitespace(char ch)
2939 {
2940         if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
2941                 return 1;
2942         return 0;
2943 }