Annotation of src/usr.bin/mg/fileio.c, Revision 1.18
1.18 ! art 1: /* $OpenBSD: fileio.c,v 1.17 2001/05/24 10:19:51 art 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.7 millert 401: if ((pid = vfork())) {
402: if (pid == -1)
403: return -1;
1.14 art 404: execl("/bin/cp", "cp", frname, toname, NULL);
1.7 millert 405: _exit(1); /* shouldn't happen */
406: }
1.12 art 407: waitpid(pid, &status, 0);
408: return (WIFEXITED(status) && WEXITSTATUS(status) == 0);
1.7 millert 409: }
410:
411: BUFFER *
412: dired_(dirname)
1.16 mickey 413: char *dirname;
1.7 millert 414: {
1.16 mickey 415: BUFFER *bp;
416: FILE *dirpipe;
417: char line[256];
1.15 mickey 418: int len;
1.7 millert 419:
420: if ((dirname = adjustname(dirname)) == NULL) {
421: ewprintf("Bad directory name");
422: return NULL;
423: }
1.15 mickey 424: /* this should not be done, instead adjustname() should get a flag */
425: len = strlen(dirname);
426: if (dirname[len - 1] != '/') {
427: dirname[len++] = '/';
428: dirname[len] = '\0';
429: }
1.7 millert 430: if ((bp = findbuffer(dirname)) == NULL) {
431: ewprintf("Could not create buffer");
432: return NULL;
433: }
434: if (bclear(bp) != TRUE)
435: return FALSE;
1.15 mickey 436: if (snprintf(line, sizeof(line), "ls -al %s", dirname) >= sizeof(line)){
437: ewprintf("Path too long");
438: return NULL;
439: }
1.7 millert 440: if ((dirpipe = popen(line, "r")) == NULL) {
441: ewprintf("Problem opening pipe to ls");
442: return NULL;
443: }
444: line[0] = line[1] = ' ';
1.15 mickey 445: while (fgets(&line[2], sizeof(line) - 2, dirpipe) != NULL) {
1.7 millert 446: line[strlen(line) - 1] = '\0'; /* remove ^J */
1.13 art 447: (void) addline(bp, line);
1.7 millert 448: }
449: if (pclose(dirpipe) == -1) {
450: ewprintf("Problem closing pipe to ls");
451: return NULL;
452: }
453: bp->b_dotp = lforw(bp->b_linep); /* go to first line */
1.13 art 454: (void) strncpy(bp->b_fname, dirname, NFILEN);
1.7 millert 455: if ((bp->b_modes[0] = name_mode("dired")) == NULL) {
1.17 art 456: bp->b_modes[0] = name_mode("fundamental");
1.7 millert 457: ewprintf("Could not find mode dired");
458: return NULL;
459: }
460: bp->b_nmodes = 0;
461: return bp;
1.1 deraadt 462: }
463:
1.7 millert 464: int
1.1 deraadt 465: d_makename(lp, fn)
1.7 millert 466: LINE *lp;
467: char *fn;
1.1 deraadt 468: {
1.7 millert 469: char *cp;
1.1 deraadt 470:
1.7 millert 471: if (llength(lp) <= 56)
472: return ABORT;
1.13 art 473: (void) strcpy(fn, curbp->b_fname);
1.7 millert 474: cp = fn + strlen(fn);
475: bcopy(&lp->l_text[56], cp, llength(lp) - 56);
476: cp[llength(lp) - 56] = '\0';
477: return lgetc(lp, 2) == 'd';
1.1 deraadt 478: }
1.7 millert 479: #endif /* NO_DIRED */
1.1 deraadt 480:
481: struct filelist {
1.16 mickey 482: LIST fl_l;
483: char fl_name[NFILEN + 2];
1.1 deraadt 484: };
485:
1.7 millert 486: /*
1.1 deraadt 487: * return list of file names that match the name in buf.
488: */
489:
1.7 millert 490: LIST *
1.12 art 491: make_file_list(buf)
1.16 mickey 492: char *buf;
1.7 millert 493: {
1.16 mickey 494: char *dir, *file, *cp;
495: int len, preflen;
496: DIR *dirp;
497: struct dirent *dent;
498: LIST *last;
1.7 millert 499: struct filelist *current;
1.16 mickey 500: char prefixx[NFILEN + 1];
1.7 millert 501:
502: /*
503: * We need three different strings: dir - the name of the directory
504: * containing what the user typed. Must be a real unix file name,
505: * e.g. no ~user, etc.. Must not end in /. prefix - the portion of
506: * what the user typed that is before the names we are going to find
507: * in the directory. Must have a trailing / if the user typed it.
508: * names from the directory. we open dir, and return prefix
509: * concatenated with names.
510: */
511:
512: /* first we get a directory name we can look up */
513: /*
514: * Names ending in . are potentially odd, because adjustname will
515: * treat foo/.. as a reference to another directory, whereas we are
516: * interested in names starting with ..
517: */
518: len = strlen(buf);
519: if (buf[len - 1] == '.') {
520: buf[len - 1] = 'x';
521: dir = adjustname(buf);
522: buf[len - 1] = '.';
523: } else
524: dir = adjustname(buf);
525: /*
526: * If the user typed a trailing / or the empty string
527: * he wants us to use his file spec as a directory name.
528: */
529: if (buf[0] && buf[strlen(buf) - 1] != '/') {
530: file = strrchr(dir, '/');
531: if (file) {
532: *file = 0;
533: if (*dir == 0)
534: dir = "/";
535: } else {
536: return (NULL);
537: }
1.1 deraadt 538: }
1.7 millert 539: /* Now we get the prefix of the name the user typed. */
540: strcpy(prefixx, buf);
541: cp = strrchr(prefixx, '/');
542: if (cp == NULL)
543: prefixx[0] = 0;
544: else
545: cp[1] = 0;
546:
547: preflen = strlen(prefixx);
548: /* cp is the tail of buf that really needs to be compared */
549: cp = buf + preflen;
550: len = strlen(cp);
551:
552: /*
553: * Now make sure that file names will fit in the buffers allocated.
554: * SV files are fairly short. For BSD, something more general would
555: * be required.
556: */
557: if ((preflen + MAXNAMLEN) > NFILEN)
558: return (NULL);
559:
560: /* loop over the specified directory, making up the list of files */
561:
562: /*
563: * Note that it is worth our time to filter out names that don't
564: * match, even though our caller is going to do so again, and to
565: * avoid doing the stat if completion is being done, because stat'ing
566: * every file in the directory is relatively expensive.
567: */
1.1 deraadt 568:
1.11 art 569: dirp = opendir(dir);
570: if (dirp == NULL)
1.7 millert 571: return (NULL);
572: last = NULL;
1.11 art 573:
574: while ((dent = readdir(dirp)) != NULL) {
1.18 ! art 575: int isdir;
! 576:
1.11 art 577: if (dent->d_namlen < len || memcmp(cp, dent->d_name, len) != 0)
578: continue;
579:
1.18 ! art 580: isdir = 0;
! 581: if (dent->d_type == DT_DIR) {
! 582: isdir = 1;
! 583: } else if (dent->d_type == DT_LNK ||
! 584: dent->d_type == DT_UNKNOWN) {
! 585: struct stat statbuf;
! 586: char statname[NFILEN + 2];
! 587:
! 588: statbuf.st_mode = 0;
! 589: if (snprintf(statname, sizeof(statname), "%s/%s",
! 590: dir, dent->d_name) > sizeof(statname) - 1) {
! 591: continue;
! 592: }
! 593: if (stat(statname, &statbuf) < 0)
! 594: continue;
! 595: if (statbuf.st_mode & S_IFDIR)
! 596: isdir = 1;
! 597: }
! 598:
! 599: current = malloc(sizeof(struct filelist));
1.12 art 600: if (current == NULL)
601: break;
1.18 ! art 602:
1.11 art 603: if (snprintf(current->fl_name, sizeof(current->fl_name),
1.18 ! art 604: "%s%s%s", prefixx, dent->d_name, isdir ? "/" : "")
! 605: >= sizeof(current->fl_name)) {
1.11 art 606: free(current);
1.7 millert 607: continue;
608: }
609: current->fl_l.l_next = last;
610: current->fl_l.l_name = current->fl_name;
611: last = (LIST *) current;
1.1 deraadt 612: }
1.11 art 613: closedir(dirp);
1.1 deraadt 614:
1.7 millert 615: return (last);
1.1 deraadt 616: }