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