Browse code

Support the unsaved data recovery plugin from redata. Rework the question interface.

Dario Rodriguez authored on 05/10/2020 21:42:19
Showing 5 changed files
... ...
@@ -94,6 +94,7 @@ redata_init(int (*pluginregisterfn)(redata_t *redata, redata_plugin_t *slot), ..
94 94
         redata->undostack.undo=NULL;
95 95
         redata->undostack.buf=NULL;
96 96
         /* plugins */
97
+        va_start(args,pluginregisterfn);
97 98
         for(nargs=0,fn=pluginregisterfn;fn!=NULL;fn=va_arg(args,int (*)(redata_t *redata,redata_plugin_t *slot))) {
98 99
                 res=fn(redata,redata->plugins+nargs);
99 100
                 redata->plugins[nargs].active=(res==0)?1:0;
... ...
@@ -146,6 +147,20 @@ redata_free(redata_t *redata)
146 147
         return;
147 148
 }
148 149
 
150
+void
151
+redata_idleproc(redata_t *redata, char *filename)
152
+{
153
+        int i;
154
+        if(redata==NULL)
155
+                return;
156
+        for(i=0;i<redata->sizeplugins;i++) {
157
+                if(redata->plugins[i].commit!=NULL)
158
+                        redata->plugins[i].commit(redata,redata->plugins+i,filename);
159
+        }
160
+        return;
161
+}
162
+
163
+
149 164
 int
150 165
 redata_config_chunkdatasize(redata_t *redata, int chunkdatasize)
151 166
 {
... ...
@@ -524,27 +539,94 @@ redata_fix_nl(redata_t *redata, int chunkno)
524 539
 }
525 540
 
526 541
 int
527
-redata_load(redata_t *redata, char *filename, int (*callback_question)(/*char *title, char *body, int nopts, char *opts[],void *userptr*/), void *userptr)
542
+redata_loadquestions_setup(redata_t *redata, char *filename)
543
+{
544
+        int i;
545
+        if(redata==NULL || filename==NULL)
546
+                return(-1); /* sanity check failed */
547
+        redata_loadquestions_wipe(redata);
548
+        /* unsaved and other plugins: loadquestion (pre-to-load) */
549
+        for(i=0;i<redata->sizeplugins;i++) {
550
+                if(!redata->plugins[i].active)
551
+                        continue;
552
+                if(redata->plugins[i].loadquestion!=NULL)
553
+                        redata->plugins[i].loadquestion(redata,redata->plugins+i,filename);
554
+        }
555
+        return(0);
556
+
557
+}
558
+
559
+question_t *
560
+redata_loadquestions_getnext(redata_t *redata)
561
+{
562
+        int i;
563
+        if(redata==NULL)
564
+                return(NULL); /* sanity check failed */
565
+        for(i=0;i<redata->sizeplugins;i++) {
566
+                if(!redata->plugins[i].active || redata->plugins[i].question.active==0)
567
+                        continue;
568
+                if(redata->plugins[i].question.selectedoption==-1)
569
+                        return(&(redata->plugins[i].question));
570
+        }
571
+        return(NULL); /* no more questions */
572
+}
573
+
574
+int
575
+redata_loadquestions_reply(redata_t *redata, question_t *question, int selectedoption)
576
+{
577
+        if(redata==NULL || question==NULL || selectedoption<-1 || selectedoption>=question->nopts)
578
+                return(-1);
579
+        question->selectedoption=(selectedoption==-1)?question->defaultoption:selectedoption;
580
+        return(0);
581
+}
582
+
583
+int
584
+redata_loadquestions_wipe(redata_t *redata)
528 585
 {
586
+        int i;
587
+        if(redata==NULL)
588
+                return(-1);
589
+        for(i=0;i<redata->sizeplugins;i++) {
590
+                if(!redata->plugins[i].active)
591
+                        continue;
592
+                memset(redata->plugins[i].questionfilename,0,sizeof(redata->plugins[i].questionfilename));
593
+                memset(&(redata->plugins[i].question),0,sizeof(redata->plugins[i].question));
594
+                redata->plugins[i].question.active=0;
595
+                redata->plugins[i].question.selectedoption=-1;
596
+        }
597
+        return(0);
598
+}
599
+
600
+int
601
+redata_load(redata_t *redata, char *filename, char **errordesc)
602
+{
603
+#warning TODO: what to do when there plugins with questions AFTER loading instead of before loading...
529 604
         int fd,nread,totalread;
530 605
         int chunkno, avail;
531 606
         struct stat statbuf;
532 607
         rechunk_t *chunk;
533 608
         int i;
534 609
         int reply;
535
-        if(redata==NULL || filename==NULL)
610
+        if(redata==NULL || filename==NULL) {
611
+                if(errordesc!=NULL)
612
+                        *errordesc="Internal error";
536 613
                 return(-1); /* sanity check failed */
614
+        }
537 615
         redata_wipe(redata);
538 616
         strncpy(redata->filename,filename,sizeof(redata->filename));
539 617
         redata->filename[sizeof(redata->filename)-1]='\0';
540 618
         if((fd=open(filename,O_RDONLY))==-1 || fstat(fd,&statbuf)!=0 || !S_ISREG(statbuf.st_mode)) {
541 619
                 if(fd!=-1)
542 620
                         close(fd),fd=-1;
543
-                return(-1); /* file not found, couldn't query size or not regular file */
621
+                if(errordesc!=NULL)
622
+                        *errordesc="File not found, couldn't query size or not a regular file";
623
+                return(-1); /* file not found, couldn't query size or not a regular file */
544 624
         }
545 625
         /* preallocate 10% more than needed */
546 626
         if(redata_preallocate(redata,statbuf.st_size+(statbuf.st_size/10)+1 )) {
547 627
                 close(fd),fd=-1;
628
+                if(errordesc!=NULL)
629
+                        *errordesc="Insufficient memory";
548 630
                 return(-1); /* insuf. mem. */
549 631
         }
550 632
         for(totalread=0,chunkno=0,nread=0;totalread<statbuf.st_size;totalread+=nread,nread=0) {
... ...
@@ -568,6 +650,8 @@ redata_load(redata_t *redata, char *filename, int (*callback_question)(/*char *t
568 650
                 }
569 651
                 if((nread=read(fd,chunk->data+chunk->useddata,avail))<=0) {
570 652
                         close(fd),fd=-1;
653
+                        if(errordesc!=NULL)
654
+                                *errordesc="Short read loading file";
571 655
                         return(-1); /* short read */
572 656
                 }
573 657
                 chunk->useddata+=nread;
... ...
@@ -578,49 +662,56 @@ redata_load(redata_t *redata, char *filename, int (*callback_question)(/*char *t
578 662
                 redata_fix_nl(redata,chunkno);
579 663
         for(chunkno=0;chunkno<redata->sizechunks;chunkno++)
580 664
                 redata_whatin_refresh(redata,chunkno);
581
-        /* unsaved and other plugins */
665
+        /* unsaved and other plugins: postload */
582 666
         for(i=0;i<redata->sizeplugins;i++) {
583 667
                 if(!redata->plugins[i].active)
584 668
                         continue;
585
-                reply=-1;
586
-                if(redata->plugins[i].loadquestion!=NULL && callback_question!=NULL) {
587
-                        char *title, *body;
588
-                        int nopts;
589
-                        char **opts;
590
-                        if(redata->plugins[i].loadquestion(redata,redata->plugins+i,filename,&title,&body,&nopts,&opts)!=-1)
591
-                                reply=callback_question(title,body,nopts,opts,userptr);
592
-                }
593 669
                 if(redata->plugins[i].postload!=NULL)
594
-                        redata->plugins[i].postload(redata,redata->plugins+i,filename,reply);
670
+                        redata->plugins[i].postload(redata,redata->plugins+i,filename);
595 671
         }
672
+        redata_loadquestions_wipe(redata);
673
+#warning TODO: IMPLEMENT POSTLOADQUESTIONS (call now loadquestion_postload).
596 674
         /* all done */
597 675
         return(0);
598 676
 }
599 677
 
600 678
 int
601
-redata_save(redata_t *redata, char *filename)
679
+redata_save(redata_t *redata, char *filename, char **errordesc)
602 680
 {
603 681
         int fd;
604 682
         int i,n;
605 683
         char tmpfile[PATH_MAX+1];
606
-        if(redata==NULL || filename==NULL)
684
+        if(redata==NULL || filename==NULL) {
685
+                if(errordesc!=NULL)
686
+                        *errordesc="Internal error";
607 687
                 return(-1); /* sanity check failed */
608
-        if((securesave_genname(redata->filename,tmpfile,sizeof(tmpfile)))==NULL)
688
+        }
689
+        if((securesave_genname(redata->filename,tmpfile,sizeof(tmpfile)))==NULL) {
690
+                if(errordesc!=NULL)
691
+                        *errordesc="Malformed filename";
609 692
                 return(-1); /* malformed filename */
610
-        if((fd=open(tmpfile,O_WRONLY|O_TRUNC|O_CREAT,0644))==-1)
693
+        }
694
+        if((fd=open(tmpfile,O_WRONLY|O_TRUNC|O_CREAT,0644))==-1) {
695
+                if(errordesc!=NULL)
696
+                        *errordesc="Couldn't open file for writing";
611 697
                 return(-1); /* couldn't open file for writing */
698
+        }
612 699
         for(i=0;i<redata->sizechunks;i++) {
613 700
                 if(redata->chunks[i]->useddata==0)
614 701
                         continue;
615 702
                 if((n=write(fd,redata->chunks[i]->data,redata->chunks[i]->useddata))==-1 || n!=redata->chunks[i]->useddata) {
616 703
                         close(fd),fd=-1;
617 704
                         unlink(tmpfile);
705
+                        if(errordesc!=NULL)
706
+                                *errordesc="Short write saving file";
618 707
                         return(-1); /* short write */
619 708
                 }
620 709
         }
621 710
         close(fd),fd=-1;
622 711
         if(rename(tmpfile,filename)!=0) {
623 712
                 unlink(tmpfile);
713
+                if(errordesc!=NULL)
714
+                        *errordesc="Couldn't overwrite old file";
624 715
                 return(-1); /* couldn't overwrite old file */
625 716
         }
626 717
         for(i=0;i<redata->sizeplugins;i++) {
... ...
@@ -13,8 +13,22 @@
13 13
 
14 14
 #include <limits.h>
15 15
 
16
+#ifndef RE_DATA_H
17
+#define RE_DATA_H
16 18
 #define MAXPLUGINNAME 32
17 19
 
20
+typedef struct question_t {
21
+        int active;
22
+        int is_postload;
23
+        char *id;
24
+        char *title,*titleshort;
25
+        char *body,*bodyshort;
26
+        int nopts;
27
+        char **opts,**optsshort;
28
+        int defaultoption;
29
+        int selectedoption;
30
+} question_t;
31
+
18 32
 typedef struct whatin_t {
19 33
         int nlcount;
20 34
         int iscontinuation;
... ...
@@ -51,11 +65,12 @@ typedef struct undostack_t {
51 65
 typedef struct redata_plugin_t {
52 66
         int active;
53 67
         char name[MAXPLUGINNAME];
68
+        char *questionfilename[PATH_MAX];
69
+        question_t question;
54 70
         int (*unregister)(/*redata_t *redata, redata_plugin_t *plugin,char *filename*/);
55 71
         int (*wipe)(/*redata_t *redata, redata_plugin_t *plugin,char *filename*/);
56
-        /* loadquestion returns -1 if no question needed */
57
-        int (*loadquestion)(/*redata_t *redata, redata_plugin_t *plugin,char *filename, char **title, char **body, int *nopts, char **opts[] */);
58
-        int (*postload)(/*redata_t *redata, redata_plugin_t *plugin,char *filename,int questionreply */);
72
+        int (*loadquestion)(/*redata_t *redata, redata_plugin_t *plugin,char *filename */);
73
+        int (*postload)(/*redata_t *redata, redata_plugin_t *plugin,char *filename */);
59 74
         int (*postsave)(/*redata_t *redata, redata_plugin_t *plugin,char *oldfilename,char *newfilename*/);
60 75
         int (*add)(/*redata_t *redata, redata_plugin_t *plugin,undo_t *undo*/);
61 76
         int (*unadd)(/*redata_t *redata, redata_plugin_t *plugin,undo_t *undo*/);
... ...
@@ -84,6 +99,8 @@ typedef struct redata_t {
84 99
 redata_t *redata_init( int (*pluginregisterfn)(redata_t *redata, redata_plugin_t *slot), ...);
85 100
 void redata_free(redata_t *redata);
86 101
 
102
+void redata_idleproc(redata_t *redata, char *filename);
103
+
87 104
 int redata_config_chunkdatasize(redata_t *redata, int chunkdatasize);
88 105
 
89 106
 long redata_getsize(redata_t *redata);
... ...
@@ -119,8 +136,13 @@ int redata_undo_groupinit(redata_t *redata, undostack_t *stack);
119 136
 int redata_undo_groupcommit(redata_t *redata, undostack_t *stack);
120 137
 
121 138
 /* high level stuff */
122
-int redata_load(redata_t *redata, char *filename, int (*callback_question)(/*char *title, char *body, int nopts, char *opts[],void *userptr*/), void *userptr);
123
-int redata_save(redata_t *redata, char *filename);
139
+int redata_loadquestions_setup(redata_t *redata, char *filename);
140
+question_t *redata_loadquestions_getnext(redata_t *redata);
141
+int redata_loadquestions_reply(redata_t *redata, question_t *question, int selectedoption);
142
+int redata_loadquestions_wipe(redata_t *redata);
143
+
144
+int redata_load(redata_t *redata, char *filename, char **errordesc);
145
+int redata_save(redata_t *redata, char *filename, char **errordesc);
124 146
 
125 147
 int redata_op_add(redata_t *redata, long pos, char *buf, long buflen, undostack_t *fromhere);
126 148
 int redata_op_addn(redata_t *redata, long pos, char character, long n, undostack_t *fromhere);
... ...
@@ -170,3 +192,4 @@ int redata_pos2linecol(redata_t *redata, long pos, int *line, int *col);
170 192
 /* get the pos of that line/col (calculated using the utf8 convenience functions) */
171 193
 int redata_linecol2pos(redata_t *redata, int line, int colrequest, long *pos, int *coldone);
172 194
 
195
+#endif
... ...
@@ -28,8 +28,8 @@
28 28
 
29 29
 
30 30
 
31
-static int redata_unsaved_loadquestion(redata_t *redata, redata_plugin_t *slot,char *filename, char **title, char **body, int *nopts, char **opts[]);
32
-static int redata_unsaved_postload(redata_t *redata, redata_plugin_t *slot,char *filename,int questionreply);
31
+static int redata_unsaved_loadquestion(redata_t *redata, redata_plugin_t *slot,char *filename);
32
+static int redata_unsaved_postload(redata_t *redata, redata_plugin_t *slot,char *filename);
33 33
 static int redata_unsaved_check_gen(redata_t *redata, redata_plugin_t *slot, char *filename);
34 34
 static char *unsaved_genname(char *filename, char *buf, int bufsize);
35 35
 static char *ptr_getlong(char *ptr,char *endptr,long *data);
... ...
@@ -58,6 +58,7 @@ redata_unsaved_register(redata_t *redata, redata_plugin_t *slot)
58 58
         slot->add=redata_unsaved_add;
59 59
         slot->unadd=redata_unsaved_unadd;
60 60
         slot->commit=redata_unsaved_commit;
61
+        slot->userptr=unsaved;
61 62
         return(0);
62 63
 }
63 64
 
... ...
@@ -205,6 +206,9 @@ redata_unsaved_trunc(redata_t *redata, redata_plugin_t *slot, char *oldfilename,
205 206
 int
206 207
 redata_unsaved_truncload(redata_t *redata, redata_plugin_t *slot, char *filename)
207 208
 {
209
+#if 1
210
+fprintf(stderr,"UNSAVED: TRUNCLOAD (ignore)\n");
211
+#endif
208 212
         return(redata_unsaved_trunc(redata, slot, NULL, filename));
209 213
 }
210 214
 
... ...
@@ -224,16 +228,28 @@ redata_unsaved_loadappend(redata_t *redata, redata_plugin_t *slot, char *filenam
224 228
         char endcode;
225 229
         int flag_multipart;
226 230
         unsaved_t *unsaved=(unsaved_t *) ((slot!=NULL)?(slot->userptr):NULL);
231
+#if 1
232
+fprintf(stderr,"UNSAVED: LOADAPPEND (recover)\n");
233
+#endif
227 234
         if(redata==NULL || slot==NULL || unsaved==NULL || filename==NULL)
228 235
                 return(-1);
236
+#if 1
237
+fprintf(stderr,"UNSAVED: LOADAPPEND pre-check\n");
238
+#endif
229 239
         if((fd=redata_unsaved_check_gen(redata,slot,filename))==-1)
230 240
                 return(-1); /* check failed */
241
+#if 1
242
+fprintf(stderr,"UNSAVED: LOADAPPEND post-check\n");
243
+#endif
231 244
         if(fstat(fd,&statbuf)!=0 || !S_ISREG(statbuf.st_mode)) {
232 245
                 close(fd),fd=-1;
233 246
                 return(-1); /* couldn't query size or not regular file */
234 247
         }
235 248
         redata_hash(redata,unsaved->initialhash);
236
-        headerhashsize=(sizeof(header)-1)+1+128+1;
249
+        headerhashsize=(sizeof(header)-1)+128+1;
250
+#if 1
251
+fprintf(stderr,"UNSAVED: LOADAPPEND headerhashsize: %li\n",headerhashsize);
252
+#endif
237 253
         /* load unsaved to memory */
238 254
         if(unsaved->sizebuf<(statbuf.st_size-headerhashsize)) {
239 255
                 if((newptr=realloc(unsaved->buf,(statbuf.st_size-headerhashsize)))==NULL) {
... ...
@@ -254,8 +270,21 @@ redata_unsaved_loadappend(redata_t *redata, redata_plugin_t *slot, char *filenam
254 270
         }
255 271
         close(fd),fd=-1;
256 272
         /* process unsaved data */
273
+        slot->active=0;
257 274
         endptr=unsaved->buf+unsaved->usedbuf;
258 275
         for(ptr=unsaved->buf;ptr<endptr;) {
276
+#ifdef DEBUG_PLUGIN_UNSAVED
277
+{
278
+int m;
279
+fprintf(stderr,"%05X: ",ptr-unsaved->buf);
280
+for(m=0;m<16 && (ptr+m)<endptr;m++)
281
+        fprintf(stderr,"%02X%s ",((unsigned char *)ptr)[m],(m==7)?" ":"");
282
+fprintf(stderr," | ");
283
+for(m=0;m<16 && (ptr+m)<endptr;m++)
284
+        fprintf(stderr,"%c%s",((((unsigned char *)ptr)[m])<20)?'.':((((unsigned char *)ptr)[m])>126)?'.':((unsigned char *)ptr)[m],(m==7)?" ":"");
285
+fprintf(stderr,"\n");
286
+}
287
+#endif
259 288
                 if((ptr=ptr_getchar(ptr,endptr,&actioncode))==NULL)
260 289
                         return(-1); /* no space for action char */
261 290
                 /* multipart example: A10+$aj$%$% >> insert "aj$" into pos 10 */
... ...
@@ -322,7 +351,8 @@ redata_unsaved_loadappend(redata_t *redata, redata_plugin_t *slot, char *filenam
322 351
                         return(-1); /* corrupted undobuf */
323 352
                 }
324 353
         }
325
-        return(-1);
354
+        slot->active=1;
355
+        return(0);
326 356
 }
327 357
 
328 358
 int
... ...
@@ -337,7 +367,7 @@ redata_unsaved_add(redata_t *redata, redata_plugin_t *slot, undo_t *undo)
337 367
         undostack_t *stack;
338 368
         unsaved_t *unsaved=(unsaved_t *) ((slot!=NULL)?(slot->userptr):NULL);
339 369
         stack=redata_getstack(redata,undo);
340
-        if(redata==NULL || slot==NULL || unsaved==NULL || undo==NULL || stack==NULL)
370
+        if(redata==NULL || slot==NULL || unsaved==NULL || undo==NULL || stack==NULL || slot->active==0)
341 371
                 return(-1); /* sanity check failed */
342 372
         /* syntax (see loadappend): A<pos><+?><sep><text><sep>[<+?><sep><text><sep>[...]] */
343 373
         if(undo->type!='A' && undo->type!='D' && undo->type!='M')
... ...
@@ -389,7 +419,8 @@ redata_unsaved_add(redata_t *redata, redata_plugin_t *slot, undo_t *undo)
389 419
                                 unsaved->sizebuf=newsize;
390 420
                         }
391 421
                         buf=unsaved->buf+unsaved->usedbuf;
392
-                }
422
+                } else
423
+                        unsaved->usedbuf+=maxsize;
393 424
         }
394 425
         return(0);
395 426
 }
... ...
@@ -407,7 +438,7 @@ redata_unsaved_unadd(redata_t *redata, redata_plugin_t *slot, undo_t *undo)
407 438
         undostack_t *stack;
408 439
         unsaved_t *unsaved=(unsaved_t *) ((slot!=NULL)?(slot->userptr):NULL);
409 440
         stack=redata_getstack(redata,undo);
410
-        if(redata==NULL || slot==NULL || unsaved==NULL || undo==NULL || stack==NULL)
441
+        if(redata==NULL || slot==NULL || unsaved==NULL || undo==NULL || stack==NULL || slot->active==0)
411 442
                 return(-1); /* sanity check failed */
412 443
         /* syntax (see loadappend): A<pos><+?><sep><text><sep>[<+?><sep><text><sep>[...]] */
413 444
         if(undo->type!='A' && undo->type!='D' && undo->type!='M')
... ...
@@ -475,6 +506,9 @@ redata_unsaved_commit(redata_t *redata, redata_plugin_t *slot,char *filename)
475 506
         unsaved_t *unsaved=(unsaved_t *) ((slot!=NULL)?(slot->userptr):NULL);
476 507
         if(redata==NULL || slot==NULL || unsaved==NULL || unsaved->unsavedfd==-1)
477 508
                 return(-1);
509
+#if 1
510
+fprintf(stderr,"UNSAVED_COMMITTED\n");
511
+#endif
478 512
         for(nwritten=0;nwritten<unsaved->usedbuf;nwritten+=n) {
479 513
                 if((n=write(unsaved->unsavedfd,unsaved->buf+nwritten,unsaved->usedbuf-nwritten))<0) {
480 514
                         close(unsaved->unsavedfd),unsaved->unsavedfd=-1;
... ...
@@ -488,30 +522,47 @@ redata_unsaved_commit(redata_t *redata, redata_plugin_t *slot,char *filename)
488 522
 }
489 523
 
490 524
 static int
491
-redata_unsaved_loadquestion(redata_t *redata, redata_plugin_t *slot,char *filename, char **title, char **body, int *nopts, char **opts[])
525
+redata_unsaved_loadquestion(redata_t *redata, redata_plugin_t *slot,char *filename)
492 526
 {
493 527
         static char mytitle[]={"Unsaved data from a previous session detected"};
494 528
         static char mybody[]={"The file you're trying to load has some unsaved data from a previous session. I can recover the data. What do you want to do?"};
495 529
         static char *myopts[]={"Recover unsaved data (safest option)","Don't use old unsaved data, delete old unsaved data (if you're sure what you're doing)"};
496
-        if(redata==NULL || slot==NULL || filename==NULL || title==NULL || body==NULL || nopts==NULL || opts==NULL)
530
+        static char mytitleshort[]={"Unsaved data found"};
531
+        static char mybodyshort[]={"Should try to recover the data?"};
532
+        static char *myoptsshort[]={"Recover","Ignore it"};
533
+        question_t *q;
534
+        if(redata==NULL || slot==NULL || filename==NULL)
497 535
                 return(-1); /* sanity check failed */
536
+        q=&(slot->question);
537
+        q->active=0;
498 538
         if(redata_unsaved_check(redata,slot,filename)==-1)
499
-                return(-1); /* no unsaved data on disk */
500
-        *title=mytitle;
501
-        *body=mybody;
502
-        *opts=myopts;
503
-        *nopts=2;
539
+                return(0); /* no unsaved data on disk */
540
+        q->active=1;
541
+        q->title=mytitle;
542
+        q->titleshort=mytitleshort;
543
+        q->body=mybody;
544
+        q->bodyshort=mybodyshort;
545
+        q->opts=myopts;
546
+        q->optsshort=myoptsshort;
547
+        q->nopts=2;
548
+        q->defaultoption=1;
549
+        q->selectedoption=-1;
504 550
         return(0);
505 551
 }
506 552
 
507 553
 static int
508
-redata_unsaved_postload(redata_t *redata, redata_plugin_t *slot,char *filename,int questionreply)
554
+redata_unsaved_postload(redata_t *redata, redata_plugin_t *slot,char *filename)
509 555
 {
556
+#warning XXX TODO: add errordesc parameter, so that the user can see what happened.
557
+        int selectedoption;
558
+        if(redata==NULL || slot==NULL || filename==NULL)
559
+                return(-1); /* sanity check failed */
510 560
         /* questionreply is from loadquestion, specifically the selected element in the array "opts" */
511
-        if(questionreply==0) /* recover */
512
-                return(redata_unsaved_loadappend(redata, slot, filename));
513
-        else if(questionreply==1 || questionreply==-1) /* delete unsaved data */
561
+        selectedoption=slot->question.selectedoption;
562
+        if(slot->question.active==0 || selectedoption==1 || selectedoption==-1) /* delete unsaved data */
514 563
                 return(redata_unsaved_truncload(redata, slot, filename));
564
+        else if(selectedoption==0) /* recover */
565
+                return(redata_unsaved_loadappend(redata, slot, filename));
515 566
         return(-1);
516 567
 }
517 568
 
... ...
@@ -154,7 +154,7 @@ fprintf(stderr,"\ntest_newfile(%s%s%s,%s%s%s,%i,%i);\nResult: ",(filename!=NULL)
154 154
         redata_filehash(redata,filename,hash129_pre);
155 155
         free(mem),mem=NULL;
156 156
         /* load file */
157
-        if(redata_load(redata,filename,NULL,NULL)!=0) {
157
+        if(redata_load(redata,filename,NULL)!=0) {
158 158
                 unlink(filename);
159 159
                 return("couldn't load file");
160 160
         }
... ...
@@ -163,7 +163,7 @@ fprintf(stderr,"\ntest_newfile(%s%s%s,%s%s%s,%i,%i);\nResult: ",(filename!=NULL)
163 163
         if(strcmp(hash129_pre,hash129_post)!=0)
164 164
                 return("loaded file is corrupted");
165 165
         /* save file */
166
-        redata_save(redata,filename);
166
+        redata_save(redata,filename,NULL);
167 167
         hash129_post[0]='\0';
168 168
         redata_filehash(redata,filename,hash129_post);
169 169
         if(strcmp(hash129_pre,hash129_post)!=0)
... ...
@@ -201,7 +201,7 @@ fprintf(stderr,"\ntest_edit(%s%s%s,%s%s%s,%i,%i);\nResult: ",(filename!=NULL)?"\
201 201
                                 progress[l+1]='\0';
202 202
                         }
203 203
 if(k!=0) unlink("test.pre"),unlink("test.post"),unlink("test.undo"),unlink("test.redo");
204
-if(k!=0) redata_save(redata,"test.pre");
204
+if(k!=0) redata_save(redata,"test.pre",NULL);
205 205
                         if(*ptr=='A') {
206 206
                                 /* A<insertpos><endchar><text><endchar> */
207 207
                                 ptr++;
... ...
@@ -318,7 +318,7 @@ if(k!=0) redata_save(redata,"test.pre");
318 318
                         if(cursize>maxsize)
319 319
                                 maxsize=cursize;
320 320
                         if(k!=0) {
321
-if(k!=0) redata_save(redata,"test.post");
321
+if(k!=0) redata_save(redata,"test.post",NULL);
322 322
                                 redata_hash(redata,hash129_redata);
323 323
                                 redata_memhash(redata,mem,cursize,hash129_mem);
324 324
                                 if(strcmp(hash129_redata,hash129_mem)!=0) {
... ...
@@ -330,7 +330,7 @@ if(k!=0) redata_save(redata,"test.post");
330 330
                                         return(errorbuf);
331 331
                                 }
332 332
                                 redata_op_undo(redata,NULL);
333
-redata_save(redata,"test.undo");
333
+redata_save(redata,"test.undo",NULL);
334 334
                                 redata_hash(redata,hash129_redata);
335 335
                                 if(strcmp(hash129_redata,hash129_memold)!=0) {
336 336
                                         if(mem!=NULL)
... ...
@@ -341,7 +341,7 @@ redata_save(redata,"test.undo");
341 341
                                         return(errorbuf);
342 342
                                 }
343 343
                                 redata_op_redo(redata,NULL);
344
-redata_save(redata,"test.redo");
344
+redata_save(redata,"test.redo",NULL);
345 345
                                 redata_hash(redata,hash129_redata);
346 346
                                 if(strcmp(hash129_redata,hash129_mem)!=0) {
347 347
                                         if(mem!=NULL)
... ...
@@ -359,7 +359,7 @@ redata_save(redata,"test.redo");
359 359
                                 return("insuf. mem. for temp. buffer");
360 360
                         if(write_file(filename,mem,filesize)==-1)
361 361
                                 return("couldn't create temporary file");
362
-                        if(redata_load(redata,filename,NULL,NULL)!=0) {
362
+                        if(redata_load(redata,filename,NULL)!=0) {
363 363
                                 unlink(filename);
364 364
                                 return("couldn't load file");
365 365
                         }
... ...
@@ -13,18 +13,24 @@
13 13
 #include <string.h>
14 14
 #include <limits.h>
15 15
 #include <signal.h>
16
+#include <time.h>
16 17
 
17 18
 #include "re_data.h"
19
+#include "re_plugin_unsaved.h"
18 20
 #include "re_ui.h"
19 21
 #include "ext/socklib.h"
20 22
 
21 23
 #define LINEFORCESCROLL 1
22 24
 #define COLFORCESCROLL 5
23 25
 
26
+#define IDLETIMEOUTSECONDS 10
27
+
24 28
 #define COMMANDBUFSIZE 1024
25 29
 
26 30
 #define COMMAND_WARNING "(!)"
27 31
 #define COMMAND_GOTOLINE "Go to line: "
32
+#define COMMAND_QUESTION "(?)"
33
+#define COMMAND_EXIT "Exit"
28 34
 
29 35
 #define COLOR_STATUSBG "\x00\x00\xff\xff"
30 36
 #define COLOR_STATUSFG "\xff\xff\x00\xff"
... ...
@@ -48,8 +54,10 @@ typedef struct re_t {
48 54
         int maxrow,maxcol;
49 55
         int headerdirty;
50 56
         int contentsdirty;
57
+        int command_first_key;
51 58
         char *command;
52 59
         char commandbuf[COMMANDBUFSIZE];
60
+        question_t *question;
53 61
         int ignorenkeys;
54 62
 } re_t;
55 63
 
... ...
@@ -86,6 +94,8 @@ main(int argc, char *argv[])
86 94
         SDL_Event event;
87 95
         sselect *ssel;
88 96
         int flag_had_events;
97
+        time_t lastidle,now;
98
+        int load_pending;
89 99
         if(argc!=2 || strcmp(argv[argc-1],"--help")==0) {
90 100
                 fprintf(stderr,"Syntax: %s filename\n",argv[0]);
91 101
                 return(1);
... ...
@@ -104,18 +114,6 @@ main(int argc, char *argv[])
104 114
                 re_free(re),re=NULL;
105 115
                 return(2);
106 116
         }
107
-        if((redata_load(re->data,re->filename,NULL,NULL))!=0)
108
-                re->flag_newfile=1;
109
-        else
110
-                re->flag_newfile=0;
111
-#if 0
112
-#warning TESTS
113
-        {
114
-                char buf[129];
115
-                redata_hash(re->data,buf);
116
-                fprintf(stderr,"%s %s\n",buf,re->filename);
117
-        }
118
-#endif
119 117
         reui_title(re->ui,re->filename);
120 118
         flag_sigint=flag_sigpipe=0;
121 119
         setsignal(SIGINT,sighandler_sigint);
... ...
@@ -134,13 +132,43 @@ main(int argc, char *argv[])
134 132
         re->contentsdirty=1;
135 133
         flag_had_events=0;
136 134
         SDL_StartTextInput();
135
+        lastidle=0;
136
+        load_pending=1;
137
+        redata_loadquestions_setup(re->data, re->filename);
137 138
         while(do_exit==0 && flag_sigint==0) {
139
+                /* load file (or ask pre-load questions), if pending */
140
+                if(load_pending && re->command==NULL) {
141
+                        question_t *question;
142
+                        if((question=redata_loadquestions_getnext(re->data))!=NULL) {
143
+                                re->command=COMMAND_QUESTION;
144
+                                re->question=question;
145
+#if 0
146
+fprintf(stderr,"QUESTION INIT: %s: %s\n",re->question->title,re->question->body);
147
+#endif
148
+                        } else {
149
+                                if(redata_load(re->data,re->filename,NULL)==-1)
150
+                                        re->flag_newfile=1;
151
+                                else
152
+                                        re->flag_newfile=0;
153
+#warning XXX TODO: NO PARECE FUNCIONAR EL PLUGIN_UNSAVED (pregunta pero no carga los cambios)
154
+                                re->originline=re->origincol=0;
155
+                                re->curline=re->curcol=0;
156
+                                re->cursorpos=0;
157
+                                load_pending=0;
158
+                                re->headerdirty=1;
159
+                                re->contentsdirty=1;
160
+                        }
161
+                }
138 162
                 if(re->headerdirty) {
139 163
                         if(re->command==NULL) {
164
+#if 0
140 165
 fprintf(stderr,"REDRAW Header (editing)\n");
166
+#endif
141 167
                                 re_drawheader_editing(re);
142 168
                         } else {
169
+#if 0
143 170
 fprintf(stderr,"REDRAW Header (command)\n");
171
+#endif
144 172
                                 re_drawheader_command(re);
145 173
                                 if(strcmp(re->command,COMMAND_WARNING)==0) {
146 174
                                         /* the warnings only get shown once, remove it */
... ...
@@ -182,6 +210,15 @@ fprintf(stderr,"REDRAW Header (command)\n");
182 210
                                         break;
183 211
                         }
184 212
                 }
213
+                /* additional processing of global commands */
214
+                if(re->command!=NULL && strcmp(re->command,COMMAND_EXIT)==0)
215
+                        do_exit=1;
216
+                /* timeouts */
217
+                now=time(NULL);
218
+                if((lastidle+IDLETIMEOUTSECONDS)<now || (lastidle-IDLETIMEOUTSECONDS)>now) {
219
+                        lastidle=now;
220
+                        redata_idleproc(re->data,re->filename);
221
+                }
185 222
         }
186 223
         SDL_StopTextInput();
187 224
         sselect_free(ssel),ssel=NULL;
... ...
@@ -219,7 +256,7 @@ re_init(void)
219 256
         if((re=malloc(sizeof(re_t)))==NULL)
220 257
                 return(NULL); /* insuf. mem. */
221 258
         memset(re,0,sizeof(re_t));
222
-        if((re->data=redata_init(NULL))==NULL) {
259
+        if((re->data=redata_init(redata_unsaved_register,NULL))==NULL) {
223 260
                 re_free(re),re=NULL;
224 261
                 return(NULL); /* insuf. mem. */
225 262
         }
... ...
@@ -326,7 +363,7 @@ fprintf(stderr,"SDL_TEXTEDITING: composition:\"%s\" start:%i len:%i\n",event->ed
326 363
         /* default case: keydown event */
327 364
         if(event->type!=SDL_KEYDOWN)
328 365
                 return(0); /* Ignore other possible events */
329
-#if 1
366
+#if 0
330 367
 fprintf(stderr,"SDL_KEYDOWN: sym:%i\n",event->key.keysym.sym);
331 368
 #endif
332 369
         if(event->key.keysym.sym==SDLK_DOWN || event->key.keysym.sym==SDLK_UP) {
... ...
@@ -411,10 +448,15 @@ fprintf(stderr,"SDL_KEYDOWN: sym:%i\n",event->key.keysym.sym);
411 448
         } else if(event->key.keysym.sym==SDLK_q && (SDL_GetModState()&KMOD_CTRL)!=0) {
412 449
 fprintf(stderr,"re_processkey(): received Control+q\n");
413 450
                 re->command="";
451
+                re->command_first_key='q';
452
+                re->headerdirty=1;
453
+        } else if(event->key.keysym.sym==SDLK_k && (SDL_GetModState()&KMOD_CTRL)!=0) {
454
+fprintf(stderr,"re_processkey(): received Control+k\n");
455
+                re->command="";
456
+                re->command_first_key='k';
414 457
                 re->headerdirty=1;
415 458
         } else if(event->key.keysym.sym==SDLK_z && (SDL_GetModState()&KMOD_CTRL)!=0) {
416 459
                 long newpos;
417
-                int oldline;
418 460
 fprintf(stderr,"re_processkey(): received Control+z\n");
419 461
                 if(redata_op_undo(re->data,&newpos))
420 462
                         return(-1);
... ...
@@ -439,10 +481,14 @@ re_processkey_commandwait(re_t *re, SDL_Event *event)
439 481
         /* default case: keydown event */
440 482
         if(event->type!=SDL_KEYDOWN)
441 483
                 return(0); /* Ignore other possible events */
442
-        if(event->key.keysym.sym==SDLK_l) {
484
+        if(re->command_first_key=='q' && event->key.keysym.sym==SDLK_l) {
443 485
                 re->command=COMMAND_GOTOLINE;
444 486
                 re->commandbuf[0]='\0';
445 487
                 re->headerdirty=1;
488
+        } else if(re->command_first_key=='k' && event->key.keysym.sym==SDLK_q) {
489
+                re->command=COMMAND_EXIT;
490
+                re->commandbuf[0]='\0';
491
+                re->headerdirty=1;
446 492
         } else {
447 493
                 re->command=NULL;
448 494
                 re->headerdirty=1;
... ...
@@ -478,6 +524,7 @@ re_processkey_commanddata(re_t *re, SDL_Event *event)
478 524
                 re->command=NULL;
479 525
                 re->commandbuf[0]='\0';
480 526
                 re->headerdirty=1;
527
+#warning XXX: TODO: re->question: Make cursor change option, make numbers change option
481 528
         } else if(event->key.keysym.sym==SDLK_BACKSPACE && re->commandbuf[0]!='\0') {
482 529
                 int nchar;
483 530
                 char *ptr;
... ...
@@ -517,6 +564,17 @@ re_processcommand(re_t *re)
517 564
                 re->originline=line-(re->maxrow/2);
518 565
                 re->headerdirty=1;
519 566
                 re->contentsdirty=1;
567
+        } else if(strcmp(re->command,COMMAND_QUESTION)==0) {
568
+                /* validate reply */
569
+                if(!(atoi(re->commandbuf)>0 && atoi(re->commandbuf)<=re->question->nopts)) {
570
+                        /* invalid reply: reset buf */
571
+                        re->commandbuf[0]=0;
572
+                        re->headerdirty=1;
573
+                        return(-1);
574
+                }
575
+                /* send reply */
576
+                redata_loadquestions_reply(re->data,re->question,atoi(re->commandbuf)-1);
577
+                re->headerdirty=1;
520 578
         }
521 579
         re->command=NULL;
522 580
         re->commandbuf[0]='\0';
... ...
@@ -536,7 +594,9 @@ re_moveupdown(re_t *re, int totalinc)
536 594
         inc=(totalinc<0)?-1:1;
537 595
         newpos=re->cursorpos; /* get rid of compiler warning (will be overwitten in the loop as totalinc never is 0) */
538 596
         for(doneinc=0;doneinc!=totalinc;doneinc+=inc) {
597
+#if 0
539 598
 fprintf(stderr,"MOVING from cursorpos:%li line:%i col:%i\n",re->cursorpos,re->curline,re->curcol);
599
+#endif
540 600
                 if((inc==-1 && redata_line_prevrealstart(re->data,re->cursorpos,&realstart)==-1)
541 601
                    || (inc==1 && redata_line_nextrealstart(re->data,re->cursorpos,&realstart)==-1)) {
542 602
                         break; /* couldn't get current line data, we are at start/end */
... ...
@@ -549,17 +609,25 @@ fprintf(stderr,"MOVING from cursorpos:%li line:%i col:%i\n",re->cursorpos,re->cu
549 609
                 }
550 610
                 if(re->curline>(re->originline+re->maxrow-LINEFORCESCROLL))
551 611
                         re->originline+=LINEFORCESCROLL;
612
+#if 0
552 613
 fprintf(stderr,"MOVING   to cursorpos:%li line:%i col:%i\n",re->cursorpos,re->curline,re->curcol);
614
+#endif
553 615
         }
554 616
         if(redata_line_inccol(re->data,re->cursorpos,re->curcol,&newpos2,NULL)==-1) {
555 617
                 /* error advancing cursor, "emergency" repositioning */
618
+#if 0
556 619
 fprintf(stderr,"COLUMN ERROR\n");
620
+#endif
557 621
                 re->curcol=0;
558 622
                 newpos2=newpos;
559 623
         }
624
+#if 0
560 625
 fprintf(stderr,"COLUMN from cursorpos:%li line:%i col:%i\n",re->cursorpos,re->curline,re->curcol);
626
+#endif
561 627
         re->cursorpos=newpos2;
628
+#if 0
562 629
 fprintf(stderr,"COLUMN   to cursorpos:%li line:%i col:%i\n",re->cursorpos,re->curline,re->curcol);
630
+#endif
563 631
         re->contentsdirty=1;
564 632
         re->headerdirty=1;
565 633
         return(0);
... ...
@@ -701,6 +769,21 @@ re_drawheader_command(re_t *re)
701 769
         } else if(strcmp(re->command,COMMAND_WARNING)==0) {
702 770
                 reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,COLOR_WARNINGBG);
703 771
                 reui_printf(re->ui,0,0,COLOR_WARNINGFG,"%s %s",re->command,re->commandbuf);
772
+        } else if(strcmp(re->command,COMMAND_QUESTION)==0) {
773
+                question_t *q;
774
+                reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,COLOR_QUERYBG);
775
+#warning XXX TODO: suppont arbitrary number of options, highlight current option
776
+                q=re->question;
777
+                re->commandbuf[sizeof(re->commandbuf)-1]='\0';
778
+                reui_printf(re->ui,0,0,COLOR_QUERYFG,"%s %s: %s %s%s%s%s%s%s%s%s: %s"
779
+                   ,re->command
780
+                   ,(q->titleshort!=NULL)?q->titleshort:q->title
781
+                   ,(q->bodyshort!=NULL)?q->bodyshort:q->body
782
+                   ,(q->nopts>0)?"1.":"",(q->nopts>0)?(q->optsshort!=NULL)?q->optsshort[0]:q->opts[0]:""
783
+                   ,(q->nopts>1)?" 2.":"",(q->nopts>1)?(q->optsshort!=NULL)?q->optsshort[1]:q->opts[1]:""
784
+                   ,(q->nopts>2)?" 3.":"",(q->nopts>2)?(q->optsshort!=NULL)?q->optsshort[2]:q->opts[2]:""
785
+                   ,(q->nopts>3)?" 4.":"",(q->nopts>4)?(q->optsshort!=NULL)?q->optsshort[3]:q->opts[3]:""
786
+                   ,re->commandbuf);
704 787
         } else {
705 788
                 reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,COLOR_QUERYBG);
706 789
                 re->commandbuf[sizeof(re->commandbuf)-1]='\0';