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