another update from CVS HEAD, for QA
[alioth/jupp.git] / ufile.c
1 /*
2  *      User file operations
3  *      Copyright
4  *              (C) 1992 Joseph H. Allen
5  *
6  *      This file is part of JOE (Joe's Own Editor)
7  */
8 #define EXTERN_UFILE_C
9 #include "config.h"
10 #include "types.h"
11
12 __RCSID("$MirOS: contrib/code/jupp/ufile.c,v 1.25 2017/12/08 03:24:16 tg Exp $");
13
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18
19 #ifdef UTIME
20 #include <utime.h>
21 #define HAVEUTIME 1
22 #else
23 #ifdef SYSUTIME
24 #include <sys/utime.h>
25 #define HAVEUTIME 1
26 #endif
27 #endif
28
29 #include "b.h"
30 #include "bw.h"
31 #include "macro.h"
32 #include "main.h"
33 #include "menu.h"
34 #include "path.h"
35 #include "pw.h"
36 #include "qw.h"
37 #include "scrn.h"
38 #include "tab.h"
39 #include "tty.h"
40 #include "tw.h"
41 #include "ublock.h"
42 #include "uerror.h"
43 #include "ufile.h"
44 #include "ushell.h"
45 #include "utils.h"
46 #include "va.h"
47 #include "vs.h"
48 #include "charmap.h"
49 #include "w.h"
50
51 extern int orphan;
52 unsigned char *backpath = NULL;         /* Place to store backup files */
53 static B *filehist = NULL;      /* History of file names */
54 int nobackups = 0;
55 int exask = 0;
56
57 /* Ending message generator */
58 /**** message which is shown after closing joe (CTRL+x; CTRL+k) *****/
59 void genexmsg(BW *bw, int saved, unsigned char *name)
60 {
61         unsigned char *s;
62
63         if (bw->b->name && bw->b->name[0]) {
64                 s = bw->b->name;
65         } else {
66                 s = US "(Unnamed)";
67         }
68
69         if (name) {
70                 if (saved) {
71                         joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "File %s saved", name);
72                 } else {
73                         joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "File %s not saved", name);
74                 }
75         } else if (bw->b->changed && bw->b->count == 1) {
76                 joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "File %s not saved", s);
77         } else if (saved) {
78                 joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "File %s saved", s);
79         } else {
80                 joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "File %s not changed so no update needed", s);
81         }
82         msgnw(bw->parent, msgbuf);
83
84         if (exmsg)
85                 vsrm(exmsg);
86
87         if (bw->b->changed && bw->b->count == 1) {
88                 exmsg = vsncpy(NULL, 0, sc("File "));
89                 exmsg = vsncpy(sv(exmsg), sz(s));
90                 exmsg = vsncpy(sv(exmsg), sc(" not saved."));
91         } else if (saved) {
92                 exmsg = vsncpy(NULL, 0, sc("File "));
93                 exmsg = vsncpy(sv(exmsg), sz(s));
94                 exmsg = vsncpy(sv(exmsg), sc(" saved."));
95         } else {
96                 exmsg = vsncpy(NULL, 0, sc("File "));
97                 exmsg = vsncpy(sv(exmsg), sz(s));
98                 exmsg = vsncpy(sv(exmsg), sc(" not changed so no update needed."));
99         }
100 }
101
102 /* For ^X ^C */
103 static void
104 genexmsgmulti(BW *bw, int saved, int skipped)
105 {
106         if (saved)
107                 if (skipped)
108                         joe_snprintf_0((char *)msgbuf, JOE_MSGBUFSIZE, "Some files have not been saved.");
109                 else
110                         joe_snprintf_0((char *)msgbuf, JOE_MSGBUFSIZE, "All modified files have been saved.");
111         else
112                 joe_snprintf_0((char *)msgbuf, JOE_MSGBUFSIZE, "No modified files, so no updates needed.");
113
114         msgnw(bw->parent, msgbuf);
115
116         exmsg = vsncpy(NULL,0,sz(msgbuf));
117 }
118
119 /* Write highlighted block to a file */
120
121 int ublksave(BW *bw)
122 {
123         if (markb && markk && markb->b == markk->b && (markk->byte - markb->byte) > 0 && (!square || piscol(markk) > piscol(markb))) {
124                 if (wmkpw(bw->parent, UC "Name of file to write (^C to abort): ", &filehist, dowrite, UC "Names", NULL, cmplt, NULL, NULL, locale_map)) {
125                         return 0;
126                 } else {
127                         return -1;
128                 }
129         } else {
130                 return usave(bw);
131         }
132 }
133
134 /* Shell escape */
135
136 int ushell(BW *bw)
137 {
138         nescape(bw->parent->t->t);
139         ttsusp();
140         nreturn(bw->parent->t->t);
141         return 0;
142 }
143
144 /* Copy a file */
145
146 static int
147 cp(unsigned char *from, int g, unsigned char *tmpfn, unsigned char *to)
148 {
149         int f, amnt;
150         struct stat sbuf;
151
152 #ifdef HAVEUTIME
153 #ifdef NeXT
154         time_t utbuf[2];
155 #else
156         struct utimbuf utbuf;
157 #endif
158 #endif
159
160         f = open((char *)from, O_RDONLY);
161         if (f < 0) {
162                 return -1;
163         }
164         if (fstat(f, &sbuf) < 0) {
165                 return -1;
166         }
167         if (fchmod(g, sbuf.st_mode & 0777)) {
168                 close(f);
169                 return (-1);
170         }
171         while ((amnt = read(f, stdbuf, stdsiz)) > 0) {
172                 if (amnt != joe_write(g, stdbuf, amnt)) {
173                         break;
174                 }
175         }
176         close(f);
177         if (amnt) {
178                 return -1;
179         }
180
181         if (tmpfn && rename(tmpfn, to)) {
182                 return (-1);
183         }
184         /*
185          * Do not return !0 from here on.
186          * Below are only operations that run when the copy
187          * process finished successfully.
188          */
189         close(g);
190
191 #ifdef HAVEUTIME
192 #ifdef NeXT
193         utbuf[0] = (time_t) sbuf.st_atime;
194         utbuf[1] = (time_t) sbuf.st_mtime;
195 #else
196         utbuf.actime = sbuf.st_atime;
197         utbuf.modtime = sbuf.st_mtime;
198 #endif
199         utime(to, &utbuf);
200 #endif
201
202         copy_security_context(from, to);
203
204         return 0;
205 }
206
207 /* Make backup file if it needs to be made
208  * Returns 0 if backup file was made or didn't need to be made
209  * Returns 1 for error
210  */
211
212 static int
213 backup(BW *bw)
214 {
215         unsigned char tmp[1024 + 12];
216         unsigned char name[1024];
217         unsigned char *simple_backup_suffix;
218         int fd;
219
220         if (bw->b->backup || nobackups || !(bw->b->name) || !(bw->b->name[0]))
221                 return (0);
222
223         /* Create backup file name */
224         simple_backup_suffix = (unsigned char *)getenv("SIMPLE_BACKUP_SUFFIX");
225
226         if (simple_backup_suffix == NULL) {
227                 simple_backup_suffix = US "~";
228         }
229         if (backpath) {
230                 joe_snprintf_3((char *)name, sizeof(name), "%s/%s%s", backpath, namepart(tmp, bw->b->name), simple_backup_suffix);
231         } else {
232                 joe_snprintf_2((char *)name, sizeof(name), "%s%s", bw->b->name, simple_backup_suffix);
233         }
234
235         /* Securely generate a backup file temporary file */
236         *tmp = '\0';
237         if (*name != '/') {
238                 /* relative pathname */
239                 if (!getcwd((char *)tmp, sizeof(tmp)) ||
240                     strlcat((char *)tmp, "/", sizeof(tmp)) >= sizeof(tmp))
241                         return (1);
242         }
243         if (strlcat((char *)tmp, (char *)name, sizeof(tmp)) >= sizeof(tmp))
244                 return (1);
245         *(dirprt_ptr(tmp)) = '\0';
246         if ((simple_backup_suffix = mktmp(tmp, &fd)) == NULL)
247                 return (1);
248
249         /* Attempt to delete backup file first */
250         unlink((char *)name);
251
252         /* Copy original file to backup file securely */
253         if (cp(bw->b->name, fd, simple_backup_suffix, name)) {
254                 close(fd);
255                 unlink((char *)simple_backup_suffix);
256                 return (1);
257         }
258
259         bw->b->backup = 1;
260         return (0);
261 }
262
263 /* Write file */
264
265 /* Continuation structure */
266
267 struct savereq {
268         int (*callback)(BW *, struct savereq *, int, int *);
269         unsigned char *name;
270         B *first;
271         int not_saved;  /* Set if a modified file was not saved */
272         int rename;     /* Set if we're renaming the file during save */
273 };
274
275 static struct savereq *
276 mksavereq(int (*callback)(BW *, struct savereq *, int, int *),
277     unsigned char *name, B *first, int dorename)
278 {
279         struct savereq *req = malloc(sizeof(struct savereq));
280
281         req->callback = callback;
282         req->name = name;
283         req->first = first;
284         req->not_saved = 0;
285         req->rename = dorename;
286         return req;
287 }
288
289 static void rmsavereq(struct savereq *req)
290 {
291         vsrm(req->name);
292         free(req);
293 }
294
295 static int saver(BW *bw, int c, struct savereq *req, int *notify)
296 {
297         int fl;
298         if ((c | 0x20) == 'n') {
299                 msgnw(bw->parent, UC "Couldn't make backup file... file not saved");
300                 if (req->callback) {
301                         return req->callback(bw, req, -1, notify);
302                 } else {
303                         if (notify) {
304                                 *notify = 1;
305                         }
306                         rmsavereq(req);
307                         return -1;
308                 }
309         }
310         if ((c | 0x20) != 'y') {
311                 if (mkqw(bw->parent, sc("Could not make backup file.  Save anyway (y,n,^C)? "), saver, NULL, req, notify)) {
312                         return 0;
313                 } else {
314                         rmsavereq(req);
315                         if (notify)
316                                 *notify = 1;
317                         return -1;
318                 }
319         }
320         if (bw->b->er == -1 && bw->o.msnew) {
321                 exemac(bw->o.msnew);
322                 bw->b->er = -3;
323         }
324         if (bw->b->er == 0 && bw->o.msold) {
325                 exemac(bw->o.msold);
326         }
327         if ((fl = bsave(bw->b->bof, req->name, bw->b->eof->byte, 1)) != 0) {
328                 msgnw(bw->parent, msgs[-fl]);
329                 if (req->callback) {
330                         return req->callback(bw, req, -1, notify);
331                 } else {
332                         rmsavereq(req);
333                         if (notify) {
334                                 *notify = 1;
335                         }
336                         return -1;
337                 }
338         } else {
339                 if (req->rename) {
340                         free(bw->b->name);
341                         bw->b->name = 0;
342                 }
343                 if (!bw->b->name)
344                         bw->b->name = joesep((unsigned char *)strdup((char *)req->name));
345                 if (!strcmp(bw->b->name, req->name)) {
346                         bw->b->changed = 0;
347                         saverr(bw->b->name);
348                 }
349                 {
350                         /* Last UNDOREC which wasn't modified will be changed
351                          * to modified. And because this block is
352                          * executed after each 'save', there can't be more
353                          * than one record which is not modified
354                          *              24 Apr 2001, Marx
355                          */
356                         UNDO *u = bw->b->undo;
357                         UNDOREC *rec, *rec_start;
358
359                         rec = rec_start = &u->recs;
360
361                         do {
362                                 rec = rec->link.prev;
363                         } while (rec != rec_start && rec->changed);
364                         if(rec->changed == 0)
365                                 rec->changed = 1;
366
367                 }
368                 genexmsg(bw, 1, req->name);
369                 if (req->callback) {
370                         return req->callback(bw, req, 0, notify);
371                 } else {
372                         rmsavereq(req);
373                         return 0;
374                 }
375         }
376 }
377
378 static int dosave(BW *bw, struct savereq *req, int *notify)
379 {
380         if (backup(bw)) {
381                 return saver(bw, 0, req, notify);
382         } else {
383                 return saver(bw, 'y', req, notify);
384         }
385 }
386
387 static int dosave2(BW *bw, int c, struct savereq *req, int *notify)
388 {
389         if ((c | 0x20) == 'y') {
390                 return dosave(bw, req, notify);
391         } else if ((c | 0x20) == 'n') {
392                 if (notify) {
393                         *notify = 1;
394                 }
395                 genexmsg(bw, 0, req->name);
396                 rmsavereq(req);
397                 return -1;
398         } else if (mkqw(bw->parent, sc("File exists.  Overwrite (y,n,^C)? "), dosave2, NULL, req, notify)) {
399                 return 0;
400         } else {
401                 /* Should be in abort function */
402                 rmsavereq(req);
403                 return -1;
404         }
405 }
406
407 static int dosave2a(BW *bw, int c, struct savereq *req, int *notify)
408 {
409         if ((c | 0x20) == 'y') {
410                 return dosave(bw, req, notify);
411         } else if ((c | 0x20) == 'n') {
412                 if (notify) {
413                         *notify = 1;
414                 }
415                 genexmsg(bw, 0, req->name);
416                 rmsavereq(req);
417                 return -1;
418         } else if (mkqw(bw->parent, sc("File on disk is newer.  Overwrite (y,n,^C)? "), dosave2a, NULL, req, notify)) {
419                 return 0;
420         } else {
421                 rmsavereq(req);
422                 return -1;
423         }
424 }
425
426 /* Checks if file exists. */
427
428 static int dosave1(BW *bw, unsigned char *s, struct savereq *req, int *notify)
429 {
430         int f;
431
432         if (req->name)
433                 vsrm(req->name);
434         req->name = s;
435
436         if (s[0] != '!' && !(s[0] == '>' && s[1] == '>')) {
437                 /* It's a normal file: not a pipe or append */
438                 if (!bw->b->name || strcmp(s, bw->b->name)) {
439                         /* Newly named file or name is different than buffer */
440                         f = open((char *)s, O_RDONLY);
441                         if (f != -1) {
442                                 close(f);
443                                 return dosave2(bw, 0, req, notify);
444                         }
445                 }
446                 else {
447                         /* We're saving a newer version of the same file */
448                         struct stat sbuf;
449                         if (!stat((char *)s,&sbuf)) {
450                                 if (sbuf.st_mtime>bw->b->mod_time) {
451                                         return dosave2a(bw, 0, req, notify);
452                                 }
453                         }
454                 }
455         }
456
457         return dosave(bw, req, notify);
458 }
459
460 /* User command: ^K D */
461
462 int usave(BW *bw)
463 {
464         BW *pbw;
465
466         pbw = wmkpw(bw->parent, UC "Name of file to save (^C to abort): ", &filehist, dosave1, UC "Names", NULL, cmplt, mksavereq(NULL,NULL,NULL,0), NULL, locale_map);
467
468         if (pbw && bw->b->name) {
469                 binss(pbw->cursor, bw->b->name);
470                 pset(pbw->cursor, pbw->b->eof);
471                 pbw->cursor->xcol = piscol(pbw->cursor);
472         }
473         if (pbw) {
474                 return 0;
475         } else {
476                 return -1;
477         }
478 }
479
480 /* Load file to edit */
481
482 static int
483 doedit1(BW *bw, int c, unsigned char *s, int *notify)
484 {
485         int ret = 0;
486         int er;
487         void *object;
488         W *w;
489         B *b;
490
491         switch (c | 0x20) {
492         case 'y':
493                 /* Reload from file */
494                 c = 1;
495                 break;
496         case 'n':
497                 /* Edit already loaded buffer */
498                 c = 0;
499                 break;
500         default:
501                 /* Ask what todo */
502                 /* FIXME: need abort handler to prevent leak */
503                 if (mkqw(bw->parent, sc("Load original file from disk (y,n,^C)? "), doedit1, NULL, s, notify))
504                         return (0);
505                 vsrm(s);
506                 return (-1);
507         }
508
509         if (notify)
510                 *notify = 1;
511
512         b = c ? bfind_reload(s) : bfind(s);
513         er = error;
514         c = c ? (bw->b->count >= 1) : (bw->b->count == 1);
515
516         if (c && (bw->b->changed || bw->b->name)) {
517                 if (orphan) {
518                         orphit(bw);
519                 } else {
520                         if (uduptw(bw)) {
521                                 brm(b);
522                                 vsrm(s);
523                                 return (-1);
524                         }
525                         bw = maint->curwin->object.bw;
526                 }
527         }
528         if (er) {
529                 msgnwt(bw->parent, msgs[-er]);
530                 if (er != -1)
531                         ret = -1;
532         }
533         object = bw->object;
534         w = bw->parent;
535         bwrm(bw);
536         w->object.bw = bw = bwmk(w, b, 0);
537         wredraw(bw->parent);
538         bw->object = object;
539         vsrm(s);
540         if (er == -1 && bw->o.mnew)
541                 exemac(bw->o.mnew);
542         if (er == 0 && bw->o.mold)
543                 exemac(bw->o.mold);
544         return (ret);
545 }
546
547 static int
548 doedit(BW *bw, unsigned char *s, void *obj, int *notify)
549 {
550         B *b;
551
552         b = bcheck_loaded(s);
553
554         if (b) {
555                 if (b->changed)
556                         /* Modified buffer exists, so ask */
557                         return doedit1(bw, 0, s, notify);
558                 else
559                         /* Buffer not modified- just use it as is */
560                         return doedit1(bw, 'n', s, notify);
561         } else
562                 /* File not in buffer: don't ask */
563                 return doedit1(bw, 'y', s, notify);
564 }
565
566 int okrepl(BW *bw)
567 {
568         if (bw->b->count == 1 && bw->b->changed) {
569                 msgnw(bw->parent, UC "Can't replace modified file");
570                 return -1;
571         } else {
572                 return 0;
573         }
574 }
575
576 int uedit(BW *bw)
577 {
578         if (wmkpw(bw->parent, UC "Name of file to edit (^C to abort): ", &filehist, doedit, UC "Names", NULL, cmplt, NULL, NULL, locale_map)) {
579                 return 0;
580         } else {
581                 return -1;
582         }
583 }
584
585 int doswitch(BW *bw, unsigned char *s, void *obj, int *notify)
586 {
587         /* Try buffer, then file */
588         return doedit1(bw, 'n', s, notify);
589 }
590
591 int uswitch(BW *bw)
592 {
593         if (wmkpw(bw->parent, UC "Name of buffer to edit (^C to abort): ", &filehist, doswitch, UC "Names", NULL, cmplt, NULL, NULL, locale_map)) {
594                 return 0;
595         } else {
596                 return -1;
597         }
598 }
599
600 static int
601 doscratch(BW *bw, unsigned char *s, void *obj, int *notify)
602 {
603         int ret = 0;
604         int er;
605         void *object;
606         W *w;
607         B *b;
608
609         if (notify) {
610                 *notify = 1;
611         }
612
613         b = bfind_scratch(s);
614         er = error;
615         vsrm(s);
616         if (bw->b->count == 1 && (bw->b->changed || bw->b->name)) {
617                 if (orphan) {
618                         orphit(bw);
619                 } else {
620                         if (uduptw(bw)) {
621                                 brm(b);
622                                 return -1;
623                         }
624                         bw = maint->curwin->object.bw;
625                 }
626         }
627         if (er && er != -1) {
628                 msgnwt(bw->parent, msgs[-er]);
629                 ret = -1;
630         }
631         object = bw->object;
632         w = bw->parent;
633         bwrm(bw);
634         w->object.bw = bw = bwmk(w, b, 0);
635         wredraw(bw->parent);
636         bw->object = object;
637         if (er == -1 && bw->o.mnew) {
638                 exemac(bw->o.mnew);
639         }
640         if (er == 0 && bw->o.mold) {
641                 exemac(bw->o.mold);
642         }
643         return ret;
644 }
645
646 int uscratch(BW *bw)
647 {
648         if (wmkpw(bw->parent, UC "Name of scratch buffer to edit (^C to abort): ", &filehist, doscratch, UC "Names", NULL, cmplt, NULL, NULL, locale_map)) {
649                 return 0;
650         } else {
651                 return -1;
652         }
653 }
654
655 /* Load file into buffer: can result in an orphaned buffer */
656
657 static int dorepl(BW *bw, unsigned char *s, void *obj, int *notify)
658 {
659         void *object = bw->object;
660         int ret = 0;
661         int er;
662         W *w = bw->parent;
663         B *b;
664
665         if (notify) {
666                 *notify = 1;
667         }
668         b = bfind(s);
669         er = error;
670         if (error) {
671                 msgnwt(bw->parent, msgs[-error]);
672                 if (error != -1) {
673                         ret = -1;
674                 }
675         }
676         if (bw->b->count == 1 && (bw->b->changed || bw->b->name)) {
677                 orphit(bw);
678         }
679         bwrm(bw);
680         w->object.bw = bw = bwmk(w, b, 0);
681         wredraw(bw->parent);
682         bw->object = object;
683         vsrm(s);
684         if (er == -1 && bw->o.mnew) {
685                 exemac(bw->o.mnew);
686         }
687         if (er == 0 && bw->o.mold) {
688                 exemac(bw->o.mold);
689         }
690         return ret;
691 }
692
693 /* Switch to next buffer in window */
694
695 int unbuf(BW *bw)
696 {
697         void *object = bw->object;
698         W *w = bw->parent;
699         B *b;
700         b = bnext();
701         if (b == bw->b) {
702                 b = bnext();
703         }
704         if (b == bw->b) {
705                 return 0;
706                 /* return -1; this helps with querysave (no error when only one buffer) */
707         }
708         if (!b->orphan) {
709                 ++b->count;
710         } else {
711                 b->orphan = 0;
712         }
713         if (bw->b->count == 1) {
714                 orphit(bw);
715         }
716         bwrm(bw);
717         w->object.bw = bw = bwmk(w, b, 0);
718         wredraw(bw->parent);
719         bw->object = object;
720         return 0;
721 }
722
723 int upbuf(BW *bw)
724 {
725         void *object = bw->object;
726         W *w = bw->parent;
727         B *b;
728         b = bprev();
729         if (b == bw->b) {
730                 b = bprev();
731         }
732         if (b == bw->b) {
733                 return 0;
734                 /* return -1; */
735         }
736         if (!b->orphan) {
737                 ++b->count;
738         } else {
739                 b->orphan = 0;
740         }
741         if (bw->b->count == 1) {
742                 orphit(bw);
743         }
744         bwrm(bw);
745         w->object.bw = bw = bwmk(w, b, 0);
746         wredraw(bw->parent);
747         bw->object = object;
748         return 0;
749 }
750
751 int uinsf(BW *bw)
752 {
753         if (wmkpw(bw->parent, UC "Name of file to insert (^C to abort): ", &filehist, doinsf, UC "Names", NULL, cmplt, NULL, NULL, locale_map)) {
754                 return 0;
755         } else {
756                 return -1;
757         }
758 }
759
760 /* Save and exit */
761
762 static int exdone(BW *bw, struct savereq *req,int flg,int *notify)
763 {
764         if (notify)
765                 *notify = 1;
766         rmsavereq(req);
767         if (flg) {
768                 return -1;
769         } else {
770                 bw->b->changed = 0;
771                 saverr(bw->b->name);
772                 return uabort1(bw, -1);
773         }
774 }
775
776 int uexsve(BW *bw)
777 {
778         if (!bw->b->changed || bw->b->scratch) {
779                 /* It didn't change or it's just a scratch buffer: don't save */
780                 uabort(bw, -1);
781                 return 0;
782         } else if (bw->b->name && !exask) {
783                 /* It changed, it's not a scratch buffer and it's named */
784                 return dosave1(bw, vsncpy(NULL, 0, sz(bw->b->name)), mksavereq(exdone,NULL,NULL,0), NULL);
785         } else {
786                 BW *pbw = wmkpw(bw->parent, UC "Name of file to save (^C to abort): ", &filehist, dosave1, UC "Names", NULL, cmplt, mksavereq(exdone,NULL,NULL,1), NULL, locale_map);
787
788                 if (pbw && bw->b->name) {
789                         binss(pbw->cursor, bw->b->name);
790                         pset(pbw->cursor, pbw->b->eof);
791                         pbw->cursor->xcol = piscol(pbw->cursor);
792                 }
793                 if (pbw) {
794                         return 0;
795                 } else {
796                         return -1;
797                 }
798         }
799 }
800
801 /* If buffer is modified, prompt for saving: if user types 'n', uabort(), otherwise just return. */
802 /* If buffer is not modified, just return. */
803
804 static int nask(BW *bw, int c, void *object, int *notify)
805 {
806         if ((c | 0x20) == 'y') {
807                 /* uexsve macro should be here... */
808                 if(notify)
809                         *notify = 1;
810                 return 0;
811         } else if ((c | 0x20) == 'n') {
812                 if(notify)
813                         *notify = -1;
814                 genexmsg(bw, 0, NULL);
815                 abortit(bw);
816                 return -1;
817         } else if (bw->b->count == 1 && bw->b->changed && !bw->b->scratch) {
818                 if (mkqw(bw->parent, sc("Save changes to this file (y,n,^C)? "), nask, NULL, object, notify)) {
819                         return 0;
820                 } else {
821                         return -1;
822                 }
823         } else {
824                 if (notify) {
825                         *notify = 1;
826                 }
827                 return 0;
828         }
829 }
830
831 int uask(BW *bw)
832 {
833         return nask(bw, 0, NULL, NULL);
834 }
835
836 /* Kill a buffer: any windows which have it get their buffer replaced with a
837  * a scratch buffer */
838
839 static int dolose(BW *bw, int c, void *object, int *notify)
840 {
841         W *w;
842         B *b, *new_b;
843         int cnt;
844
845         if (notify) {
846                 *notify = 1;
847         }
848         if ((c | 0x20) != 'y') {
849                 return -1;
850         }
851
852         b=bw->b;
853         cnt = b->count;
854         b->count = 1;
855         genexmsg(bw, 0, NULL);
856         b->count = cnt;
857
858         if ((w = maint->topwin) != NULL) {
859                 do {
860                         if ((w->watom->what & TYPETW) && w->object.bw->b == b) {
861                                 if ((new_b = borphan()) != NULL) {
862                                         BW *bw2 = w->object.bw;
863                                         void *object_ = bw2->object;
864                                         /* FIXME: Shouldn't we wabort() and wcreate here to kill
865                                            any prompt windows? */
866
867                                         bwrm(bw2);
868                                         w->object.bw = bw2 = bwmk(w, new_b, 0);
869                                         wredraw(w);
870                                         bw2->object = object_;
871                                 } else {
872                                         BW *bw2 = w->object.bw;
873                                         object = bw2->object;
874                                         bwrm(bw2);
875                                         w->object.bw = bw2 = bwmk(w, bfind(US ""), 0);
876                                         wredraw(w);
877                                         bw2->object = object;
878                                         if (bw2->o.mnew)
879                                                 exemac(bw2->o.mnew);
880                                 }
881                         }
882                 w = w->link.next;
883                 } while (w != maint->topwin);
884         }
885         return 0;
886 }
887
888 int ulose(BW *bw)
889 {
890         msgnw(bw->parent, NULL);
891         if (bw->b->count==1 && bw->b->pid) {
892                 return ukillpid(bw);
893         }
894         if (bw->b->changed && !bw->b->scratch) {
895                 if (mkqw(bw->parent, sc("Lose changes to this file (y,n,^C)? "), dolose, NULL, NULL, NULL)) {
896                         return 0;
897                 } else {
898                         return -1;
899                 }
900         } else {
901                 return dolose(bw, 'y', NULL, NULL);
902         }
903 }
904
905 /* Buffer list */
906
907 static int dobuf(MENU *m, int x, unsigned char **s)
908 {
909         unsigned char *name;
910         BW *bw = m->parent->win->object.bw;
911         int *notify = m->parent->notify;
912
913         m->parent->notify = 0;
914         name = vsdup(s[x]);
915         wabort(m->parent);
916         return dorepl(bw, name, NULL, notify);
917 }
918
919 static int abrtb(MENU *m, int x, unsigned char **s)
920 {
921         varm(s);
922         return -1;
923 }
924
925 int ubufed(BW *bw)
926 {
927         unsigned char **s = getbufs();
928
929         vasort(av(s));
930         if (mkmenu(bw->parent, s, dobuf, abrtb, NULL, 0, s, NULL))
931                 return 0;
932         else {
933                 varm(s);
934                 return -1;
935         }
936 }
937
938 /* Query save loop */
939
940 static int doquerysave(BW *bw,int c,struct savereq *req,int *notify)
941 {
942         W *w = bw->parent;
943
944         if ((c | 0x20) == 'y') {
945                 if (bw->b->name && bw->b->name[0])
946                         return dosave1(bw, vsncpy(NULL,0,sz(bw->b->name)), req, notify);
947                 else {
948                         BW *pbw;
949                         pbw = wmkpw(bw->parent, UC "Name of file to save (^C to abort): ", &filehist, dosave1, UC "Names", NULL, cmplt, req, notify, locale_map);
950
951                         if (pbw) {
952                                 return 0;
953                         } else {
954                                 free(req);
955                                 return -1;
956                         }
957                 }
958         } else if ((c | 0x20) == 'n') {
959                 /* Find next buffer to save */
960                 if (bw->b->changed)
961                         req->not_saved = 1;
962  next:
963                 if (unbuf(bw)) {
964                         if (notify)
965                                 *notify = 1;
966                         rmsavereq(req);
967                         return -1;
968                 }
969                 bw = w->object.bw;
970                 if (bw->b==req->first) {
971                         if (notify)
972                                 *notify = 1;
973                         rmsavereq(req);
974                         genexmsgmulti(bw,1,req->not_saved);
975                         return 0;
976                 }
977                 if (!bw->b->changed || bw->b->scratch)
978                         goto next;
979
980                 return doquerysave(bw,0,req,notify);
981         } else {
982                 unsigned char buf[1024];
983                 joe_snprintf_1(buf,1024,"File %s has been modified.  Save it (y,n,^C)? ",bw->b->name ? bw->b->name : US "(Unnamed)" );
984                 if (mkqw(bw->parent, sz(buf), doquerysave, NULL, req, notify)) {
985                         return 0;
986                 } else {
987                         /* Should be in abort function */
988                         rmsavereq(req);
989                         return -1;
990                 }
991         }
992 }
993
994 static int query_next(BW *bw, struct savereq *req,int flg,int *notify)
995 {
996         if (flg) {
997                 if (notify)
998                         *notify = 1;
999                 rmsavereq(req);
1000                 return -1;
1001         } else
1002                 return doquerysave(bw,'N',req,notify);
1003 }
1004
1005 int uquerysave(BW *bw)
1006 {
1007         W *w = bw->parent;
1008         B *first = bw->b;
1009
1010         /* Find a modified buffer */
1011         do {
1012                 if (bw->b->changed && !bw->b->scratch)
1013                         return doquerysave(bw,0,mksavereq(query_next,NULL,first,0),NULL);
1014                 else if (unbuf(bw))
1015                         return -1;
1016                 bw = w->object.bw;
1017         } while(bw->b!=first);
1018
1019         genexmsgmulti(bw,0,0);
1020
1021         return 0;
1022 }
1023
1024 int ukilljoe(BW *bw)
1025 {
1026         leave = 1;
1027         return 0;
1028 }
1029
1030 extern int main_rv;
1031
1032 int
1033 uabendjoe(BW *bw)
1034 {
1035         main_rv = 1;
1036         return (ukilljoe(bw));
1037 }