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