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