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