This is file.c in view mode; [Download] [Up]
/* * Copyright (c) 1992, 1995 John E. Davis (davis@space.mit.edu) * All Rights Reserved. */ #include <config.h> #include <stdio.h> #include <slang.h> #include <string.h> #include <limits.h> #ifdef HAVE_STDLIB_H # include <stdlib.h> #endif #ifdef HAVE_UNISTD_H # include <unistd.h> #endif #ifdef unix # include <sys/types.h> # include <sys/stat.h> # include <sys/file.h> # ifdef SYSV # include <sys/fcntl.h> # endif #endif #ifdef __os2__ #include <fcntl.h> #include <io.h> #include <sys/types.h> #include <sys/stat.h> typedef struct HOLDFEA *PHOLDFEA; PHOLDFEA QueryEAs (char *name); int WriteEAs (char *name, PHOLDFEA pHoldFEA); #endif #ifdef msdos #include <fcntl.h> #include <io.h> #include <sys/stat.h> #endif #ifdef __GO32__ #include <fcntl.h> #endif #ifdef __DECC #include <unixio.h> #include <unixlib.h> #endif #include <errno.h> /* Was anything missed? */ #ifndef O_RDONLY #ifdef VMS #include <file.h> #else #include <fcntl.h> #endif #endif #include "buffer.h" #include "file.h" #include "misc.h" #include "sysdep.h" #include "paste.h" #include "ins.h" #include "ledit.h" #if defined (msdos) || defined (__os2_16__) #define MAX_LINE_LEN 1024 #else #define MAX_LINE_LEN 64 * 1024 #endif #ifdef VMS static int vms_max_rec_size; #include <stat.h> #include <rms.h> static int VMS_write_rfm_fixed; int vms_stupid_open(char *file) { struct stat s; char rat_buf[80], rfm_buf[80], mrs_buf[40], *rfm = "var"; unsigned short mode = 0, c; int ret; VMS_write_rfm_fixed = 0; strcpy(rfm_buf, "rfm="); if (0 == stat(file, &s)) { strcpy(rat_buf, "rat"); c = s.st_fab_rat; if (c & FAB$M_FTN) strcat(rat_buf, ",ftn"); if (c & FAB$M_CR) strcat(rat_buf, ",cr"); if (c & FAB$M_PRN) strcat(rat_buf, ",prn"); if (c & FAB$M_BLK) strcat(rat_buf, ",blk"); if (rat_buf[3] != 0) rat_buf[3] = '='; else *rat_buf = 0; c = s.st_fab_rfm; switch(c) { case FAB$C_UDF: rfm = "udf"; break; case FAB$C_FIX: rfm = "fix"; if (s.st_fab_rat & (FAB$M_CR | FAB$M_CR)) VMS_write_rfm_fixed = 1; break; case FAB$C_VAR: rfm = "var"; break; case FAB$C_VFC: rfm = "vfc"; break; case FAB$C_STM: rfm = "stm"; break; case FAB$C_STMLF: rfm = "stmlf"; break; case FAB$C_STMCR: rfm = "stmcr"; break; } mode = s.st_mode & 0777; } else strcpy (rat_buf, "rat=cr"); strcat(rfm_buf, rfm); if (vms_max_rec_size <= 0) vms_max_rec_size = 255; sprintf(mrs_buf, "mrs=%d", vms_max_rec_size); if (*rfm == 's') /* stream */ { ret = creat(file, mode, rfm_buf); } else { if (*rat_buf) ret = creat(file, mode, rfm_buf, mrs_buf, rat_buf); else ret = creat(file, mode, rfm_buf, mrs_buf); } if (ret >= 0) chmod(file, mode); return ret; } #endif int Require_Final_Newline = 0; /* 0 = read, 1 = write , 2 = append... */ static int sys_open(char *file, int acces) { int fp = -1; int flags; unsigned int mode; #ifdef VMS char *p, neew[256]; #endif #ifdef O_BINARY mode = O_BINARY; #else mode = 0; #endif flags = file_status(file); if ((flags < 0) || (flags > 1)) return(fp); /* on VMS I cheat since I do not want to deal with RMS at this point */ #ifdef VMS VMS_write_rfm_fixed = 0; strcpy(neew, file); p = neew; while (*p) if (*p == ';') *p = 0; else p++; if (acces == 0) fp = open(file, O_RDONLY, "ctx=rec","mbf=8","mbc=32","rop=RAH","shr=upi,get,put"); else if (acces == 1) { fp = vms_stupid_open(neew); } else if (acces == 2) fp = open(file, O_WRONLY | O_APPEND | O_CREAT | mode); #else switch(acces) { case 0: flags = O_RDONLY; break; case 1: flags = O_WRONLY | O_CREAT | O_TRUNC; break; case 2: flags = O_WRONLY | O_CREAT | O_APPEND; break; default: return(fp); } flags |= mode; #if defined (msdos) || defined (__os2__) fp = open(file, flags, S_IREAD | S_IWRITE); #else fp = open(file, flags, 0666); #endif #endif /* VMS */ return(fp); } /* Leaves Point at last line inserted */ /* returns -1 if unable to open file, -2 if memory allocation error otherwise returns number of lines read */ char *file_type(char *file) { char *p, *psave; if ((file == (char *) NULL) || (*file == 0)) return( (char *) NULL); file = extract_file(file); p = file; while (*p != 0) p++; psave = p; while((p != file) && (*p != '.')) p--; if (*p == '.') p++; if (p == file) return(psave); else return(p); } void set_file_modes() { char *type; if (CBuf == NULL) return; CBuf->c_time = sys_time(); if (CBuf->file[0]) { #if defined(msdos) && !defined(W_OK) #define W_OK 2 #define F_OK 0 #endif #ifdef W_OK #ifdef __GO32__ # define access i386_access #endif char name[512]; #ifndef VMS { /* COmment: I need a more generic call to contruct a directory * name. site.sl already does this properly. */ int n; strcpy (name, CBuf->dir); n = strlen (name); if (n > #ifdef pcsystem 3 /* allow C:/file */ #else 1 /* allow /file */ #endif ) { name[n - 1] = 0; /* knock off slash */ if (!access (name, F_OK) && (access (name, W_OK))) CBuf->flags |= READ_ONLY; } } #endif /* NOT VMS */ sprintf (name, "%s%s", CBuf->dir, CBuf->file); if (!access(name, F_OK) && access(name, W_OK)) CBuf->flags |= READ_ONLY; #endif /* W_OK */ CBuf->flags |= AUTO_SAVE_BUFFER; CBuf->hits = 0; type = file_type(CBuf->file); } else type = (char *) NULL; if (type == (char *) NULL) CBuf->modes = NO_MODE; else if (SLang_run_hooks("mode_hook", type, NULL)); else CBuf->modes = NO_MODE; } int read_file(char *file) { int fp; int n, status; if ((fp = sys_open(file, 0)) < 0) { status = file_status(file); if (!status) return(-1); /* file does not exist */ return(-2); /* exists but not readable */ } n = read_file_pointer(fp); close(fp); eob(); if ('\n' == *(CLine->data + Point)) make_line(2); VFile_Mode = VFILE_TEXT; return n; } int insert_file_pointer(VFILE *vp) { int n = 0; unsigned int num; unsigned char *vbuf; Suspend_Screen_Update = 1; while(NULL != (vbuf = (unsigned char *) vgets(vp, &num))) { n++; if (SLang_Error) break; quick_insert(vbuf, (int) num); } return(n); } int insert_file(char *file) { VFILE *vp; int n; if (NULL == (vp = vopen(file, 0, VFile_Mode))) return(-1); n = insert_file_pointer(vp); vclose(vp); return(n); } #ifdef unix # define BUFSIZE 0x10000 #else #ifdef VMS # define BUFSIZE 0x3FFF #else # define BUFSIZE 512 #endif #endif static int Output_Buffer_Size = BUFSIZE; static char Output_Buffer[BUFSIZE]; static char *Output_Bufferp; static char *Output_Bufferp_max; /* definitely perform the write. Return number of chars written */ static int jed_write1(int fd, char *b, unsigned int n) { #if !defined(msdos) || defined(__WATCOMC__) || defined(__WIN32__) int len; unsigned int total = 0; #ifdef VMS register char *bmax; #endif while (total < n) { len = n - total; #ifdef VMS if (VMS_write_rfm_fixed) { } /* VMS wants to terminate a record with a cr so adjust for this * unfortunate fact. The len - 1 stuff is so *bmax does not peek * beyond its boundary. */ bmax = b + (len - 1); while ((bmax > b) && (*bmax != '\n')) bmax--; if (bmax == b) bmax = b + (len - 1); /* cannot be helped */ len = (int) (bmax - b) + 1; #endif if ((len = write (fd, b, len)) < 0) { /* If the write is interruped, what should I do?? */ break; } total += (unsigned int) len; b += len; } return total; #else int num = -1; asm mov ah, 40h asm mov bx, fd asm mov cx, n asm push ds asm lds dx, dword ptr b asm int 21h asm pop ds asm jc L1 asm mov num, ax /* number of bytes written */ L1: return(num); #endif } /* RMS wants to start a NEW record after a write so just forget it! */ /* maybe do write-- return number of chars possibly written */ static int jed_write(int fd, char *b, unsigned int n) { int num, max, nl_flag = 0; unsigned int nsave = n; int cr_flag = CBuf->flags & ADD_CR_ON_WRITE_FLAG; #ifdef MAP_CR_TO_NL_FLAG if (CBuf->flags & MAP_CR_TO_NL_FLAG) { char *bmax = b + n; char *p, *pmax, ch; p = Output_Bufferp; pmax = Output_Bufferp_max; while (b < bmax) { ch = *b++; if ((ch == '\r') || (ch == '\n')) { if (cr_flag) { *p++ = ch; if (p == pmax) { num = (int) (Output_Bufferp_max - Output_Bufferp); if (num != jed_write1 (fd, Output_Bufferp, num)) return -1; Output_Bufferp = Output_Buffer; p = Output_Bufferp; } } *p++ = '\n'; } else *p++ = ch; if (p == pmax) { num = (int) (Output_Bufferp_max - Output_Bufferp); if (num != jed_write1 (fd, Output_Buffer, num)) return -1; Output_Bufferp = Output_Buffer; p = Output_Bufferp; } } Output_Bufferp = p; return nsave; } #endif /* amount of space left in buffer */ /* copy whats in b to the output buffer */ while (n > 0) { num = (int) (Output_Bufferp_max - Output_Bufferp); if (n > num) { #ifdef VMS max = (int) (Output_Bufferp - Output_Buffer); if (max) { if (max != jed_write1(fd, Output_Buffer, max)) return(-1); Output_Bufferp = Output_Buffer; continue; } #endif max = num; MEMCPY(Output_Bufferp, b, max); Output_Bufferp += max; } else if (cr_flag && (*(b + (n - 1)) == '\n') && (VFile_Mode == VFILE_TEXT)) { max = n - 1; MEMCPY(Output_Bufferp, b, max); Output_Bufferp += max; *Output_Bufferp++ = '\r'; max++; /* can only write the \r */ if (n == num) nl_flag = 1; else *Output_Bufferp++ = '\n'; } else { max = n; MEMCPY(Output_Bufferp, b, max); Output_Bufferp += max; } if (Output_Bufferp == Output_Bufferp_max) { Output_Bufferp = Output_Buffer; if (Output_Buffer_Size != jed_write1(fd, Output_Buffer, Output_Buffer_Size)) return(-1); if (nl_flag) { nl_flag = 0; *Output_Bufferp++ = '\n'; } } b += max; n -= max; } return(nsave); } /* returns -1 on failure */ int write_region_to_fp(int fp) { register int pnt, len; register Line *first, *last; int last_pnt, n = 0; char *err = "Write Failed!"; #ifndef VMS char nl = '\n'; #endif Output_Bufferp = Output_Buffer; Output_Bufferp_max = Output_Buffer + BUFSIZE; Output_Buffer_Size = BUFSIZE; #ifdef VMS if (VMS_write_rfm_fixed && (vms_max_rec_size <= BUFSIZE)) { Output_Buffer_Size = vms_max_rec_size; } else VMS_write_rfm_fixed = 0; #endif if (!check_region(&Number_One)) return(-1); last = CLine; last_pnt = Point; pop_mark(&Number_One); first = CLine; pnt = Point; /* first should never be null without hitting last first. If this ever happens, check_region failed. */ while (first != last) { len = first->len - pnt; if (len != jed_write(fp, (char *) (first->data + pnt), len)) { msg_error(err); } /* This goes here inside the loop because it is possible for external events to set error_buffer */ pnt = 0; if (SLang_Error) break; first = first->next; n++; } if (!SLang_Error && (last_pnt != 0)) { len = last_pnt - pnt; if (len != jed_write(fp, (char *) (last->data + pnt), len)) { msg_error(err); } n++; } #ifndef VMS if ((Require_Final_Newline) && (CBuf->end == last)) { eob(); if (Point) jed_write(fp, &nl, 1); } #endif /* Now flush output buffer if necessary */ len = (int) (Output_Bufferp - Output_Buffer); if (!SLang_Error && len) if (len != jed_write1(fp, Output_Buffer, len)) { msg_error(err); } Output_Bufferp = Output_Buffer; pop_spot(); VFile_Mode = VFILE_TEXT; if (SLang_Error) return(-1); return(n); } /* write current buffer to open file pointer. Return number of lines */ static int jed_close (int fp) { while (-1 == close(fp)) { #ifdef EINTR #ifndef pc_system if (errno == EINTR) { errno = 0; sleep (1); continue; } #endif #endif msg_error ("Error closing file. File system may be full."); return -1; } return 0; } int write_region (char *file) { int fp; int n; char msg[256]; if (!check_region(&Number_Zero)) return(-1); if ((fp = sys_open(file, 1)) < 0) { sprintf(msg, "Unable to open %s for writing.", file); msg_error(msg); return(-1); } n = write_region_to_fp(fp); if (-1 == jed_close (fp)) n = -1; return(n); } /* returns -1 on failure and number of lines on success */ static int write_file(char *file) { Mark *m; int n = -1; int fnl; #ifdef VMS register Line *l = CBuf->beg; register int len = 0, max = 0; while (l != NULL) { len = l->len; if (len > max) max = len; l = l->next; } vms_max_rec_size = max; #endif push_spot(); bob(); push_mark(); m = CBuf->marks; eob(); fnl = Require_Final_Newline; if (CBuf->flags & BINARY_FILE) { VFile_Mode = VFILE_BINARY; Require_Final_Newline = 0; #ifdef VMS vms_max_rec_size = 512; #endif } n = write_region(file); Require_Final_Newline = fnl; VFile_Mode = VFILE_TEXT; if (m == CBuf->marks) pop_mark(&Number_Zero); pop_spot(); return(n); } int append_to_file(char *file) { int fp; int n; if ((fp = sys_open(file, 2)) < 0) return(-1); n = write_region_to_fp(fp); if (-1 == jed_close (fp)) n = -1; check_buffers(); return(n); } static int make_autosave_filename(char *save, char *dir, char *file) { char *s; int dat; if (*file == 0) return(0); if (SLang_run_hooks("make_autosave_filename", dir, file) && !SLang_Error) { if (SLang_pop_string(&s, &dat)) return(0); strncpy(save, s, 254); save[255] = 0; if (dat == 1) SLFREE(s); } else { #ifdef unix sprintf(save, "%s#%s#", dir, file); #else #if defined (msdos) || defined (__os2__) sprintf(save, "%s#%s", dir, file); #else sprintf(save, "%s_$%s;1", dir, file); #endif #endif } return(1); } int write_file_with_backup(char *dir, char *file) { char neew[256]; char save[256]; int n; int mode, do_mode; short uid, gid; #ifndef VMS char *old; int ok = 0, bu, do_free = 0; #endif #ifdef __os2__ PHOLDFEA EAs; #endif if (*file == 0) return(-1); sprintf(neew, "%s%s", dir, file); if (((CBuf->flags & AUTO_SAVE_BUFFER) == 0) || !make_autosave_filename(save, dir, file)) *save = 0; do_mode = sys_chmod(neew, 0, &mode, &uid, &gid); if ((do_mode < 0) || (do_mode > 1)) return(-1); #ifndef VMS #ifdef __os2__ EAs = QueryEAs (neew); #endif bu = 1; if (((CBuf->flags & NO_BACKUP_FLAG) == 0) && SLang_run_hooks("make_backup_filename", dir, file)) { if (((bu = !SLang_pop_string(&old, &do_free)) != 0) && (*old)) { unlink(old); ok = !rename(neew, old); } } #endif if (-1 != (n = write_file(neew))) { if (*save) { sys_delete_file(save); } if (do_mode) /* must be an existing file, so preserve mode */ { #ifdef msdos /* Want the archive bit set since this is a new version */ mode |= 1 << 5; #endif sys_chmod (neew, 1, &mode, &uid, &gid); #ifdef __os2__ WriteEAs (neew, EAs); #endif } /* This is for NFS time problems */ CBuf->c_time = sys_file_mod_time(neew); /* Since we wrote the buffer to the file, it is not modified. */ if ( #ifdef __os2__ !strcmpi(file, CBuf->file) && !strcmp(dir, CBuf->dir) #else !strcmp(file, CBuf->file) && !strcmp(dir, CBuf->dir) #endif ) CBuf->flags &= ~FILE_MODIFIED; /* CBuf->c_time = sys_time(); */ } #ifndef VMS else if (ok && bu && *old) rename(old, neew); /* error -- put it back */ if ((do_free == 1) && bu) SLFREE(old); #endif return(n); } /* warning-- this saves on the narrowed part of buffer. * Here, I widen first. I need a save_restriction type of thing because * I do not narrow back. */ void auto_save_buffer(Buffer *b) { char tmp[256]; Buffer *old_buf; unsigned int vfm = VFile_Mode; if (b == NULL) return; b->hits = 0; if ((b->narrow != NULL) || !(b->flags & BUFFER_TRASHED)) return; old_buf = CBuf; switch_to_buffer(b); if (b->flags & BINARY_FILE) VFile_Mode = VFILE_BINARY; else VFile_Mode = VFILE_TEXT; if (b->flags & AUTO_SAVE_BUFFER) { if (make_autosave_filename(tmp, b->dir, b->file)) { flush_message("autosaving..."); sys_delete_file(tmp); write_file(tmp); message("autosaving...done"); } } else if (b->flags & AUTO_SAVE_JUST_SAVE) { if (write_file_with_backup(b->dir, b->file) >= 0) { b->flags &= ~BUFFER_TRASHED; } } switch_to_buffer(old_buf); VFile_Mode = vfm; } void auto_save_all() { Buffer *b; if (NULL == (b = CBuf)) return; do { while (b->narrow != NULL) widen_buffer(b); if ((*b->file != 0) && (b->hits != 0)) auto_save_buffer(b); b = b->next; } while (b != CBuf); } #ifdef unix #ifndef __GO32__ static int is_link(char *f, char *f1) { struct stat s; int l; int is_dir = 0; char work[256]; l = strlen(f) - 1; if ((f[l] == '/') && (l > 1)) { strcpy(work, f); is_dir = 1; f = work; f[l] = 0; } if (( lstat(f, &s) == -1 ) /* || ((s.st_mode & S_IFMT) S_IFLNK)) */ || (((s.st_mode & S_IFMT) & S_IFLNK) == 0)) return(0); l = 255; if (is_dir) { /* This way, it will be expanded properly upon return */ *f1++ = '.'; *f1++ = '.'; *f1++ = '/'; l -= 4; /* 1 for final slash */ } if (-1 == (l = readlink(f, f1, l))) return(0); if (is_dir) f1[l++] = '/'; f1[l] = 0; return(1); } /* This routine should be modified to work with all components of the path * and not just the last component. For example, suppose /usr/lib/jed is a * link to /usr/local/src/jed and one tries to read the file * /usr/lib/jed/lib/site.sl. This should be expanded to * /usr/local/lib/jed/lib/site.sl. Currently, it will only have an effect if * site.sl is itself a link. */ char *expand_link(char *f) { char work[256], lnk[256]; char *d = expand_filename(f); if (is_link(d, lnk)) { strcpy(work, d); *(extract_file(work)) = 0; strcat(work, lnk); d = expand_filename(work); } return (d); } #endif #endif void visit_file(char *dir, char *file) { #ifndef __os2__ if (strcmp(file, CBuf->file) || strcmp(dir, CBuf->dir)) #else if (strcmpi(file, CBuf->file) || strcmp(dir, CBuf->dir)) #endif { if (NULL == find_buffer(file)) strcpy(CBuf->name, file); else uniquely_name_buffer(file); strcpy(CBuf->dir, dir); strcpy(CBuf->file, file); } /* We have already taken care of this in write buffer function. */ /* CBuf->c_time = sys_time(); */ check_buffers(); } char *dir_file_merge(char *dir, char *file) /* * returns result of merging dir with file. If file is empty, dir is * considered to be whole file. */ { char name[512]; strcpy (name, dir); if ((file != NULL) && *file) { fixup_dir(name); strcat(name, file); } return expand_filename(name); } int file_status(char *file) /* * Returns a coded integer about file. If the file does not exist, 0 is * returned. Meaning: * * 2 file is a directory * 1 file exists * 0 file does not exist. * -1 no access. * -2 path invalid * -3 unknown error */ { int mode = 0; short uid, gid; return sys_chmod(file, 0, &mode, &uid, &gid); } int file_changed_on_disk(char *file) { unsigned long t; Buffer *buf; if (NULL == (buf = find_file_buffer(file))) return(0); if (buf->flags & FILE_MODIFIED) return 1; t = sys_file_mod_time(file); return(t > buf->c_time); } int file_time_cmp(char *file1, char *file2) { unsigned long t1, t2; t1 = sys_file_mod_time(file1); t2 = sys_file_mod_time(file2); /*These times are modification times from 1970. Hence, the bigger * number represents the more recent change. Let '>' denote * 'more recent than'. This function returns: * 1: file1 > file2 * 0: file 1 == file2 * -1: otherwise. * So: */ if (t1 == t2) return(0); if (t1 > t2) return(1); return(-1); } void auto_save(void) { auto_save_buffer(CBuf); } void check_buffer(Buffer *b) { #ifdef REAL_UNIX_SYSTEM /* Update (or even invalidate) inode information since directories may have * been renamed, etc... */ (void) get_inode_info (b->dir, &b->device, &b->inode); #endif if ((*b->file != 0) && file_changed_on_disk(dir_file_merge(b->dir, b->file))) { b->flags |= FILE_MODIFIED; } else b->flags &= ~FILE_MODIFIED; } void check_buffers() { Buffer *b = CBuf; do { check_buffer(b); b = b->next; } while (b != CBuf); } void set_file_trans(int *mode) { if (*mode) VFile_Mode = VFILE_BINARY; else VFile_Mode = VFILE_TEXT; } int read_file_pointer(int fp) { int n = 1; unsigned int num; unsigned char *vbuf; VFILE *vp; if (SLang_Error || (vp = vstream(fp, MAX_LINE_LEN, VFile_Mode)) == NULL) return(-1); if (NULL == (vbuf = (unsigned char *) vgets(vp, &num))) return(0); #ifdef KEEP_SPACE_INFO if (CLine->space < num) #endif remake_line(CLine->len + num + 1); MEMCPY((char *) CLine->data, (char *) vbuf, (int) num); CLine->len = num; while(NULL != (vbuf = (unsigned char *) vgets(vp, &num))) { n++; if ((num == 1) && (*vbuf == '\n')) make_line(num); else make_line(num + 1); MEMCPY((char *) CLine->data, (char *) vbuf, (int) num); CLine->len = num; if (SLang_Error) break; } if (vp->buf != NULL) SLFREE(vp->buf); if (vp->cr_flag) CBuf->flags |= ADD_CR_ON_WRITE_FLAG; else CBuf->flags &= ~ADD_CR_ON_WRITE_FLAG; SLFREE(vp); return n; } #ifdef REAL_UNIX_SYSTEM int get_inode_info (char *dir, int *device, int *inode) { struct stat st; char work[512]; char *f; f = extract_file (dir); if (*f != 0) { unsigned int len = f - dir; strncpy (work, dir, len); work[len] = 0; dir = work; } *inode = *device = -1; if (-1 == stat (dir, &st)) return -1; *inode = st.st_ino; *device = st.st_dev; return 0; } #endif
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.