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