]> git.decadent.org.uk Git - ion3.git/blob - mod_query/edln.c
[svn-upgrade] Integrating new upstream version, ion3 (20070506)
[ion3.git] / mod_query / edln.c
1 /*
2  * ion/mod_query/edln.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2007. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <string.h>
10
11 #include <libtu/minmax.h>
12 #include <ioncore/common.h>
13 #include <ioncore/selection.h>
14 #include <ioncore/strings.h>
15
16 #include "edln.h"
17 #include "wedln.h"
18 #include "history.h"
19
20 #define EDLN_ALLOCUNIT 16
21
22 #define UPDATE(X) \
23     edln->ui_update(edln->uiptr, X, 0)
24
25 #define UPDATE_MOVED(X) \
26     edln->ui_update(edln->uiptr, X, EDLN_UPDATE_MOVED)
27
28 #define UPDATE_CHANGED(X)                                  \
29     edln->ui_update(edln->uiptr, X,                        \
30                     EDLN_UPDATE_MOVED|EDLN_UPDATE_CHANGED)
31
32 #define UPDATE_CHANGED_NOMOVE(X)         \
33     edln->ui_update(edln->uiptr, X,      \
34                     EDLN_UPDATE_CHANGED)
35
36 #define UPDATE_NEW()                                                       \
37     edln->ui_update(edln->uiptr, 0,                                        \
38                     EDLN_UPDATE_NEW|EDLN_UPDATE_MOVED|EDLN_UPDATE_CHANGED)
39
40 #define CHAR wchar_t
41 #define ISALNUM iswalnum
42 #define CHAR_AT(P, N) str_wchar_at(P, N)
43
44
45 /*{{{ Alloc */
46
47
48 static bool edln_pspc(Edln *edln, int n)
49 {
50     char *np;
51     int pa;
52     
53     if(edln->palloced<edln->psize+1+n){
54         pa=edln->palloced+n;
55         pa|=(EDLN_ALLOCUNIT-1);
56         np=ALLOC_N(char, pa);
57         
58         if(np==NULL)
59             return FALSE;
60
61         memmove(np, edln->p, edln->point*sizeof(char));
62         memmove(np+edln->point+n, edln->p+edln->point,
63                 (edln->psize-edln->point+1)*sizeof(char));
64         free(edln->p);
65         edln->p=np;
66         edln->palloced=pa;
67     }else{
68         memmove(edln->p+edln->point+n, edln->p+edln->point,
69                 (edln->psize-edln->point+1)*sizeof(char));
70     }
71
72     if(edln->mark>edln->point)
73         edln->mark+=n;
74     
75     edln->psize+=n;
76
77     edln->modified=1;
78     return TRUE;
79 }
80
81
82 static bool edln_rspc(Edln *edln, int n)
83 {
84     char *np;
85     int pa;
86     
87     if(n+edln->point>=edln->psize)
88         n=edln->psize-edln->point;
89     
90     if(n==0)
91         return TRUE;
92     
93     if((edln->psize+1-n)<(edln->palloced&~(EDLN_ALLOCUNIT-1))){
94         pa=edln->palloced&~(EDLN_ALLOCUNIT-1);
95         np=ALLOC_N(char, pa);
96         
97         if(np==NULL)
98             goto norm;
99         
100         memmove(np, edln->p, edln->point*sizeof(char));
101         memmove(np+edln->point, edln->p+edln->point+n,
102                 (edln->psize-edln->point+1-n)*sizeof(char));
103         free(edln->p);
104         edln->p=np;
105         edln->palloced=pa;
106     }else{
107     norm:
108         memmove(edln->p+edln->point, edln->p+edln->point+n,
109                 (edln->psize-edln->point+1-n)*sizeof(char));
110     }
111     edln->psize-=n;
112
113     if(edln->mark>edln->point)
114         edln->mark-=n;
115     
116     edln->modified=1;
117     return TRUE;
118 }
119
120
121 static void edln_clearstr(Edln *edln)
122 {
123     if(edln->p!=NULL){
124         free(edln->p);
125         edln->p=NULL;
126     }
127     edln->palloced=0;
128     edln->psize=0;
129 }
130
131
132 static bool edln_initstr(Edln *edln, const char *p)
133 {
134     int l=strlen(p), al;
135     
136     al=(l+1)|(EDLN_ALLOCUNIT-1);
137     
138     edln->p=ALLOC_N(char, al);
139     
140     if(edln->p==NULL)
141         return FALSE;
142
143     edln->palloced=al;
144     edln->psize=l;
145     strcpy(edln->p, p);
146     
147     return TRUE;
148 }
149
150
151 static bool edln_setstr(Edln *edln, const char *p)
152 {
153     edln_clearstr(edln);
154     return edln_initstr(edln, p);
155 }
156
157
158 /*}}}*/
159
160
161 /*{{{ Insert */
162
163
164 bool edln_insstr(Edln *edln, const char *str)
165 {
166     int l;
167     
168     if(str==NULL)
169         return FALSE;
170     
171     l=strlen(str);
172     
173     return edln_insstr_n(edln, str, l, TRUE, TRUE);
174 }
175
176
177 bool edln_insstr_n(Edln *edln, const char *str, int l, 
178                    bool update, bool movepoint)
179 {
180     if(!edln_pspc(edln, l))
181         return FALSE;
182     
183     memmove(&(edln->p[edln->point]), str, l);
184     if(movepoint){
185         edln->point+=l;
186         if(update)
187             UPDATE_CHANGED(edln->point-l);
188     }else{
189         if(update)
190             UPDATE_CHANGED_NOMOVE(edln->point-l);
191     }
192
193     return TRUE;
194 }
195
196
197 /*}}}*/
198
199
200 /*{{{ Transpose */
201
202
203 bool edln_transpose_chars(Edln *edln)
204 {
205     int off1, off2, pos;
206     char *buf;
207
208     if((edln->point==0) || (edln->psize<2))
209         return FALSE;
210
211     pos=edln->point;
212     if(edln->point==edln->psize)
213         pos=pos-str_prevoff(edln->p, edln->point);
214
215     off1=str_nextoff(edln->p, pos);
216     off2=str_prevoff(edln->p, pos);
217
218     buf=ALLOC_N(char, off2);
219     if(buf==NULL)
220         return FALSE;
221     memmove(buf, &(edln->p[pos-off2]), off2);
222     memmove(&(edln->p[pos-off2]), &(edln->p[pos]), off1);
223     memmove(&(edln->p[pos-off2+off1]), buf, off2);
224     FREE(buf);
225
226     if(edln->point!=edln->psize)
227         edln->point+=off1;
228
229     UPDATE_CHANGED(0);
230     return TRUE;
231 }
232
233
234 bool edln_transpose_words(Edln *edln)
235 {
236     int m1, m2, m3, m4, off1, off2, oldp;
237     char *buf;
238
239     if((edln->point==edln->psize) || (edln->psize<3))
240         return FALSE;
241
242     oldp=edln->point;
243     edln_bskip_word(edln);
244     m1=edln->point;
245     edln_skip_word(edln);
246     m2=edln->point;
247     edln_skip_word(edln);
248     if(edln->point==m2)
249         goto noact;
250     m4=edln->point;
251     edln_bskip_word(edln);
252     if(edln->point==m1)
253         goto noact;
254     m3=edln->point;
255
256     off1=m4-m3;
257     off2=m3-m2;
258
259     buf=ALLOC_N(char, m4-m1);
260     if(buf==NULL)
261         goto noact;
262     memmove(buf, &(edln->p[m3]), off1);
263     memmove(buf+off1, &(edln->p[m2]), off2);
264     memmove(buf+off1+off2, &(edln->p[m1]), m2-m1);
265     memmove(&(edln->p[m1]), buf, m4-m1);
266     FREE(buf);
267
268     edln->point=m4;
269     UPDATE_CHANGED(0);
270     return TRUE;
271
272 noact:
273     edln->point=oldp;
274     UPDATE_MOVED(edln->point);
275     return FALSE;
276 }
277
278
279 /*}}}*/
280
281
282 /*{{{ Movement */
283
284
285 static int do_edln_back(Edln *edln)
286 {
287     int l=str_prevoff(edln->p, edln->point);
288     edln->point-=l;
289     return l;
290 }
291
292
293 void edln_back(Edln *edln)
294 {
295     int p=edln->point;
296     do_edln_back(edln);
297     /*if(edln->point!=p)*/
298         UPDATE_MOVED(edln->point);
299 }
300
301
302 static int do_edln_forward(Edln *edln)
303 {
304     int l=str_nextoff(edln->p, edln->point);
305     edln->point+=l;
306     return l;
307 }
308
309
310 void edln_forward(Edln *edln)
311 {
312     int p=edln->point;
313     do_edln_forward(edln);
314     /*if(edln->point!=p)*/
315         UPDATE_MOVED(p);
316 }
317
318
319 void edln_bol(Edln *edln)
320 {
321     if(edln->point!=0){
322         edln->point=0;
323         UPDATE_MOVED(0);
324     }
325 }
326
327
328 void edln_eol(Edln *edln)
329 {
330     int o=edln->point;
331     
332     if(edln->point!=edln->psize){
333         edln->point=edln->psize;
334         UPDATE_MOVED(o);
335     }
336 }
337
338
339 void edln_bskip_word(Edln *edln)
340 {
341     int p, n;
342     CHAR c;
343     
344     while(edln->point>0){
345         n=do_edln_back(edln);
346         c=CHAR_AT(edln->p+edln->point, n);
347         if(ISALNUM(c))
348             goto fnd;
349     }
350     UPDATE_MOVED(edln->point);
351     return;
352     
353 fnd:
354     while(edln->point>0){
355         p=edln->point;
356         n=do_edln_back(edln);
357         c=CHAR_AT(edln->p+edln->point, n);
358
359         if(!ISALNUM(c)){
360             edln->point=p;
361             break;
362         }
363     }
364     UPDATE_MOVED(edln->point);
365 }
366
367
368 void edln_skip_word(Edln *edln)
369 {
370     int oldp=edln->point;
371     CHAR c;
372     
373     while(edln->point<edln->psize){
374         c=CHAR_AT(edln->p+edln->point, edln->psize-edln->point);
375         if(ISALNUM(c))
376             goto fnd;
377         if(do_edln_forward(edln)==0)
378             break;
379     }
380     UPDATE_MOVED(oldp);
381     return;
382     
383 fnd:
384     while(edln->point<edln->psize){
385         c=CHAR_AT(edln->p+edln->point, edln->psize-edln->point);
386         if(!ISALNUM(c))
387             break;
388         if(do_edln_forward(edln)==0)
389             break;
390     }
391     UPDATE_MOVED(oldp);
392 }
393
394
395 void edln_set_point(Edln *edln, int point)
396 {
397     int o=edln->point;
398     
399     if(point<0)
400         point=0;
401     else if(point>edln->psize)
402         point=edln->psize;
403     
404     edln->point=point;
405     
406     if(o<point)
407         UPDATE_MOVED(o);
408     else
409         UPDATE_MOVED(point);
410 }
411
412                
413 /*}}}*/
414
415
416 /*{{{ Delete */
417
418
419 void edln_delete(Edln *edln)
420 {
421     int left=edln->psize-edln->point;
422     size_t l;
423     
424     if(left<=0)
425         return;
426     
427     l=str_nextoff(edln->p, edln->point);
428     
429     if(l>0)
430         edln_rspc(edln, l);
431     
432     UPDATE_CHANGED_NOMOVE(edln->point);
433 }
434
435
436 void edln_backspace(Edln *edln)
437 {
438     int n;
439     if(edln->point==0)
440         return;
441     n=do_edln_back(edln);
442     if(n!=0){
443         edln_rspc(edln, n);
444         UPDATE_CHANGED(edln->point);
445     }
446 }
447
448 void edln_kill_to_eol(Edln *edln)
449 {
450     edln_rspc(edln, edln->psize-edln->point);
451     UPDATE_CHANGED_NOMOVE(edln->point);
452 }
453
454
455 void edln_kill_to_bol(Edln *edln)
456 {
457     int p=edln->point;
458     
459     edln_bol(edln);
460     edln_rspc(edln, p);
461     edln->point=0;
462     UPDATE_CHANGED(0);
463 }
464
465
466 void edln_kill_line(Edln *edln)
467 {
468     edln_bol(edln);
469     edln_kill_to_eol(edln);
470     UPDATE_CHANGED(0);
471 }
472
473
474 void edln_kill_word(Edln *edln)
475 {
476     int oldp=edln->point;
477     int l;
478     edln_skip_word(edln);
479     
480     if(edln->point==oldp)
481         return;
482     
483     l=edln->point-oldp;
484     edln->point=oldp;
485     edln_rspc(edln, l);
486     
487     UPDATE_CHANGED_NOMOVE(oldp);
488 }
489
490
491 void edln_bkill_word(Edln *edln)
492 {
493     int oldp=edln->point;
494     
495     edln_bskip_word(edln);
496     
497     if(edln->point==oldp)
498         return;
499     
500     edln_rspc(edln, oldp-edln->point);
501     UPDATE_CHANGED(edln->point);
502 }
503
504
505 /*}}}*/
506
507
508 /*{{{ Selection */
509
510
511 static void do_set_mark(Edln *edln, int nm)
512 {
513     int m=edln->mark;
514     edln->mark=nm;
515     if(m!=-1)
516         UPDATE(m < edln->point ? m : edln->point);
517 }
518
519
520 void edln_set_mark(Edln *edln)
521 {
522     do_set_mark(edln, edln->point);
523 }
524
525
526 void edln_clear_mark(Edln *edln)
527 {
528     do_set_mark(edln, -1);
529 }
530
531
532 static void edln_do_copy(Edln *edln, bool del)
533 {
534     int beg, end;
535     
536     if(edln->mark<0 || edln->point==edln->mark)
537         return;
538     
539     if(edln->point<edln->mark){
540         beg=edln->point;
541         end=edln->mark;
542     }else{
543         beg=edln->mark;
544         end=edln->point;
545     }
546     
547     ioncore_set_selection_n(edln->p+beg, end-beg);
548     
549     if(del){
550         edln->point=beg;
551         edln_rspc(edln, end-beg);
552     }
553     edln->mark=-1;
554     
555     UPDATE(beg);
556 }
557
558
559 void edln_cut(Edln *edln)
560 {
561     edln_do_copy(edln, TRUE);
562 }
563
564
565 void edln_copy(Edln *edln)
566 {
567     edln_do_copy(edln, FALSE);
568 }
569
570
571 /*}}}*/
572
573
574 /*{{{ History */
575
576
577 bool edln_set_context(Edln *edln, const char *str)
578 {
579     char *s=scat(str, ":"), *cp;
580     
581     if(s==NULL)
582         return FALSE;
583     
584     cp=strchr(s, ':');
585     while(cp!=NULL && *(cp+1)!='\0'){
586         *cp='_';
587         cp=strchr(cp, ':');
588     }
589
590     if(edln->context!=NULL)
591         free(edln->context);
592     edln->context=s;
593         
594     return TRUE;
595 }
596
597
598 static void edln_do_set_hist(Edln *edln, int e, bool match)
599 {
600     const char *str=mod_query_history_get(e), *s2;
601     if(str!=NULL){
602         if(edln->histent<0){
603             edln->tmp_p=edln->p;
604             edln->tmp_palloced=edln->palloced;
605             edln->p=NULL;
606         }
607         
608         /* Skip context label */
609         s2=strchr(str, ':');
610         if(s2!=NULL)
611             str=s2+1;
612         
613         edln->histent=e;
614         edln_setstr(edln, str);
615         edln->point=(match
616                      ? minof(edln->point, edln->psize) 
617                      : edln->psize);
618         edln->mark=-1;
619         edln->modified=FALSE;
620         UPDATE_NEW();
621     }
622 }
623
624
625 static char *history_search_str(Edln *edln)
626 {
627     char *sstr;
628     char tmp=edln->p[edln->point];
629     edln->p[edln->point]='\0';
630     sstr=scat(edln->context ? edln->context : "*:", edln->p);
631     edln->p[edln->point]=tmp;
632     return sstr;
633 }
634
635
636 static int search(Edln *edln, int from, bool bwd, bool match)
637 {
638     int e;
639     
640     if(match && edln->point>0){
641         char *tmpstr=history_search_str(edln);
642         if(tmpstr==NULL)
643             return edln->histent;
644         e=mod_query_history_search(tmpstr, from, bwd, FALSE);
645         free(tmpstr);
646     }else{
647         e=mod_query_history_search(edln->context, from, bwd, FALSE);
648     }
649     
650     return e;
651 }
652
653
654 void edln_history_prev(Edln *edln, bool match)
655 {
656     int e=search(edln, edln->histent+1, FALSE, match);
657     if(e>=0)
658         edln_do_set_hist(edln, e, match);
659 }
660
661
662 void edln_history_next(Edln *edln, bool match)
663 {
664     int e=edln->histent;
665     
666     if(edln->histent<0)
667         return;
668
669     e=search(edln, edln->histent-1, TRUE, match);
670     
671     if(e>=0){
672         edln_do_set_hist(edln, e, match);
673     }else{
674         edln->histent=-1;
675         if(edln->p!=NULL)
676             free(edln->p);
677         edln->p=edln->tmp_p;
678         edln->palloced=edln->tmp_palloced;
679         edln->tmp_p=NULL;
680         edln->psize=(edln->p==NULL ? 0 : strlen(edln->p));
681         edln->point=edln->psize;
682         edln->mark=-1;
683         edln->modified=TRUE;
684         UPDATE_NEW();
685     }
686 }
687
688
689 uint edln_history_matches(Edln *edln, char ***h_ret)
690 {
691     char *tmpstr=history_search_str(edln);
692     uint ret;
693     
694     if(tmpstr==NULL){
695         *h_ret=NULL;
696         return 0;
697     }
698         
699     ret=mod_query_history_complete(tmpstr, h_ret);
700     
701     free(tmpstr);
702     
703     return ret;
704 }
705
706
707 /*}}}*/
708
709
710 /*{{{ Init/deinit */
711
712
713 bool edln_init(Edln *edln, const char *p)
714 {
715     if(p==NULL)
716         p="";
717     
718     if(!edln_initstr(edln, p))
719         return FALSE;
720     
721     edln->point=edln->psize;
722     edln->mark=-1;
723     edln->histent=-1;
724     edln->modified=FALSE;
725     edln->tmp_p=NULL;
726     edln->context=NULL;
727     
728     return TRUE;
729 }
730
731
732 void edln_deinit(Edln *edln)
733 {
734     if(edln->p!=NULL){
735         free(edln->p);
736         edln->p=NULL;
737     }
738     if(edln->tmp_p!=NULL){
739         free(edln->tmp_p);
740         edln->tmp_p=NULL;
741     }
742     if(edln->context!=NULL){
743         free(edln->context);
744         edln->context=NULL;
745     }
746 }
747
748
749 static const char *ctx(Edln *edln)
750 {
751     if(edln->context!=NULL)
752         return edln->context;
753     else
754         return "default:";
755 }
756
757
758 char* edln_finish(Edln *edln)
759 {
760     char *p=edln->p, *hist;
761     
762     if(p!=NULL){
763         libtu_asprintf(&hist, "%s%s", ctx(edln), p);
764         if(hist!=NULL)
765             mod_query_history_push_(hist);
766     }
767     
768     edln->p=NULL;
769     edln->psize=edln->palloced=0;
770     
771     /*stripws(p);*/
772     return str_stripws(p);
773 }
774
775 /*}}}*/
776