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