Annotation of src/usr.bin/mg/fileio.c, Revision 1.9
1.1 deraadt 1: /*
1.7 millert 2: * POSIX fileio.c
1.1 deraadt 3: */
4: #include "def.h"
5:
1.7 millert 6: static FILE *ffp;
1.1 deraadt 7:
8: #include <sys/types.h>
9: #include <sys/stat.h>
10: #include <sys/dir.h>
1.9 ! millert 11: #include <errno.h>
! 12: #include <fcntl.h>
! 13: #include <unistd.h>
1.1 deraadt 14:
15: /*
16: * Open a file for reading.
17: */
1.7 millert 18: int
19: ffropen(fn, bp)
20: char *fn;
21: BUFFER *bp;
22: {
23: struct stat statbuf;
24:
25: if ((ffp = fopen(fn, "r")) == NULL)
1.1 deraadt 26: return (FIOFNF);
27: if (bp && fstat(fileno(ffp), &statbuf) == 0) {
1.7 millert 28: /* set highorder bit to make sure this isn't all zero */
1.1 deraadt 29: bp->b_fi.fi_mode = statbuf.st_mode | 0x8000;
30: bp->b_fi.fi_uid = statbuf.st_uid;
31: bp->b_fi.fi_gid = statbuf.st_gid;
32: }
33: return (FIOSUC);
34: }
35:
36: /*
37: * Open a file for writing.
38: * Return TRUE if all is well, and
39: * FALSE on error (cannot create).
40: */
1.7 millert 41: int
42: ffwopen(fn, bp)
43: char *fn;
44: BUFFER *bp;
45: {
46:
47: if ((ffp = fopen(fn, "w")) == NULL) {
1.1 deraadt 48: ewprintf("Cannot open file for writing");
49: return (FIOERR);
50: }
1.7 millert 51:
52: /*
53: * If we have file information, use it. We don't bother to check for
54: * errors, because there's no a lot we can do about it. Certainly
55: * trying to change ownership will fail if we aren' root. That's
56: * probably OK. If we don't have info, no need to get it, since any
57: * future writes will do the same thing.
1.1 deraadt 58: */
59: if (bp && bp->b_fi.fi_mode) {
60: chmod(fn, bp->b_fi.fi_mode & 07777);
61: chown(fn, bp->b_fi.fi_uid, bp->b_fi.fi_gid);
62: }
63: return (FIOSUC);
64: }
65:
66: /*
67: * Close a file.
1.7 millert 68: * XXX - Should look at the status.
1.1 deraadt 69: */
1.7 millert 70: /* ARGSUSED */
71: int
72: ffclose(bp)
73: BUFFER *bp;
74: {
75:
1.1 deraadt 76: (VOID) fclose(ffp);
77: return (FIOSUC);
78: }
79:
80: /*
81: * Write a buffer to the already
82: * opened file. bp points to the
83: * buffer. Return the status.
84: * Check only at the newline and
85: * end of buffer.
86: */
1.7 millert 87: int
1.1 deraadt 88: ffputbuf(bp)
1.7 millert 89: BUFFER *bp;
1.1 deraadt 90: {
1.7 millert 91: char *cp;
92: char *cpend;
93: LINE *lp;
94: LINE *lpend;
95:
96: lpend = bp->b_linep;
97: lp = lforw(lpend);
98: do {
99: cp = <ext(lp)[0]; /* begining of line */
100: cpend = &cp[llength(lp)]; /* end of line */
101: while (cp != cpend) {
102: putc(*cp, ffp);
103: cp++; /* putc may evaluate arguments
104: more than once */
105: }
106: lp = lforw(lp);
107: if (lp == lpend)
108: break; /* no implied \n on last line */
109: putc('\n', ffp);
110: } while (!ferror(ffp));
111: if (ferror(ffp)) {
112: ewprintf("Write I/O error");
113: return FIOERR;
114: }
115: return (FIOSUC);
1.1 deraadt 116: }
117:
118: /*
119: * Read a line from a file, and store the bytes
120: * in the supplied buffer. Stop on end of file or end of
121: * line. When FIOEOF is returned, there is a valid line
122: * of data without the normally implied \n.
123: */
1.7 millert 124: int
1.1 deraadt 125: ffgetline(buf, nbuf, nbytes)
1.7 millert 126: char *buf;
127: int nbuf;
128: int *nbytes;
1.1 deraadt 129: {
1.7 millert 130: int c;
131: int i;
1.1 deraadt 132:
133: i = 0;
1.7 millert 134: while ((c = getc(ffp)) != EOF && c != '\n') {
1.1 deraadt 135: buf[i++] = c;
1.7 millert 136: if (i >= nbuf)
137: return FIOLONG;
1.1 deraadt 138: }
1.7 millert 139: if (c == EOF && ferror(ffp) != FALSE) {
1.1 deraadt 140: ewprintf("File read error");
141: return FIOERR;
142: }
143: *nbytes = i;
1.7 millert 144: return c == EOF ? FIOEOF : FIOSUC;
1.1 deraadt 145: }
146:
147: #ifndef NO_BACKUP
148: /*
1.9 ! millert 149: * Make a backup copy of "fname". On Unix the backup has the same
! 150: * name as the original file, with a "~" on the end; this seems to
! 151: * be newest of the new-speak. The error handling is all in "file.c".
! 152: * We do a copy instead of a rename since otherwise another process
! 153: * with an open fd will get the backup, not the new file. This is
! 154: * a problem when using mg with things like crontab and vipw.
1.1 deraadt 155: */
1.7 millert 156: int
157: fbackupfile(fn)
158: char *fn;
159: {
1.9 ! millert 160: struct stat sb;
! 161: int from, to, serrno;
! 162: size_t nread;
! 163: size_t len;
! 164: char buf[BUFSIZ];
1.7 millert 165: char *nname;
1.1 deraadt 166:
1.9 ! millert 167: len = strlen(fn);
! 168: if ((nname = malloc(len + 1 + 1)) == NULL) {
! 169: ewprintf("Can't get %d bytes", len + 1 + 1);
1.1 deraadt 170: return (ABORT);
171: }
172: (void) strcpy(nname, fn);
1.9 ! millert 173: (void) strcpy(nname + len, "~");
! 174:
! 175: if (stat(fn, &sb) == -1) {
! 176: ewprintf("Can't stat %s", fn);
! 177: return (FALSE);
! 178: }
! 179:
! 180: if ((from = open(fn, O_RDONLY)) == -1)
1.1 deraadt 181: return (FALSE);
1.9 ! millert 182: to = open(nname, O_WRONLY|O_CREAT|O_TRUNC, (sb.st_mode & 0777));
! 183: if (to == -1) {
! 184: serrno = errno;
! 185: close(from);
! 186: errno = serrno;
! 187: return (FALSE);
! 188: }
! 189: while ((nread = read(from, buf, sizeof(buf))) > 0) {
! 190: if (write(to, buf, nread) != nread) {
! 191: nread = -1;
! 192: break;
! 193: }
1.1 deraadt 194: }
1.9 ! millert 195: serrno = errno;
! 196: close(from);
! 197: close(to);
! 198: if (nread == -1)
! 199: unlink(nname);
1.1 deraadt 200: free(nname);
1.9 ! millert 201: errno = serrno;
! 202: return (nread == -1 ? FALSE : TRUE);
1.1 deraadt 203: }
204: #endif
205:
206: /*
207: * The string "fn" is a file name.
208: * Perform any required appending of directory name or case adjustments.
209: * If NO_DIR is not defined, the same file should be refered to even if the
210: * working directory changes.
211: */
212: #ifdef SYMBLINK
213: #include <sys/types.h>
214: #include <sys/stat.h>
215: #ifndef MAXLINK
216: #define MAXLINK 8 /* maximum symbolic links to follow */
217: #endif
218: #endif
219: #include <pwd.h>
220: #ifndef NO_DIR
1.7 millert 221: extern char *wdir;
1.1 deraadt 222: #endif
223:
1.7 millert 224: char *
225: adjustname(fn)
226: char *fn;
1.1 deraadt 227: {
1.7 millert 228: char *cp;
229: static char fnb[NFILEN];
230: struct passwd *pwent;
1.1 deraadt 231: #ifdef SYMBLINK
1.7 millert 232: struct stat statbuf;
233: int i, j;
234: char linkbuf[NFILEN];
1.1 deraadt 235: #endif
236:
1.7 millert 237: switch (*fn) {
238: case '/':
239: cp = fnb;
240: *cp++ = *fn++;
241: break;
1.1 deraadt 242: case '~':
1.7 millert 243: fn++;
1.8 millert 244: cp = getenv("HOME");
245: if (cp != NULL && *cp != '\0' && (*fn == '/' || *fn == '\0')) {
246: (VOID) strcpy(fnb, cp);
1.7 millert 247: cp = fnb + strlen(fnb);
248: if (*fn)
249: fn++;
250: break;
1.1 deraadt 251: } else {
1.7 millert 252: cp = fnb;
253: while (*fn && *fn != '/')
254: *cp++ = *fn++;
255: *cp = '\0';
256: if ((pwent = getpwnam(fnb)) != NULL) {
257: (VOID) strcpy(fnb, pwent->pw_dir);
258: cp = fnb + strlen(fnb);
259: break;
260: } else {
261: fn -= strlen(fnb) + 1;
262: /* can't find ~user, continue to default case */
263: }
1.1 deraadt 264: }
265: default:
266: #ifndef NODIR
1.7 millert 267: strcpy(fnb, wdir);
268: cp = fnb + strlen(fnb);
269: break;
1.1 deraadt 270: #else
1.7 millert 271: return fn; /* punt */
1.1 deraadt 272: #endif
1.7 millert 273: }
274: if (cp != fnb && cp[-1] != '/')
275: *cp++ = '/';
276: while (*fn) {
277: switch (*fn) {
278: case '.':
279: switch (fn[1]) {
280: case '\0':
281: *--cp = '\0';
282: return fnb;
283: case '/':
284: fn += 2;
285: continue;
286: case '.':
287: if (fn[2] != '/' && fn[2] != '\0')
288: break;
1.1 deraadt 289: #ifdef SYMBLINK
1.7 millert 290: cp[-1] = '\0';
291: for (j = MAXLINK; j-- &&
292: lstat(fnb, &statbuf) != -1 &&
293: (statbuf.st_mode & S_IFMT) == S_IFLNK &&
294: (i = readlink(fnb, linkbuf, sizeof linkbuf))
295: != -1;) {
296: if (linkbuf[0] != '/') {
297: --cp;
298: while (cp > fnb && *--cp != '/') {
299: }
300: ++cp;
301: (VOID) strncpy(cp, linkbuf, i);
302: cp += i;
303: } else {
304: (VOID) strncpy(fnb, linkbuf, i);
305: cp = fnb + i;
306: }
307: if (cp[-1] != '/')
308: *cp++ = '\0';
309: else
310: cp[-1] = '\0';
1.1 deraadt 311: }
1.7 millert 312: cp[-1] = '/';
1.1 deraadt 313: #endif
1.7 millert 314: --cp;
315: while (cp > fnb && *--cp != '/') {
316: }
317: ++cp;
318: if (fn[2] == '\0') {
319: *--cp = '\0';
320: return fnb;
321: }
322: fn += 3;
323: continue;
324: default:
325: break;
326: }
327: break;
328: case '/':
329: fn++;
330: continue;
331: default:
332: break;
333: }
334: while (*fn && (*cp++ = *fn++) != '/') {
335: }
336: }
337: if (cp[-1] == '/')
338: --cp;
339: *cp = '\0';
340: return fnb;
1.1 deraadt 341: }
342:
343: #ifndef NO_STARTUP
344: #include <sys/file.h>
345:
346: /*
347: * Find a startup file for the user and return its name. As a service
348: * to other pieces of code that may want to find a startup file (like
349: * the terminal driver in particular), accepts a suffix to be appended
350: * to the startup file name.
351: */
352: char *
353: startupfile(suffix)
1.7 millert 354: char *suffix;
1.1 deraadt 355: {
1.7 millert 356: char *file;
357: static char home[NFILEN];
1.1 deraadt 358:
1.8 millert 359: if ((file = getenv("HOME")) == NULL || *file == '\0')
1.7 millert 360: goto notfound;
361: if (strlen(file) + 7 >= NFILEN - 1)
362: goto notfound;
1.1 deraadt 363: (VOID) strcpy(home, file);
364: (VOID) strcat(home, "/.mg");
365: if (suffix != NULL) {
366: (VOID) strcat(home, "-");
367: (VOID) strcat(home, suffix);
368: }
1.7 millert 369: if (access(home, F_OK) == 0)
370: return home;
1.1 deraadt 371:
372: notfound:
373: #ifdef STARTUPFILE
374: file = STARTUPFILE;
375: if (suffix != NULL) {
376: (VOID) strcpy(home, file);
377: (VOID) strcat(home, "-");
378: (VOID) strcat(home, suffix);
379: file = home;
380: }
1.7 millert 381: if (access(file, F_OK) == 0)
382: return file;
1.1 deraadt 383: #endif
384:
385: return NULL;
386: }
387: #endif
388:
389: #ifndef NO_DIRED
1.4 millert 390: #include <sys/wait.h>
1.1 deraadt 391: #include "kbd.h"
392:
1.7 millert 393: int
1.1 deraadt 394: copy(frname, toname)
1.7 millert 395: char *frname;
396: char *toname;
1.1 deraadt 397: {
1.7 millert 398: pid_t pid;
399: int status;
1.1 deraadt 400:
1.7 millert 401: if ((pid = vfork())) {
402: if (pid == -1)
403: return -1;
404: execl("/bin/cp", "cp", frname, toname, (char *) NULL);
405: _exit(1); /* shouldn't happen */
406: }
407: while (wait(&status) != pid);
408: return status == 0;
409: }
410:
411: BUFFER *
412: dired_(dirname)
413: char *dirname;
414: {
415: BUFFER *bp;
416: FILE *dirpipe;
417: char line[256];
418:
419: if ((dirname = adjustname(dirname)) == NULL) {
420: ewprintf("Bad directory name");
421: return NULL;
422: }
423: if (dirname[strlen(dirname) - 1] != '/')
424: (VOID) strcat(dirname, "/");
425: if ((bp = findbuffer(dirname)) == NULL) {
426: ewprintf("Could not create buffer");
427: return NULL;
428: }
429: if (bclear(bp) != TRUE)
430: return FALSE;
431: (VOID) strcpy(line, "ls -al ");
432: (VOID) strcpy(&line[7], dirname);
433: if ((dirpipe = popen(line, "r")) == NULL) {
434: ewprintf("Problem opening pipe to ls");
435: return NULL;
436: }
437: line[0] = line[1] = ' ';
438: while (fgets(&line[2], 254, dirpipe) != NULL) {
439: line[strlen(line) - 1] = '\0'; /* remove ^J */
440: (VOID) addline(bp, line);
441: }
442: if (pclose(dirpipe) == -1) {
443: ewprintf("Problem closing pipe to ls");
444: return NULL;
445: }
446: bp->b_dotp = lforw(bp->b_linep); /* go to first line */
447: (VOID) strncpy(bp->b_fname, dirname, NFILEN);
448: if ((bp->b_modes[0] = name_mode("dired")) == NULL) {
449: bp->b_modes[0] = &map_table[0];
450: ewprintf("Could not find mode dired");
451: return NULL;
452: }
453: bp->b_nmodes = 0;
454: return bp;
1.1 deraadt 455: }
456:
1.7 millert 457: int
1.1 deraadt 458: d_makename(lp, fn)
1.7 millert 459: LINE *lp;
460: char *fn;
1.1 deraadt 461: {
1.7 millert 462: char *cp;
1.1 deraadt 463:
1.7 millert 464: if (llength(lp) <= 56)
465: return ABORT;
466: (VOID) strcpy(fn, curbp->b_fname);
467: cp = fn + strlen(fn);
468: bcopy(&lp->l_text[56], cp, llength(lp) - 56);
469: cp[llength(lp) - 56] = '\0';
470: return lgetc(lp, 2) == 'd';
1.1 deraadt 471: }
1.7 millert 472: #endif /* NO_DIRED */
1.1 deraadt 473:
474: struct filelist {
1.7 millert 475: LIST fl_l;
476: char fl_name[NFILEN + 2];
1.1 deraadt 477: };
478:
1.7 millert 479: /*
480: * these things had better be contiguous, because we're going to refer to the
481: * end of dirbuf + 1 byte
482: */
483: struct dirent dirbuf;
484: char dirdummy;
1.1 deraadt 485:
486: /*
487: * return list of file names that match the name in buf.
488: * System V version. listing is a flag indicating whether the
489: * list is being used for printing a listing rather than
490: * completion. In that case, trailing * and / are put on
491: * for executables and directories. The list is not sorted.
492: */
493:
1.7 millert 494: LIST *
495: make_file_list(buf, listing)
496: char *buf;
497: int listing;
498: {
499: char *dir, *file, *cp;
500: int len, i, preflen;
501: int fp;
502: LIST *last;
503: struct filelist *current;
504: char prefixx[NFILEN + 1];
505: struct stat statbuf;
506: char statname[NFILEN + 2];
507:
508: /*
509: * We need three different strings: dir - the name of the directory
510: * containing what the user typed. Must be a real unix file name,
511: * e.g. no ~user, etc.. Must not end in /. prefix - the portion of
512: * what the user typed that is before the names we are going to find
513: * in the directory. Must have a trailing / if the user typed it.
514: * names from the directory. we open dir, and return prefix
515: * concatenated with names.
516: */
517:
518: /* first we get a directory name we can look up */
519: /*
520: * Names ending in . are potentially odd, because adjustname will
521: * treat foo/.. as a reference to another directory, whereas we are
522: * interested in names starting with ..
523: */
524: len = strlen(buf);
525: if (buf[len - 1] == '.') {
526: buf[len - 1] = 'x';
527: dir = adjustname(buf);
528: buf[len - 1] = '.';
529: } else
530: dir = adjustname(buf);
531: /*
532: * If the user typed a trailing / or the empty string
533: * he wants us to use his file spec as a directory name.
534: */
535: if (buf[0] && buf[strlen(buf) - 1] != '/') {
536: file = strrchr(dir, '/');
537: if (file) {
538: *file = 0;
539: if (*dir == 0)
540: dir = "/";
541: } else {
542: return (NULL);
543: }
1.1 deraadt 544: }
1.7 millert 545: /* Now we get the prefix of the name the user typed. */
546: strcpy(prefixx, buf);
547: cp = strrchr(prefixx, '/');
548: if (cp == NULL)
549: prefixx[0] = 0;
550: else
551: cp[1] = 0;
552:
553: preflen = strlen(prefixx);
554: /* cp is the tail of buf that really needs to be compared */
555: cp = buf + preflen;
556: len = strlen(cp);
557:
558: /*
559: * Now make sure that file names will fit in the buffers allocated.
560: * SV files are fairly short. For BSD, something more general would
561: * be required.
562: */
563: if ((preflen + MAXNAMLEN) > NFILEN)
564: return (NULL);
565: if ((strlen(dir) + MAXNAMLEN) > NFILEN)
566: listing = 0;
567:
568: /* loop over the specified directory, making up the list of files */
569:
570: /*
571: * Note that it is worth our time to filter out names that don't
572: * match, even though our caller is going to do so again, and to
573: * avoid doing the stat if completion is being done, because stat'ing
574: * every file in the directory is relatively expensive.
575: */
1.1 deraadt 576:
1.7 millert 577: fp = open(dir, 0);
578: if (fp < 0) {
579: return (NULL);
580: }
581: last = NULL;
582: /* clear entry after last so we can treat d_name as ASCIZ */
583: dirbuf.d_name[MAXNAMLEN] = 0;
584: while (1) {
585: if (read(fp, &dirbuf, sizeof(struct dirent)) <= 0) {
1.1 deraadt 586: break;
1.7 millert 587: }
588: if (dirbuf.d_ino == 0) /* entry not allocated */
589: continue;
590: for (i = 0; i < len; ++i) {
591: if (cp[i] != dirbuf.d_name[i])
592: break;
593: }
594: if (i < len)
595: continue;
596: current = (struct filelist *) malloc(sizeof(struct filelist));
597: current->fl_l.l_next = last;
598: current->fl_l.l_name = current->fl_name;
599: last = (LIST *) current;
600: strcpy(current->fl_name, prefixx);
601: strcat(current->fl_name, dirbuf.d_name);
602: if (listing) {
603: statbuf.st_mode = 0;
604: strcpy(statname, dir);
605: strcat(statname, "/");
606: strcat(statname, dirbuf.d_name);
607: stat(statname, &statbuf);
608: if (statbuf.st_mode & 040000)
609: strcat(current->fl_name, "/");
610: else if (statbuf.st_mode & 0100)
611: strcat(current->fl_name, "*");
612: }
1.1 deraadt 613: }
1.7 millert 614: close(fp);
1.1 deraadt 615:
1.7 millert 616: return (last);
1.1 deraadt 617: }