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