4 * (C) 1992 Joseph H. Allen
6 * This file is part of JOE (Joe's Own Editor)
12 __RCSID("$MirOS: contrib/code/jupp/ufile.c,v 1.25 2017/12/08 03:24:16 tg Exp $");
24 #include <sys/utime.h>
52 unsigned char *backpath = NULL; /* Place to store backup files */
53 static B *filehist = NULL; /* History of file names */
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)
63 if (bw->b->name && bw->b->name[0]) {
71 joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "File %s saved", name);
73 joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "File %s not saved", name);
75 } else if (bw->b->changed && bw->b->count == 1) {
76 joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "File %s not saved", s);
78 joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "File %s saved", s);
80 joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "File %s not changed so no update needed", s);
82 msgnw(bw->parent, msgbuf);
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."));
92 exmsg = vsncpy(NULL, 0, sc("File "));
93 exmsg = vsncpy(sv(exmsg), sz(s));
94 exmsg = vsncpy(sv(exmsg), sc(" saved."));
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."));
104 genexmsgmulti(BW *bw, int saved, int skipped)
108 joe_snprintf_0((char *)msgbuf, JOE_MSGBUFSIZE, "Some files have not been saved.");
110 joe_snprintf_0((char *)msgbuf, JOE_MSGBUFSIZE, "All modified files have been saved.");
112 joe_snprintf_0((char *)msgbuf, JOE_MSGBUFSIZE, "No modified files, so no updates needed.");
114 msgnw(bw->parent, msgbuf);
116 exmsg = vsncpy(NULL,0,sz(msgbuf));
119 /* Write highlighted block to a file */
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)) {
138 nescape(bw->parent->t->t);
140 nreturn(bw->parent->t->t);
147 cp(unsigned char *from, int g, unsigned char *tmpfn, unsigned char *to)
156 struct utimbuf utbuf;
160 f = open((char *)from, O_RDONLY);
164 if (fstat(f, &sbuf) < 0) {
167 if (fchmod(g, sbuf.st_mode & 0777)) {
171 while ((amnt = read(f, stdbuf, stdsiz)) > 0) {
172 if (amnt != joe_write(g, stdbuf, amnt)) {
181 if (tmpfn && rename(tmpfn, to)) {
185 * Do not return !0 from here on.
186 * Below are only operations that run when the copy
187 * process finished successfully.
193 utbuf[0] = (time_t) sbuf.st_atime;
194 utbuf[1] = (time_t) sbuf.st_mtime;
196 utbuf.actime = sbuf.st_atime;
197 utbuf.modtime = sbuf.st_mtime;
202 copy_security_context(from, to);
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
215 unsigned char tmp[1024 + 12];
216 unsigned char name[1024];
217 unsigned char *simple_backup_suffix;
220 if (bw->b->backup || nobackups || !(bw->b->name) || !(bw->b->name[0]))
223 /* Create backup file name */
224 simple_backup_suffix = (unsigned char *)getenv("SIMPLE_BACKUP_SUFFIX");
226 if (simple_backup_suffix == NULL) {
227 simple_backup_suffix = US "~";
230 joe_snprintf_3((char *)name, sizeof(name), "%s/%s%s", backpath, namepart(tmp, bw->b->name), simple_backup_suffix);
232 joe_snprintf_2((char *)name, sizeof(name), "%s%s", bw->b->name, simple_backup_suffix);
235 /* Securely generate a backup file temporary file */
238 /* relative pathname */
239 if (!getcwd((char *)tmp, sizeof(tmp)) ||
240 strlcat((char *)tmp, "/", sizeof(tmp)) >= sizeof(tmp))
243 if (strlcat((char *)tmp, (char *)name, sizeof(tmp)) >= sizeof(tmp))
245 *(dirprt_ptr(tmp)) = '\0';
246 if ((simple_backup_suffix = mktmp(tmp, &fd)) == NULL)
249 /* Attempt to delete backup file first */
250 unlink((char *)name);
252 /* Copy original file to backup file securely */
253 if (cp(bw->b->name, fd, simple_backup_suffix, name)) {
255 unlink((char *)simple_backup_suffix);
265 /* Continuation structure */
268 int (*callback)(BW *, struct savereq *, int, int *);
271 int not_saved; /* Set if a modified file was not saved */
272 int rename; /* Set if we're renaming the file during save */
275 static struct savereq *
276 mksavereq(int (*callback)(BW *, struct savereq *, int, int *),
277 unsigned char *name, B *first, int dorename)
279 struct savereq *req = malloc(sizeof(struct savereq));
281 req->callback = callback;
285 req->rename = dorename;
289 static void rmsavereq(struct savereq *req)
295 static int saver(BW *bw, int c, struct savereq *req, int *notify)
298 if ((c | 0x20) == 'n') {
299 msgnw(bw->parent, UC "Couldn't make backup file... file not saved");
301 return req->callback(bw, req, -1, notify);
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)) {
320 if (bw->b->er == -1 && bw->o.msnew) {
324 if (bw->b->er == 0 && bw->o.msold) {
327 if ((fl = bsave(bw->b->bof, req->name, bw->b->eof->byte, 1)) != 0) {
328 msgnw(bw->parent, msgs[-fl]);
330 return req->callback(bw, req, -1, notify);
344 bw->b->name = joesep((unsigned char *)strdup((char *)req->name));
345 if (!strcmp(bw->b->name, req->name)) {
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
356 UNDO *u = bw->b->undo;
357 UNDOREC *rec, *rec_start;
359 rec = rec_start = &u->recs;
362 rec = rec->link.prev;
363 } while (rec != rec_start && rec->changed);
364 if(rec->changed == 0)
368 genexmsg(bw, 1, req->name);
370 return req->callback(bw, req, 0, notify);
378 static int dosave(BW *bw, struct savereq *req, int *notify)
381 return saver(bw, 0, req, notify);
383 return saver(bw, 'y', req, notify);
387 static int dosave2(BW *bw, int c, struct savereq *req, int *notify)
389 if ((c | 0x20) == 'y') {
390 return dosave(bw, req, notify);
391 } else if ((c | 0x20) == 'n') {
395 genexmsg(bw, 0, req->name);
398 } else if (mkqw(bw->parent, sc("File exists. Overwrite (y,n,^C)? "), dosave2, NULL, req, notify)) {
401 /* Should be in abort function */
407 static int dosave2a(BW *bw, int c, struct savereq *req, int *notify)
409 if ((c | 0x20) == 'y') {
410 return dosave(bw, req, notify);
411 } else if ((c | 0x20) == 'n') {
415 genexmsg(bw, 0, req->name);
418 } else if (mkqw(bw->parent, sc("File on disk is newer. Overwrite (y,n,^C)? "), dosave2a, NULL, req, notify)) {
426 /* Checks if file exists. */
428 static int dosave1(BW *bw, unsigned char *s, struct savereq *req, int *notify)
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);
443 return dosave2(bw, 0, req, notify);
447 /* We're saving a newer version of the same file */
449 if (!stat((char *)s,&sbuf)) {
450 if (sbuf.st_mtime>bw->b->mod_time) {
451 return dosave2a(bw, 0, req, notify);
457 return dosave(bw, req, notify);
460 /* User command: ^K D */
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);
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);
480 /* Load file to edit */
483 doedit1(BW *bw, int c, unsigned char *s, int *notify)
493 /* Reload from file */
497 /* Edit already loaded buffer */
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))
512 b = c ? bfind_reload(s) : bfind(s);
514 c = c ? (bw->b->count >= 1) : (bw->b->count == 1);
516 if (c && (bw->b->changed || bw->b->name)) {
525 bw = maint->curwin->object.bw;
529 msgnwt(bw->parent, msgs[-er]);
536 w->object.bw = bw = bwmk(w, b, 0);
540 if (er == -1 && bw->o.mnew)
542 if (er == 0 && bw->o.mold)
548 doedit(BW *bw, unsigned char *s, void *obj, int *notify)
552 b = bcheck_loaded(s);
556 /* Modified buffer exists, so ask */
557 return doedit1(bw, 0, s, notify);
559 /* Buffer not modified- just use it as is */
560 return doedit1(bw, 'n', s, notify);
562 /* File not in buffer: don't ask */
563 return doedit1(bw, 'y', s, notify);
568 if (bw->b->count == 1 && bw->b->changed) {
569 msgnw(bw->parent, UC "Can't replace modified file");
578 if (wmkpw(bw->parent, UC "Name of file to edit (^C to abort): ", &filehist, doedit, UC "Names", NULL, cmplt, NULL, NULL, locale_map)) {
585 int doswitch(BW *bw, unsigned char *s, void *obj, int *notify)
587 /* Try buffer, then file */
588 return doedit1(bw, 'n', s, notify);
593 if (wmkpw(bw->parent, UC "Name of buffer to edit (^C to abort): ", &filehist, doswitch, UC "Names", NULL, cmplt, NULL, NULL, locale_map)) {
601 doscratch(BW *bw, unsigned char *s, void *obj, int *notify)
613 b = bfind_scratch(s);
616 if (bw->b->count == 1 && (bw->b->changed || bw->b->name)) {
624 bw = maint->curwin->object.bw;
627 if (er && er != -1) {
628 msgnwt(bw->parent, msgs[-er]);
634 w->object.bw = bw = bwmk(w, b, 0);
637 if (er == -1 && bw->o.mnew) {
640 if (er == 0 && bw->o.mold) {
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)) {
655 /* Load file into buffer: can result in an orphaned buffer */
657 static int dorepl(BW *bw, unsigned char *s, void *obj, int *notify)
659 void *object = bw->object;
671 msgnwt(bw->parent, msgs[-error]);
676 if (bw->b->count == 1 && (bw->b->changed || bw->b->name)) {
680 w->object.bw = bw = bwmk(w, b, 0);
684 if (er == -1 && bw->o.mnew) {
687 if (er == 0 && bw->o.mold) {
693 /* Switch to next buffer in window */
697 void *object = bw->object;
706 /* return -1; this helps with querysave (no error when only one buffer) */
713 if (bw->b->count == 1) {
717 w->object.bw = bw = bwmk(w, b, 0);
725 void *object = bw->object;
741 if (bw->b->count == 1) {
745 w->object.bw = bw = bwmk(w, b, 0);
753 if (wmkpw(bw->parent, UC "Name of file to insert (^C to abort): ", &filehist, doinsf, UC "Names", NULL, cmplt, NULL, NULL, locale_map)) {
762 static int exdone(BW *bw, struct savereq *req,int flg,int *notify)
772 return uabort1(bw, -1);
778 if (!bw->b->changed || bw->b->scratch) {
779 /* It didn't change or it's just a scratch buffer: don't save */
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);
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);
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);
801 /* If buffer is modified, prompt for saving: if user types 'n', uabort(), otherwise just return. */
802 /* If buffer is not modified, just return. */
804 static int nask(BW *bw, int c, void *object, int *notify)
806 if ((c | 0x20) == 'y') {
807 /* uexsve macro should be here... */
811 } else if ((c | 0x20) == 'n') {
814 genexmsg(bw, 0, NULL);
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)) {
833 return nask(bw, 0, NULL, NULL);
836 /* Kill a buffer: any windows which have it get their buffer replaced with a
837 * a scratch buffer */
839 static int dolose(BW *bw, int c, void *object, int *notify)
848 if ((c | 0x20) != 'y') {
855 genexmsg(bw, 0, NULL);
858 if ((w = maint->topwin) != NULL) {
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? */
868 w->object.bw = bw2 = bwmk(w, new_b, 0);
870 bw2->object = object_;
872 BW *bw2 = w->object.bw;
873 object = bw2->object;
875 w->object.bw = bw2 = bwmk(w, bfind(US ""), 0);
877 bw2->object = object;
883 } while (w != maint->topwin);
890 msgnw(bw->parent, NULL);
891 if (bw->b->count==1 && bw->b->pid) {
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)) {
901 return dolose(bw, 'y', NULL, NULL);
907 static int dobuf(MENU *m, int x, unsigned char **s)
910 BW *bw = m->parent->win->object.bw;
911 int *notify = m->parent->notify;
913 m->parent->notify = 0;
916 return dorepl(bw, name, NULL, notify);
919 static int abrtb(MENU *m, int x, unsigned char **s)
927 unsigned char **s = getbufs();
930 if (mkmenu(bw->parent, s, dobuf, abrtb, NULL, 0, s, NULL))
938 /* Query save loop */
940 static int doquerysave(BW *bw,int c,struct savereq *req,int *notify)
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);
949 pbw = wmkpw(bw->parent, UC "Name of file to save (^C to abort): ", &filehist, dosave1, UC "Names", NULL, cmplt, req, notify, locale_map);
958 } else if ((c | 0x20) == 'n') {
959 /* Find next buffer to save */
970 if (bw->b==req->first) {
974 genexmsgmulti(bw,1,req->not_saved);
977 if (!bw->b->changed || bw->b->scratch)
980 return doquerysave(bw,0,req,notify);
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)) {
987 /* Should be in abort function */
994 static int query_next(BW *bw, struct savereq *req,int flg,int *notify)
1002 return doquerysave(bw,'N',req,notify);
1005 int uquerysave(BW *bw)
1010 /* Find a modified buffer */
1012 if (bw->b->changed && !bw->b->scratch)
1013 return doquerysave(bw,0,mksavereq(query_next,NULL,first,0),NULL);
1017 } while(bw->b!=first);
1019 genexmsgmulti(bw,0,0);
1024 int ukilljoe(BW *bw)
1036 return (ukilljoe(bw));