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