/* * User file operations * Copyright * (C) 1992 Joseph H. Allen * * This file is part of JOE (Joe's Own Editor) */ #include "config.h" #include "types.h" #include #include #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef UTIME #include #define HAVEUTIME 1 #else #ifdef SYSUTIME #include #define HAVEUTIME 1 #endif #endif #include "b.h" #include "bw.h" #include "macro.h" #include "main.h" #include "menu.h" #include "path.h" #include "pw.h" #include "qw.h" #include "scrn.h" #include "tab.h" #include "tty.h" #include "tw.h" #include "ublock.h" #include "uerror.h" #include "ufile.h" #include "ushell.h" #include "utils.h" #include "va.h" #include "vs.h" #include "w.h" extern int orphan; char *backpath = NULL; /* Place to store backup files */ static B *filehist = NULL; /* History of file names */ int nobackups = 0; int exask = 0; /* Ending message generator */ /**** message which is shown after closing joe (CTRL+x; CTRL+k) *****/ void genexmsg(BW *bw, int saved, char *name) { char *s; if (bw->b->name && bw->b->name[0]) { s = bw->b->name; } else { s = "(Unnamed)"; } if (name) { if (saved) { snprintf(msgbuf, JOE_MSGBUFSIZE, "File %s saved", name); } else { snprintf(msgbuf, JOE_MSGBUFSIZE, "File %s not saved", name); } } else if (bw->b->changed && bw->b->count == 1) { snprintf(msgbuf, JOE_MSGBUFSIZE, "File %s not saved", s); } else if (saved) { snprintf(msgbuf, JOE_MSGBUFSIZE, "File %s saved", s); } else { snprintf(msgbuf, JOE_MSGBUFSIZE, "File %s not changed so no update needed", s); } msgnw(bw->parent, msgbuf); if (!exmsg) { if (bw->b->changed && bw->b->count == 1) { exmsg = vsncpy(NULL, 0, sc("File ")); exmsg = vsncpy(sv(exmsg), sz(s)); exmsg = vsncpy(sv(exmsg), sc(" not saved.")); } else if (saved) { exmsg = vsncpy(NULL, 0, sc("File ")); exmsg = vsncpy(sv(exmsg), sz(s)); exmsg = vsncpy(sv(exmsg), sc(" saved.")); } else { exmsg = vsncpy(NULL, 0, sc("File ")); exmsg = vsncpy(sv(exmsg), sz(s)); exmsg = vsncpy(sv(exmsg), sc(" not changed so no update needed.")); } } } /* Write highlighted block to a file */ int ublksave(BW *bw) { if (markb && markk && markb->b == markk->b && (markk->byte - markb->byte) > 0 && (!square || piscol(markk) > piscol(markb))) { if (wmkpw(bw->parent, "Name of file to write (^C to abort): ", &filehist, dowrite, "Names", NULL, cmplt, NULL, NULL)) { return 0; } else { return -1; } } else { return usave(bw); } } /* Shell escape */ int ushell(BW *bw) { nescape(bw->parent->t->t); ttsusp(); nreturn(bw->parent->t->t); return 0; } /* Copy a file */ static int cp(char *from, char *to) { int f, g, amnt; struct stat sbuf; #ifdef HAVEUTIME #ifdef NeXT time_t utbuf[2]; #else struct utimbuf utbuf; #endif #endif f = open(from, O_RDONLY); if (f < 0) { return -1; } if (fstat(f, &sbuf) < 0) { return -1; } g = creat(to, sbuf.st_mode & ~(S_ISUID | S_ISGID)); if (g < 0) { close(f); return -1; } while ((amnt = read(f, stdbuf, stdsiz)) > 0) { if (amnt != joe_write(g, stdbuf, amnt)) { break; } } close(f); close(g); if (amnt) { return -1; } #ifdef HAVEUTIME #ifdef NeXT utbuf[0] = (time_t) sbuf.st_atime; utbuf[1] = (time_t) sbuf.st_mtime; #else utbuf.actime = sbuf.st_atime; utbuf.modtime = sbuf.st_mtime; #endif utime(to, &utbuf); #endif return 0; } /* Make backup file if it needs to be made * Returns 0 if backup file was made or didn't need to be made * Returns 1 for error */ static int backup(BW *bw) { if (!bw->b->backup && !nobackups && bw->b->name && bw->b->name[0]) { char tmp[1024]; char name[1024]; #ifdef __MSDOS__ int x; if (backpath) { snprintf(name, sizeof(name), "%s/%s", backpath, namepart(tmp, bw->b->name)); } else { snprintf(name, sizeof(name), "%s", bw->b->name); } for (x = strlen(name); name[--x] != '.';) { if (name[x] == '\\' || (name[x] == ':' && x == 1) || x == 0) { x = strlen(name); break; } } strcpy(name + x, ".bak"); #else /* Create backup file name */ char *simple_backup_suffix = getenv("SIMPLE_BACKUP_SUFFIX"); if (simple_backup_suffix == NULL) { simple_backup_suffix = "~"; } if (backpath) { snprintf(name, sizeof(name), "%s/%s%s", backpath, namepart(tmp, bw->b->name), simple_backup_suffix); } else { snprintf(name, sizeof(name), "%s%s", bw->b->name, simple_backup_suffix); } /* Attempt to delete backup file first */ unlink(name); #endif /* Copy original file to backup file */ if (cp(bw->b->name, name)) { return 1; } else { bw->b->backup = 1; return 0; } } else { return 0; } } /* Write file */ struct savereq { int (*callback) (); char *name; }; static int saver(BW *bw, int c, struct savereq *req, int *notify) { int (*callback) (); int fl; callback = req->callback; if (c == 'n' || c == 'N') { vsrm(req->name); joe_free(req); if (notify) { *notify = 1; } msgnw(bw->parent, "Couldn't make backup file... file not saved"); if (callback) { return callback(bw, -1); } else { return -1; } } if (c != 'y' && c != 'Y') { if (mkqw(bw->parent, sc("Could not make backup file. Save anyway (y,n,^C)? "), saver, NULL, req, notify)) { return 0; } else { if (notify) *notify = 1; return -1; } } if (notify) { *notify = 1; } if (bw->b->er == -1 && bw->o.msnew) { exemac(bw->o.msnew); bw->b->er = -3; } if (bw->b->er == 0 && bw->o.msold) { exemac(bw->o.msold); } if ((fl = bsave(bw->b->bof, req->name, bw->b->eof->byte)) != 0) { msgnw(bw->parent, msgs[fl + 5]); vsrm(req->name); joe_free(req); if (callback) { return callback(bw, -1); } else { return -1; } } else { if (!bw->b->name) bw->b->name = joesep(strdup(req->name)); if (!strcmp(bw->b->name, req->name)) { bw->b->changed = 0; saverr(bw->b->name); } { /* Last UNDOREC which wasn't modified will be changed * to modified. And because this block is * executed after each 'save', there can't be more * than one record which is not modified * 24 Apr 2001, Marx */ UNDO *u = bw->b->undo; UNDOREC *rec, *rec_start; rec = rec_start = &u->recs; do { rec = rec->link.prev; } while (rec != rec_start && rec->changed); if(rec->changed == 0) rec->changed = 1; } genexmsg(bw, 1, req->name); vsrm(req->name); joe_free(req); if (callback) { return callback(bw, 0); } else { return 0; } } } static int dosave(BW *bw, char *s, int (*callback) (), int *notify) { struct savereq *req = (struct savereq *) joe_malloc(sizeof(struct savereq)); req->name = s; req->callback = callback; if (backup(bw)) { return saver(bw, 0, req, notify); } else { return saver(bw, 'y', req, notify); } } static int dosave2(BW *bw, int c, char *s, int *notify) { if (c == 'y' || c == 'Y') { return dosave(bw, s, NULL, notify); } else if (c == 'n' || c == 'N') { if (notify) { *notify = 1; } genexmsg(bw, 0, s); vsrm(s); return -1; } else if (mkqw(bw->parent, sc("File exists. Overwrite (y,n,^C)? "), dosave2, NULL, s, notify)) { return 0; } else { return -1; } } static int dosave1(BW *bw, char *s, void *object, int *notify) { int f; if (s[0] != '!' && !(s[0] == '>' && s[1] == '>') && (!bw->b->name || strcmp(s, bw->b->name))) { f = open(s, O_RDONLY); if (f != -1) { close(f); return dosave2(bw, 0, s, notify); } } return dosave(bw, s, object, notify); } int usave(BW *bw) { BW *pbw; pbw = wmkpw(bw->parent, "Name of file to save (^C to abort): ", &filehist, dosave1, "Names", NULL, cmplt, NULL, NULL); if (pbw && bw->b->name) { binss(pbw->cursor, bw->b->name); pset(pbw->cursor, pbw->b->eof); pbw->cursor->xcol = piscol(pbw->cursor); } if (pbw) { return 0; } else { return -1; } } /* Load file to edit */ int doedit(BW *bw, char *s, void *obj, int *notify) { int ret = 0; int er; void *object; W *w; B *b; if (notify) { *notify = 1; } if (bw->pid) { msgnw(bw->parent, "Process running in this window"); return -1; } b = bfind(s); er = error; if (bw->b->count == 1 && (bw->b->changed || bw->b->name)) { if (orphan) { orphit(bw); } else { if (uduptw(bw)) { brm(b); return -1; } bw = (BW *) maint->curwin->object; } } if (er) { msgnwt(bw->parent, msgs[er + 5]); if (er != -1) { ret = -1; } } object = bw->object; w = bw->parent; bwrm(bw); w->object = (void *) (bw = bwmk(w, b, 0)); wredraw(bw->parent); bw->object = object; vsrm(s); if (er == -1 && bw->o.mnew) { exemac(bw->o.mnew); } if (er == 0 && bw->o.mold) { exemac(bw->o.mold); } return ret; } int okrepl(BW *bw) { if (bw->b->count == 1 && bw->b->changed) { msgnw(bw->parent, "Can't replace modified file"); return -1; } else { return 0; } } int uedit(BW *bw) { if (wmkpw(bw->parent, "Name of file to edit (^C to abort): ", &filehist, doedit, "Names", NULL, cmplt, NULL, NULL)) { return 0; } else { return -1; } } /* Load file into buffer: can result in an orphaned buffer */ static int dorepl(BW *bw, char *s, void *obj, int *notify) { void *object = bw->object; int ret = 0; int er; W *w = bw->parent; B *b; if (notify) { *notify = 1; } if (bw->pid) { msgnw(bw->parent, "Process running in this window"); return -1; } b = bfind(s); er = error; if (error) { msgnwt(bw->parent, msgs[error + 5]); if (error != -1) { ret = -1; } } if (bw->b->count == 1 && (bw->b->changed || bw->b->name)) { orphit(bw); } bwrm(bw); w->object = (void *) (bw = bwmk(w, b, 0)); wredraw(bw->parent); bw->object = object; vsrm(s); if (er == -1 && bw->o.mnew) { exemac(bw->o.mnew); } if (er == 0 && bw->o.mold) { exemac(bw->o.mold); } return ret; } /* Switch to next buffer in window */ int unbuf(BW *bw) { void *object = bw->object; W *w = bw->parent; B *b; if (bw->pid) { msgnw(bw->parent, "Process running in this window"); return -1; } b = bnext(); if (b == bw->b) { b = bnext(); } if (b == bw->b) { return -1; } if (!b->orphan) { ++b->count; } else { b->orphan = 0; } if (bw->b->count == 1) { orphit(bw); } bwrm(bw); w->object = (void *) (bw = bwmk(w, b, 0)); wredraw(bw->parent); bw->object = object; return 0; } int upbuf(BW *bw) { void *object = bw->object; W *w = bw->parent; B *b; if (bw->pid) { msgnw(bw->parent, "Process running in this window"); return -1; } b = bprev(); if (b == bw->b) { b = bprev(); } if (b == bw->b) { return -1; } if (!b->orphan) { ++b->count; } else { b->orphan = 0; } if (bw->b->count == 1) { orphit(bw); } bwrm(bw); w->object = (void *) (bw = bwmk(w, b, 0)); wredraw(bw->parent); bw->object = object; return 0; } int uinsf(BW *bw) { if (wmkpw(bw->parent, "Name of file to insert (^C to abort): ", &filehist, doinsf, "Names", NULL, cmplt, NULL, NULL)) { return 0; } else { return -1; } } /* Save and exit */ static int exdone(BW *bw, int flg) { if (flg) { if (bw->b->name) joe_free(bw->b->name); bw->b->name = 0; return -1; } else { bw->b->changed = 0; saverr(bw->b->name); return uabort(bw, MAXINT); } } static int exdone1(BW *bw, int flg) { if (flg) { return -1; } else { bw->b->changed = 0; saverr(bw->b->name); return uabort(bw, MAXINT); } } static int doex(BW *bw, char *s, void *object,int *notify) { bw->b->name = joesep(strdup(s)); return dosave(bw, s, exdone, notify); } int uexsve(BW *bw) { if (!bw->b->changed) { uabort(bw, MAXINT); return 0; } else if (bw->b->name && !exask) { return dosave(bw, vsncpy(NULL, 0, sz(bw->b->name)), exdone1, NULL); } else { BW *pbw = wmkpw(bw->parent, "Name of file to save (^C to abort): ", &filehist, doex, "Names", NULL, cmplt, NULL, NULL); if (pbw && bw->b->name) { binss(pbw->cursor, bw->b->name); pset(pbw->cursor, pbw->b->eof); pbw->cursor->xcol = piscol(pbw->cursor); } if (pbw) { return 0; } else { return -1; } } } /* If buffer is modified, prompt for saving */ static int nask(BW *bw, int c, void *object, int *notify) { if (c == 'y' || c == 'Y') { if (bw->b->name) { return dosave1(bw, vsncpy(NULL, 0, sz(bw->b->name)), object, notify); } else { BW *pbw = wmkpw(bw->parent, "Name of file to save (^C to abort): ", &filehist, dosave1, "Names", NULL, cmplt, object, notify); if (pbw) { return 0; } else { return -1; } } } else if (c == 'n' || c == 'N') { genexmsg(bw, 0, NULL); if (notify) { *notify = 1; } return 0; } else if (bw->b->count == 1 && bw->b->changed) { if (mkqw(bw->parent, sc("Save changes to this file (y,n,^C)? "), nask, NULL, object, notify)) { return 0; } else { return -1; } } else { if (notify) { *notify = 1; } return 0; } } int uask(BW *bw) { return nask(bw, 0, NULL, NULL); } /* FIXME: unused ???? */ #if 0 /* Ask to save file if it is modified. If user answers yes, run save */ static int nask2(BW *bw, int c, void *object, int *notify) { if (c == 'y' || c == 'Y') { BW *pbw = wmkpw(bw->parent, "Name of file to save (^C to abort): ", &filehist, dosave1, "Names", NULL, cmplt, object, notify); if (pbw) { return 0; } else { return -1; } } else if (c == 'n' || c == 'N') { genexmsg(bw, 0, NULL); if (notify) { *notify = 1; } return 0; } else if (bw->b->count == 1 && bw->b->changed) { if (mkqw(bw->parent, sc("Save changes to this file (y,n,^C)? "), nask, NULL, object, notify)) { return 0; } else { return -1; } } else { if (notify) { *notify = 1; } return 0; } } static int uask2(BW *bw) { return nask2(bw, 0, NULL, NULL); } #endif /* If buffer is modified, ask if it's ok to lose changes */ static int dolose(BW *bw, int c, void *object, int *notify) { W *w; if (notify) { *notify = 1; } if (c != 'y' && c != 'Y') { return -1; } genexmsg(bw, 0, NULL); if (bw->b->count == 1) { bw->b->changed = 0; } object = bw->object; w = bw->parent; bwrm(bw); w->object = (void *) (bw = bwmk(w, bfind(""), 0)); wredraw(bw->parent); bw->object = object; if (bw->o.mnew) { exemac(bw->o.mnew); } return 0; } int ulose(BW *bw) { msgnw(bw->parent, NULL); if (bw->pid) { return ukillpid(bw); } if (bw->b->count == 1 && bw->b->changed) { if (mkqw(bw->parent, sc("Lose changes to this file (y,n,^C)? "), dolose, NULL, NULL, NULL)) { return 0; } else { return -1; } } else { return dolose(bw, 'y', NULL, NULL); } } /* Buffer list */ static int dobuf(MENU *m, int x, char **s) { char *name; BW *bw = m->parent->win->object; int *notify = m->parent->notify; m->parent->notify = 0; name = vsdup(s[x]); wabort(m->parent); return dorepl(bw, name, NULL, notify); } static int abrtb(MENU *m, int x, char **s) { varm(s); return -1; } int ubufed(BW *bw) { char **s = getbufs(); vasort(av(s)); if (mkmenu(bw->parent, s, dobuf, abrtb, NULL, 0, s, NULL)) return 0; else { varm(s); return -1; } }