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