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