1 /* $MirOS: contrib/code/jupp/ufile.c,v 1.11 2014/10/23 16:10:28 tg Exp $ */
5 * (C) 1992 Joseph H. Allen
7 * This file is part of JOE (Joe's Own Editor)
14 #ifdef HAVE_SYS_TYPES_H
15 #include <sys/types.h>
17 #ifdef HAVE_SYS_STAT_H
32 #include <sys/utime.h>
38 int copy_security_context(const char *from_file, const char *to_file);
65 unsigned char *backpath = NULL; /* Place to store backup files */
66 static B *filehist = NULL; /* History of file names */
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)
76 if (bw->b->name && bw->b->name[0]) {
84 joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "File %s saved", name);
86 joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "File %s not saved", name);
88 } else if (bw->b->changed && bw->b->count == 1) {
89 joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "File %s not saved", s);
91 joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "File %s saved", s);
93 joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "File %s not changed so no update needed", s);
95 msgnw(bw->parent, msgbuf);
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."));
105 exmsg = vsncpy(NULL, 0, sc("File "));
106 exmsg = vsncpy(sv(exmsg), sz(s));
107 exmsg = vsncpy(sv(exmsg), sc(" saved."));
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."));
116 void genexmsgmulti(BW *bw, int saved, int skipped)
120 joe_snprintf_0((char *)msgbuf, JOE_MSGBUFSIZE, "Some files have not been saved.");
122 joe_snprintf_0((char *)msgbuf, JOE_MSGBUFSIZE, "All modified files have been saved.");
124 joe_snprintf_0((char *)msgbuf, JOE_MSGBUFSIZE, "No modified files, so no updates needed.");
126 msgnw(bw->parent, msgbuf);
128 exmsg = vsncpy(NULL,0,sz(msgbuf));
131 /* Write highlighted block to a file */
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)) {
150 nescape(bw->parent->t->t);
152 nreturn(bw->parent->t->t);
159 cp(unsigned char *from, int g, unsigned char *tmpfn, unsigned char *to)
168 struct utimbuf utbuf;
172 f = open((char *)from, O_RDONLY);
176 if (fstat(f, &sbuf) < 0) {
179 if (fchmod(g, sbuf.st_mode & 0777)) {
183 while ((amnt = read(f, stdbuf, stdsiz)) > 0) {
184 if (amnt != joe_write(g, stdbuf, amnt)) {
193 if (tmpfn && rename(tmpfn, to)) {
197 * Do not return !0 from here on.
198 * Below are only operations that run when the copy
199 * process finished successfully.
205 utbuf[0] = (time_t) sbuf.st_atime;
206 utbuf[1] = (time_t) sbuf.st_mtime;
208 utbuf.actime = sbuf.st_atime;
209 utbuf.modtime = sbuf.st_mtime;
215 copy_security_context(from, to);
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
229 unsigned char tmp[1024 + 12];
230 unsigned char name[1024];
231 unsigned char *simple_backup_suffix;
234 if (bw->b->backup || nobackups || !(bw->b->name) || !(bw->b->name[0]))
237 /* Create backup file name */
238 simple_backup_suffix = (unsigned char *)getenv("SIMPLE_BACKUP_SUFFIX");
240 if (simple_backup_suffix == NULL) {
241 simple_backup_suffix = US "~";
244 joe_snprintf_3((char *)name, sizeof(name), "%s/%s%s", backpath, namepart(tmp, bw->b->name), simple_backup_suffix);
246 joe_snprintf_2((char *)name, sizeof(name), "%s%s", bw->b->name, simple_backup_suffix);
249 /* Securely generate a backup file temporary file */
252 /* relative pathname */
253 if (!getcwd((char *)tmp, sizeof(tmp)) ||
254 strlcat((char *)tmp, "/", sizeof(tmp)) >= sizeof(tmp))
257 if (strlcat((char *)tmp, (char *)name, sizeof(tmp)) >= sizeof(tmp))
259 *(dirprt_ptr(tmp)) = '\0';
260 if ((simple_backup_suffix = mktmp(tmp, &fd)) == NULL)
263 /* Attempt to delete backup file first */
264 unlink((char *)name);
266 /* Copy original file to backup file securely */
267 if (cp(bw->b->name, fd, simple_backup_suffix, name)) {
269 unlink((char *)simple_backup_suffix);
279 /* Continuation structure */
285 int not_saved; /* Set if a modified file was not saved */
286 int rename; /* Set if we're renaming the file during save */
289 struct savereq *mksavereq(int (*callback)(), unsigned char *name, B *first,int rename_)
291 struct savereq *req = (struct savereq *) joe_malloc(sizeof(struct savereq));
292 req->callback = callback;
296 req->rename = rename_;
300 static void rmsavereq(struct savereq *req)
306 static int saver(BW *bw, int c, struct savereq *req, int *notify)
309 if (c == 'n' || c == 'N') {
310 msgnw(bw->parent, US "Couldn't make backup file... file not saved");
312 return req->callback(bw, req, -1, notify);
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)) {
331 if (bw->b->er == -1 && bw->o.msnew) {
335 if (bw->b->er == 0 && bw->o.msold) {
338 if ((fl = bsave(bw->b->bof, req->name, bw->b->eof->byte, 1)) != 0) {
339 msgnw(bw->parent, msgs[-fl]);
341 return req->callback(bw, req, -1, notify);
351 joe_free(bw->b->name);
355 bw->b->name = joesep((unsigned char *)strdup((char *)req->name));
356 if (!strcmp(bw->b->name, req->name)) {
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
367 UNDO *u = bw->b->undo;
368 UNDOREC *rec, *rec_start;
370 rec = rec_start = &u->recs;
373 rec = rec->link.prev;
374 } while (rec != rec_start && rec->changed);
375 if(rec->changed == 0)
379 genexmsg(bw, 1, req->name);
381 return req->callback(bw, req, 0, notify);
389 static int dosave(BW *bw, struct savereq *req, int *notify)
392 return saver(bw, 0, req, notify);
394 return saver(bw, 'y', req, notify);
398 static int dosave2(BW *bw, int c, struct savereq *req, int *notify)
400 if (c == 'y' || c == 'Y') {
401 return dosave(bw, req, notify);
402 } else if (c == 'n' || c == 'N') {
406 genexmsg(bw, 0, req->name);
409 } else if (mkqw(bw->parent, sc("File exists. Overwrite (y,n,^C)? "), dosave2, NULL, req, notify)) {
412 /* Should be in abort function */
418 static int dosave2a(BW *bw, int c, struct savereq *req, int *notify)
420 if (c == 'y' || c == 'Y') {
421 return dosave(bw, req, notify);
422 } else if (c == 'n' || c == 'N') {
426 genexmsg(bw, 0, req->name);
429 } else if (mkqw(bw->parent, sc("File on disk is newer. Overwrite (y,n,^C)? "), dosave2a, NULL, req, notify)) {
437 /* Checks if file exists. */
439 static int dosave1(BW *bw, unsigned char *s, struct savereq *req, int *notify)
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);
454 return dosave2(bw, 0, req, notify);
458 /* We're saving a newer version of the same file */
460 if (!stat((char *)s,&sbuf)) {
461 if (sbuf.st_mtime>bw->b->mod_time) {
462 return dosave2a(bw, 0, req, notify);
468 return dosave(bw, req, notify);
471 /* User command: ^K D */
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);
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);
491 /* Load file to edit */
493 int doedit1(BW *bw,int c,unsigned char *s,int *notify)
500 if (c=='y' || c=='Y') {
501 /* Reload from file */
509 if (bw->b->count >= 1 && (bw->b->changed || bw->b->name)) {
517 bw = (BW *) maint->curwin->object;
521 msgnwt(bw->parent, msgs[-er]);
529 w->object = (void *) (bw = bwmk(w, b, 0));
533 if (er == -1 && bw->o.mnew) {
536 if (er == 0 && bw->o.mold) {
540 } else if(c=='n' || c=='N') {
541 /* Edit already loaded buffer */
549 if (bw->b->count == 1 && (bw->b->changed || bw->b->name)) {
557 bw = (BW *) maint->curwin->object;
561 msgnwt(bw->parent, msgs[-er]);
569 w->object = (void *) (bw = bwmk(w, b, 0));
573 if (er == -1 && bw->o.mnew) {
576 if (er == 0 && bw->o.mold) {
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))
591 int doedit(BW *bw, unsigned char *s, void *obj, int *notify)
595 b = bcheck_loaded(s);
599 /* Modified buffer exists, so ask */
600 return doedit1(bw, 0, s, notify);
602 /* Buffer not modified- just use it as is */
603 return doedit1(bw, 'n', s, notify);
605 /* File not in buffer: don't ask */
606 return doedit1(bw, 'y', s, notify);
611 if (bw->b->count == 1 && bw->b->changed) {
612 msgnw(bw->parent, US "Can't replace modified file");
621 if (wmkpw(bw->parent, US "Name of file to edit (^C to abort): ", &filehist, doedit, US "Names", NULL, cmplt, NULL, NULL, locale_map)) {
628 int doswitch(BW *bw, unsigned char *s, void *obj, int *notify)
630 /* Try buffer, then file */
631 return doedit1(bw, 'n', s, notify);
636 if (wmkpw(bw->parent, US "Name of buffer to edit (^C to abort): ", &filehist, doswitch, US "Names", NULL, cmplt, NULL, NULL, locale_map)) {
643 int doscratch(BW *bw, unsigned char *s, void *obj, int *notify)
655 b = bfind_scratch(s);
657 if (bw->b->count == 1 && (bw->b->changed || bw->b->name)) {
665 bw = (BW *) maint->curwin->object;
668 if (er && er != -1) {
669 msgnwt(bw->parent, msgs[-er]);
675 w->object = (void *) (bw = bwmk(w, b, 0));
679 if (er == -1 && bw->o.mnew) {
682 if (er == 0 && bw->o.mold) {
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)) {
697 /* Load file into buffer: can result in an orphaned buffer */
699 static int dorepl(BW *bw, unsigned char *s, void *obj, int *notify)
701 void *object = bw->object;
713 msgnwt(bw->parent, msgs[-error]);
718 if (bw->b->count == 1 && (bw->b->changed || bw->b->name)) {
722 w->object = (void *) (bw = bwmk(w, b, 0));
726 if (er == -1 && bw->o.mnew) {
729 if (er == 0 && bw->o.mold) {
735 /* Switch to next buffer in window */
739 void *object = bw->object;
748 /* return -1; this helps with querysave (no error when only one buffer) */
755 if (bw->b->count == 1) {
759 w->object = (void *) (bw = bwmk(w, b, 0));
767 void *object = bw->object;
783 if (bw->b->count == 1) {
787 w->object = (void *) (bw = bwmk(w, b, 0));
795 if (wmkpw(bw->parent, US "Name of file to insert (^C to abort): ", &filehist, doinsf, US "Names", NULL, cmplt, NULL, NULL, locale_map)) {
804 static int exdone(BW *bw, struct savereq *req,int flg,int *notify)
814 return uabort1(bw, -1);
820 if (!bw->b->changed || bw->b->scratch) {
821 /* It didn't change or it's just a scratch buffer: don't save */
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);
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);
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);
843 /* If buffer is modified, prompt for saving: if user types 'n', uabort(), otherwise just return. */
844 /* If buffer is not modified, just return. */
846 static int nask(BW *bw, int c, void *object, int *notify)
848 if (c == 'y' || c == 'Y') {
849 /* uexsve macro should be here... */
853 } else if (c == 'n' || c == 'N') {
856 genexmsg(bw, 0, NULL);
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)) {
875 return nask(bw, 0, NULL, NULL);
878 /* Kill a buffer: any windows which have it get their buffer replaced with a
879 * a scratch buffer */
881 static int dolose(BW *bw, int c, void *object, int *notify)
890 if (c != 'y' && c != 'Y') {
897 genexmsg(bw, 0, NULL);
900 if ((w = maint->topwin) != NULL) {
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? */
910 w->object = (void *) (bw_ = bwmk(w, new_b, 0));
912 bw_->object = object_;
914 BW *bw_ = (BW *)w->object;
915 object = bw_->object;
917 w->object = (void *) (bw_ = bwmk(w, bfind(US ""), 0));
919 bw_->object = object;
925 } while (w != maint->topwin);
932 msgnw(bw->parent, NULL);
933 if (bw->b->count==1 && bw->b->pid) {
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)) {
943 return dolose(bw, 'y', NULL, NULL);
949 static int dobuf(MENU *m, int x, unsigned char **s)
952 BW *bw = m->parent->win->object;
953 int *notify = m->parent->notify;
955 m->parent->notify = 0;
958 return dorepl(bw, name, NULL, notify);
961 static int abrtb(MENU *m, int x, unsigned char **s)
969 unsigned char **s = getbufs();
972 if (mkmenu(bw->parent, s, dobuf, abrtb, NULL, 0, s, NULL))
980 /* Query save loop */
982 static int doquerysave(BW *bw,int c,struct savereq *req,int *notify)
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);
990 pbw = wmkpw(bw->parent, US "Name of file to save (^C to abort): ", &filehist, dosave1, US "Names", NULL, cmplt, req, notify, locale_map);
999 } else if (c == 'n' || c == 'N') {
1000 /* Find next buffer to save */
1011 if (bw->b==req->first) {
1015 genexmsgmulti(bw,1,req->not_saved);
1018 if (!bw->b->changed || bw->b->scratch)
1021 return doquerysave(bw,0,req,notify);
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)) {
1028 /* Should be in abort function */
1035 static int query_next(BW *bw, struct savereq *req,int flg,int *notify)
1043 return doquerysave(bw,'N',req,notify);
1046 int uquerysave(BW *bw)
1051 /* Find a modified buffer */
1053 if (bw->b->changed && !bw->b->scratch)
1054 return doquerysave(bw,0,mksavereq(query_next,NULL,first,0),NULL);
1058 } while(bw->b!=first);
1060 genexmsgmulti(bw,0,0);
1065 int ukilljoe(BW *bw)