Annotation of src/usr.bin/mg/fileio.c, Revision 1.35
1.35 ! vincent 1: /* $OpenBSD: fileio.c,v 1.34 2002/08/22 23:28:19 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>
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.34 deraadt 56: }
1.24 deraadt 57:
58: if ((ffp = fdopen(fd, "w")) == NULL) {
59: ewprintf("Cannot open file for writing : %s", strerror(errno));
60: close(fd);
1.34 deraadt 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.35 ! vincent 171: if (stat(fn, &sb) == -1) {
! 172: ewprintf("Can't stat %s : %s", fn, strerror(errno));
! 173: return (FALSE);
! 174: }
! 175:
1.21 vincent 176: if (asprintf(&nname, "%s~", fn) == -1) {
177: ewprintf("Can't allocate temp file name : %s",
178: strerror(errno));
1.1 deraadt 179: return (ABORT);
1.9 millert 180: }
181:
1.21 vincent 182: if ((from = open(fn, O_RDONLY)) == -1) {
183: free(nname);
1.1 deraadt 184: return (FALSE);
1.21 vincent 185: }
1.9 millert 186: to = open(nname, O_WRONLY|O_CREAT|O_TRUNC, (sb.st_mode & 0777));
187: if (to == -1) {
188: serrno = errno;
189: close(from);
1.21 vincent 190: free(nname);
1.9 millert 191: errno = serrno;
192: return (FALSE);
193: }
194: while ((nread = read(from, buf, sizeof(buf))) > 0) {
195: if (write(to, buf, nread) != nread) {
1.30 vincent 196: nread = -1;
197: break;
1.9 millert 198: }
1.1 deraadt 199: }
1.9 millert 200: serrno = errno;
201: close(from);
202: close(to);
1.21 vincent 203: if (nread == -1) {
204: if (unlink(nname) == -1)
205: ewprintf("Can't unlink temp : %s", strerror(errno));
206: }
1.1 deraadt 207: free(nname);
1.9 millert 208: errno = serrno;
1.22 deraadt 209:
1.9 millert 210: return (nread == -1 ? FALSE : TRUE);
1.1 deraadt 211: }
212: #endif
213:
214: /*
215: * The string "fn" is a file name.
216: * Perform any required appending of directory name or case adjustments.
217: * If NO_DIR is not defined, the same file should be refered to even if the
218: * working directory changes.
219: */
220: #ifdef SYMBLINK
221: #include <sys/types.h>
222: #include <sys/stat.h>
223: #ifndef MAXLINK
224: #define MAXLINK 8 /* maximum symbolic links to follow */
225: #endif
226: #endif
227: #include <pwd.h>
228: #ifndef NO_DIR
1.16 mickey 229: extern char *wdir;
1.1 deraadt 230: #endif
231:
1.7 millert 232: char *
1.25 vincent 233: adjustname(const char *fn)
1.1 deraadt 234: {
1.33 vincent 235: static char fnb[MAXPATHLEN];
236: const char *cp;
237: char user[LOGIN_NAME_MAX + 1], path[MAXPATHLEN];
238: int len;
239:
240: path[0] = '\0';
241: /* first handle tilde expansion */
242: if (fn[0] == '~') {
243: struct passwd *pw;
244:
245: cp = strchr(fn, '/');
246: if (cp == NULL)
247: cp = fn + strlen(fn); /* point to the NUL byte */
1.1 deraadt 248:
1.33 vincent 249: if ((cp - &fn[1]) > LOGIN_NAME_MAX) {
250: ewprintf("login name too long");
251: return (NULL);
252: }
253: if (cp == &fn[1]) /* ~/ */
254: strlcpy(user, getlogin(), sizeof user);
255: else
256: strlcpy(user, &fn[1], cp - &fn[1] + 1);
257: pw = getpwnam(user);
258: if (pw == NULL) {
259: ewprintf("unknown user %s", user);
260: return (NULL);
261: }
262: strlcpy(path, pw->pw_dir, sizeof path - 1);
263: len = strlen(path);
264: if (path[len] != '/') {
265: path[len] = '/';
266: path[len + 1] = '\0';
1.1 deraadt 267: }
1.33 vincent 268: fn = cp;
269: if (*fn == '/')
1.7 millert 270: fn++;
271: }
1.33 vincent 272: strlcat(path, fn, sizeof path);
273:
274: return (realpath(path, fnb));
1.1 deraadt 275: }
276:
277: #ifndef NO_STARTUP
278: /*
279: * Find a startup file for the user and return its name. As a service
280: * to other pieces of code that may want to find a startup file (like
281: * the terminal driver in particular), accepts a suffix to be appended
282: * to the startup file name.
283: */
284: char *
1.27 millert 285: startupfile(char *suffix)
1.1 deraadt 286: {
1.16 mickey 287: static char file[NFILEN];
288: char *home;
1.12 art 289:
290: if ((home = getenv("HOME")) == NULL || *home == '\0')
291: goto nohome;
1.1 deraadt 292:
1.12 art 293: if (suffix == NULL) {
294: if (snprintf(file, sizeof(file), "%s/.mg", home)
1.32 vincent 295: >= sizeof(file))
296: return (NULL);
1.12 art 297: } else {
298: if (snprintf(file, sizeof(file), "%s/.mg-%s", home, suffix)
1.32 vincent 299: >= sizeof(file))
300: return (NULL);
1.1 deraadt 301: }
1.12 art 302:
303: if (access(file, R_OK) == 0)
1.32 vincent 304: return (file);
1.12 art 305: nohome:
306: #ifdef STARTUPFILE
1.27 millert 307: if (suffix == NULL) {
1.12 art 308: if (snprintf(file, sizeof(file), "%s", STARTUPFILE)
1.32 vincent 309: >= sizeof(file))
310: return (NULL);
1.12 art 311: } else {
312: if (snprintf(file, sizeof(file), "%s%s", STARTUPFILE, suffix)
1.32 vincent 313: >= sizeof(file))
314: return (NULL);
1.12 art 315: }
316:
317: if (access(file, R_OK) == 0)
1.32 vincent 318: return (file);
1.1 deraadt 319: #endif
1.32 vincent 320: return (NULL);
1.1 deraadt 321: }
322: #endif
323:
324: #ifndef NO_DIRED
1.4 millert 325: #include <sys/wait.h>
1.1 deraadt 326: #include "kbd.h"
327:
1.7 millert 328: int
1.27 millert 329: copy(char *frname, char *toname)
1.1 deraadt 330: {
1.30 vincent 331: int ifd, ofd, n;
332: char buf[BUFSIZ];
333: mode_t mode = DEFFILEMODE; /* XXX?? */
334: struct stat orig;
335:
336: if ((ifd = open(frname, O_RDONLY)) == -1)
337: return (FALSE);
338: if (fstat(ifd, &orig) == -1) {
339: ewprintf("fstat: %s", strerror(errno));
340: close(ifd);
341: return (FALSE);
342: }
343:
344: if ((ofd = open(toname, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) {
345: close(ifd);
346: return (FALSE);
347: }
348: while ((n = read(ifd, buf, sizeof buf)) > 0) {
349: if (write(ofd, buf, n) != n) {
350: ewprintf("write error : %s", strerror(errno));
351: break;
352: }
353: }
354: if (fchmod(ofd, orig.st_mode) == -1)
355: ewprintf("Cannot set original mode : %s", strerror(errno));
1.1 deraadt 356:
1.30 vincent 357: if (n == -1) {
358: ewprintf("Read error : %s", strerror(errno));
359: close(ifd);
360: close(ofd);
361: return (FALSE);
1.7 millert 362: }
1.30 vincent 363: /*
1.32 vincent 364: * It is "normal" for this to fail since we can't guarantee that
1.30 vincent 365: * we will be running as root
366: */
367: if (fchown(ofd, orig.st_uid, orig.st_gid) && errno != EPERM)
368: ewprintf("Cannot set owner : %s", strerror(errno));
369:
370: (void) close(ifd);
371: (void) close(ofd);
372:
373: return (TRUE);
1.7 millert 374: }
375:
1.26 vincent 376: /*
377: * dirname needs to have enough place to store an additional '/'.
378: */
1.7 millert 379: BUFFER *
1.27 millert 380: dired_(char *dirname)
1.7 millert 381: {
1.16 mickey 382: BUFFER *bp;
383: FILE *dirpipe;
384: char line[256];
1.15 mickey 385: int len;
1.7 millert 386:
387: if ((dirname = adjustname(dirname)) == NULL) {
388: ewprintf("Bad directory name");
389: return NULL;
390: }
1.15 mickey 391: /* this should not be done, instead adjustname() should get a flag */
392: len = strlen(dirname);
393: if (dirname[len - 1] != '/') {
394: dirname[len++] = '/';
395: dirname[len] = '\0';
396: }
1.7 millert 397: if ((bp = findbuffer(dirname)) == NULL) {
398: ewprintf("Could not create buffer");
399: return NULL;
400: }
401: if (bclear(bp) != TRUE)
402: return FALSE;
1.26 vincent 403: bp->b_flag |= BFREADONLY;
1.15 mickey 404: if (snprintf(line, sizeof(line), "ls -al %s", dirname) >= sizeof(line)){
405: ewprintf("Path too long");
406: return NULL;
407: }
1.7 millert 408: if ((dirpipe = popen(line, "r")) == NULL) {
409: ewprintf("Problem opening pipe to ls");
410: return NULL;
411: }
412: line[0] = line[1] = ' ';
1.15 mickey 413: while (fgets(&line[2], sizeof(line) - 2, dirpipe) != NULL) {
1.7 millert 414: line[strlen(line) - 1] = '\0'; /* remove ^J */
1.13 art 415: (void) addline(bp, line);
1.7 millert 416: }
417: if (pclose(dirpipe) == -1) {
418: ewprintf("Problem closing pipe to ls");
419: return NULL;
420: }
421: bp->b_dotp = lforw(bp->b_linep); /* go to first line */
1.21 vincent 422: (void) strlcpy(bp->b_fname, dirname, sizeof bp->b_fname);
1.7 millert 423: if ((bp->b_modes[0] = name_mode("dired")) == NULL) {
1.17 art 424: bp->b_modes[0] = name_mode("fundamental");
1.7 millert 425: ewprintf("Could not find mode dired");
426: return NULL;
427: }
428: bp->b_nmodes = 0;
429: return bp;
1.1 deraadt 430: }
431:
1.23 vincent 432: #define NAME_FIELD 8
433:
1.7 millert 434: int
1.23 vincent 435: d_makename(LINE *lp, char *fn, int len)
1.1 deraadt 436: {
1.23 vincent 437: int i;
438: char *p, *np;
1.31 vincent 439:
1.23 vincent 440: strlcpy(fn, curbp->b_fname, len);
441: p = lp->l_text;
442: for (i = 0; i < NAME_FIELD; i++) {
443: np = strpbrk(p, "\t ");
444: if (np == NULL)
445: return ABORT;
446: p = np + 1;
447: while (*p != '\0' && strchr("\t ", *p))
448: p++;
449: }
450: strlcat(fn, p, len);
1.7 millert 451: return lgetc(lp, 2) == 'd';
1.1 deraadt 452: }
1.7 millert 453: #endif /* NO_DIRED */
1.1 deraadt 454:
455: struct filelist {
1.16 mickey 456: LIST fl_l;
457: char fl_name[NFILEN + 2];
1.1 deraadt 458: };
459:
1.7 millert 460: /*
1.1 deraadt 461: * return list of file names that match the name in buf.
462: */
463:
1.7 millert 464: LIST *
1.25 vincent 465: make_file_list(char *buf)
1.7 millert 466: {
1.16 mickey 467: char *dir, *file, *cp;
468: int len, preflen;
469: DIR *dirp;
470: struct dirent *dent;
471: LIST *last;
1.7 millert 472: struct filelist *current;
1.16 mickey 473: char prefixx[NFILEN + 1];
1.7 millert 474:
475: /*
476: * We need three different strings: dir - the name of the directory
477: * containing what the user typed. Must be a real unix file name,
478: * e.g. no ~user, etc.. Must not end in /. prefix - the portion of
479: * what the user typed that is before the names we are going to find
480: * in the directory. Must have a trailing / if the user typed it.
481: * names from the directory. we open dir, and return prefix
482: * concatenated with names.
483: */
484:
485: /* first we get a directory name we can look up */
486: /*
487: * Names ending in . are potentially odd, because adjustname will
488: * treat foo/.. as a reference to another directory, whereas we are
489: * interested in names starting with ..
490: */
491: len = strlen(buf);
492: if (buf[len - 1] == '.') {
493: buf[len - 1] = 'x';
494: dir = adjustname(buf);
495: buf[len - 1] = '.';
496: } else
497: dir = adjustname(buf);
1.33 vincent 498: if (dir == NULL)
499: return (FALSE);
1.7 millert 500: /*
501: * If the user typed a trailing / or the empty string
502: * he wants us to use his file spec as a directory name.
503: */
504: if (buf[0] && buf[strlen(buf) - 1] != '/') {
505: file = strrchr(dir, '/');
506: if (file) {
507: *file = 0;
508: if (*dir == 0)
509: dir = "/";
510: } else {
511: return (NULL);
512: }
1.1 deraadt 513: }
1.7 millert 514: /* Now we get the prefix of the name the user typed. */
1.21 vincent 515: strlcpy(prefixx, buf, sizeof prefixx);
1.7 millert 516: cp = strrchr(prefixx, '/');
517: if (cp == NULL)
518: prefixx[0] = 0;
519: else
520: cp[1] = 0;
521:
522: preflen = strlen(prefixx);
523: /* cp is the tail of buf that really needs to be compared */
524: cp = buf + preflen;
525: len = strlen(cp);
526:
527: /*
528: * Now make sure that file names will fit in the buffers allocated.
529: * SV files are fairly short. For BSD, something more general would
530: * be required.
531: */
532: if ((preflen + MAXNAMLEN) > NFILEN)
533: return (NULL);
534:
535: /* loop over the specified directory, making up the list of files */
536:
537: /*
538: * Note that it is worth our time to filter out names that don't
539: * match, even though our caller is going to do so again, and to
540: * avoid doing the stat if completion is being done, because stat'ing
541: * every file in the directory is relatively expensive.
542: */
1.1 deraadt 543:
1.11 art 544: dirp = opendir(dir);
545: if (dirp == NULL)
1.7 millert 546: return (NULL);
547: last = NULL;
1.11 art 548:
549: while ((dent = readdir(dirp)) != NULL) {
1.18 art 550: int isdir;
551:
1.11 art 552: if (dent->d_namlen < len || memcmp(cp, dent->d_name, len) != 0)
553: continue;
554:
1.18 art 555: isdir = 0;
556: if (dent->d_type == DT_DIR) {
557: isdir = 1;
558: } else if (dent->d_type == DT_LNK ||
559: dent->d_type == DT_UNKNOWN) {
560: struct stat statbuf;
561: char statname[NFILEN + 2];
562:
563: statbuf.st_mode = 0;
564: if (snprintf(statname, sizeof(statname), "%s/%s",
565: dir, dent->d_name) > sizeof(statname) - 1) {
566: continue;
567: }
568: if (stat(statname, &statbuf) < 0)
569: continue;
570: if (statbuf.st_mode & S_IFDIR)
571: isdir = 1;
572: }
573:
574: current = malloc(sizeof(struct filelist));
1.12 art 575: if (current == NULL)
576: break;
1.18 art 577:
1.11 art 578: if (snprintf(current->fl_name, sizeof(current->fl_name),
1.18 art 579: "%s%s%s", prefixx, dent->d_name, isdir ? "/" : "")
580: >= sizeof(current->fl_name)) {
1.11 art 581: free(current);
1.7 millert 582: continue;
583: }
584: current->fl_l.l_next = last;
585: current->fl_l.l_name = current->fl_name;
586: last = (LIST *) current;
1.1 deraadt 587: }
1.11 art 588: closedir(dirp);
1.1 deraadt 589:
1.7 millert 590: return (last);
1.1 deraadt 591: }