Annotation of src/usr.bin/mg/fileio.c, Revision 1.10
1.10 ! niklas 1: /* $OpenBSD$ */
! 2:
1.1 deraadt 3: /*
1.7 millert 4: * POSIX fileio.c
1.1 deraadt 5: */
6: #include "def.h"
7:
1.7 millert 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)
22: char *fn;
23: BUFFER *bp;
24: {
25: struct stat statbuf;
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.1 deraadt 78: (VOID) fclose(ffp);
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.7 millert 128: char *buf;
129: int nbuf;
130: int *nbytes;
1.1 deraadt 131: {
1.7 millert 132: int c;
133: int i;
1.1 deraadt 134:
135: i = 0;
1.7 millert 136: while ((c = getc(ffp)) != EOF && c != '\n') {
1.1 deraadt 137: buf[i++] = c;
1.7 millert 138: if (i >= nbuf)
139: return FIOLONG;
1.1 deraadt 140: }
1.7 millert 141: if (c == EOF && ferror(ffp) != FALSE) {
1.1 deraadt 142: ewprintf("File read error");
143: return FIOERR;
144: }
145: *nbytes = i;
1.7 millert 146: return c == EOF ? FIOEOF : FIOSUC;
1.1 deraadt 147: }
148:
149: #ifndef NO_BACKUP
150: /*
1.9 millert 151: * Make a backup copy of "fname". On Unix the backup has the same
152: * name as the original file, with a "~" on the end; this seems to
153: * be newest of the new-speak. The error handling is all in "file.c".
154: * We do a copy instead of a rename since otherwise another process
155: * with an open fd will get the backup, not the new file. This is
156: * a problem when using mg with things like crontab and vipw.
1.1 deraadt 157: */
1.7 millert 158: int
159: fbackupfile(fn)
160: char *fn;
161: {
1.9 millert 162: struct stat sb;
163: int from, to, serrno;
164: size_t nread;
165: size_t len;
166: char buf[BUFSIZ];
1.7 millert 167: char *nname;
1.1 deraadt 168:
1.9 millert 169: len = strlen(fn);
170: if ((nname = malloc(len + 1 + 1)) == NULL) {
171: ewprintf("Can't get %d bytes", len + 1 + 1);
1.1 deraadt 172: return (ABORT);
173: }
174: (void) strcpy(nname, fn);
1.9 millert 175: (void) strcpy(nname + len, "~");
176:
177: if (stat(fn, &sb) == -1) {
178: ewprintf("Can't stat %s", fn);
179: return (FALSE);
180: }
181:
182: if ((from = open(fn, O_RDONLY)) == -1)
1.1 deraadt 183: return (FALSE);
1.9 millert 184: to = open(nname, O_WRONLY|O_CREAT|O_TRUNC, (sb.st_mode & 0777));
185: if (to == -1) {
186: serrno = errno;
187: close(from);
188: errno = serrno;
189: return (FALSE);
190: }
191: while ((nread = read(from, buf, sizeof(buf))) > 0) {
192: if (write(to, buf, nread) != nread) {
193: nread = -1;
194: break;
195: }
1.1 deraadt 196: }
1.9 millert 197: serrno = errno;
198: close(from);
199: close(to);
200: if (nread == -1)
201: unlink(nname);
1.1 deraadt 202: free(nname);
1.9 millert 203: errno = serrno;
204: return (nread == -1 ? FALSE : TRUE);
1.1 deraadt 205: }
206: #endif
207:
208: /*
209: * The string "fn" is a file name.
210: * Perform any required appending of directory name or case adjustments.
211: * If NO_DIR is not defined, the same file should be refered to even if the
212: * working directory changes.
213: */
214: #ifdef SYMBLINK
215: #include <sys/types.h>
216: #include <sys/stat.h>
217: #ifndef MAXLINK
218: #define MAXLINK 8 /* maximum symbolic links to follow */
219: #endif
220: #endif
221: #include <pwd.h>
222: #ifndef NO_DIR
1.7 millert 223: extern char *wdir;
1.1 deraadt 224: #endif
225:
1.7 millert 226: char *
227: adjustname(fn)
228: char *fn;
1.1 deraadt 229: {
1.7 millert 230: char *cp;
231: static char fnb[NFILEN];
232: struct passwd *pwent;
1.1 deraadt 233: #ifdef SYMBLINK
1.7 millert 234: struct stat statbuf;
235: int i, j;
236: char linkbuf[NFILEN];
1.1 deraadt 237: #endif
238:
1.7 millert 239: switch (*fn) {
240: case '/':
241: cp = fnb;
242: *cp++ = *fn++;
243: break;
1.1 deraadt 244: case '~':
1.7 millert 245: fn++;
1.8 millert 246: cp = getenv("HOME");
247: if (cp != NULL && *cp != '\0' && (*fn == '/' || *fn == '\0')) {
248: (VOID) strcpy(fnb, cp);
1.7 millert 249: cp = fnb + strlen(fnb);
250: if (*fn)
251: fn++;
252: break;
1.1 deraadt 253: } else {
1.7 millert 254: cp = fnb;
255: while (*fn && *fn != '/')
256: *cp++ = *fn++;
257: *cp = '\0';
258: if ((pwent = getpwnam(fnb)) != NULL) {
259: (VOID) strcpy(fnb, pwent->pw_dir);
260: cp = fnb + strlen(fnb);
261: break;
262: } else {
263: fn -= strlen(fnb) + 1;
264: /* can't find ~user, continue to default case */
265: }
1.1 deraadt 266: }
267: default:
268: #ifndef NODIR
1.7 millert 269: strcpy(fnb, wdir);
270: cp = fnb + strlen(fnb);
271: break;
1.1 deraadt 272: #else
1.7 millert 273: return fn; /* punt */
1.1 deraadt 274: #endif
1.7 millert 275: }
276: if (cp != fnb && cp[-1] != '/')
277: *cp++ = '/';
278: while (*fn) {
279: switch (*fn) {
280: case '.':
281: switch (fn[1]) {
282: case '\0':
283: *--cp = '\0';
284: return fnb;
285: case '/':
286: fn += 2;
287: continue;
288: case '.':
289: if (fn[2] != '/' && fn[2] != '\0')
290: break;
1.1 deraadt 291: #ifdef SYMBLINK
1.7 millert 292: cp[-1] = '\0';
293: for (j = MAXLINK; j-- &&
294: lstat(fnb, &statbuf) != -1 &&
295: (statbuf.st_mode & S_IFMT) == S_IFLNK &&
296: (i = readlink(fnb, linkbuf, sizeof linkbuf))
297: != -1;) {
298: if (linkbuf[0] != '/') {
299: --cp;
300: while (cp > fnb && *--cp != '/') {
301: }
302: ++cp;
303: (VOID) strncpy(cp, linkbuf, i);
304: cp += i;
305: } else {
306: (VOID) strncpy(fnb, linkbuf, i);
307: cp = fnb + i;
308: }
309: if (cp[-1] != '/')
310: *cp++ = '\0';
311: else
312: cp[-1] = '\0';
1.1 deraadt 313: }
1.7 millert 314: cp[-1] = '/';
1.1 deraadt 315: #endif
1.7 millert 316: --cp;
317: while (cp > fnb && *--cp != '/') {
318: }
319: ++cp;
320: if (fn[2] == '\0') {
321: *--cp = '\0';
322: return fnb;
323: }
324: fn += 3;
325: continue;
326: default:
327: break;
328: }
329: break;
330: case '/':
331: fn++;
332: continue;
333: default:
334: break;
335: }
336: while (*fn && (*cp++ = *fn++) != '/') {
337: }
338: }
339: if (cp[-1] == '/')
340: --cp;
341: *cp = '\0';
342: return fnb;
1.1 deraadt 343: }
344:
345: #ifndef NO_STARTUP
346: #include <sys/file.h>
347:
348: /*
349: * Find a startup file for the user and return its name. As a service
350: * to other pieces of code that may want to find a startup file (like
351: * the terminal driver in particular), accepts a suffix to be appended
352: * to the startup file name.
353: */
354: char *
355: startupfile(suffix)
1.7 millert 356: char *suffix;
1.1 deraadt 357: {
1.7 millert 358: char *file;
359: static char home[NFILEN];
1.1 deraadt 360:
1.8 millert 361: if ((file = getenv("HOME")) == NULL || *file == '\0')
1.7 millert 362: goto notfound;
363: if (strlen(file) + 7 >= NFILEN - 1)
364: goto notfound;
1.1 deraadt 365: (VOID) strcpy(home, file);
366: (VOID) strcat(home, "/.mg");
367: if (suffix != NULL) {
368: (VOID) strcat(home, "-");
369: (VOID) strcat(home, suffix);
370: }
1.7 millert 371: if (access(home, F_OK) == 0)
372: return home;
1.1 deraadt 373:
374: notfound:
375: #ifdef STARTUPFILE
376: file = STARTUPFILE;
377: if (suffix != NULL) {
378: (VOID) strcpy(home, file);
379: (VOID) strcat(home, "-");
380: (VOID) strcat(home, suffix);
381: file = home;
382: }
1.7 millert 383: if (access(file, F_OK) == 0)
384: return file;
1.1 deraadt 385: #endif
386:
387: return NULL;
388: }
389: #endif
390:
391: #ifndef NO_DIRED
1.4 millert 392: #include <sys/wait.h>
1.1 deraadt 393: #include "kbd.h"
394:
1.7 millert 395: int
1.1 deraadt 396: copy(frname, toname)
1.7 millert 397: char *frname;
398: char *toname;
1.1 deraadt 399: {
1.7 millert 400: pid_t pid;
401: int status;
1.1 deraadt 402:
1.7 millert 403: if ((pid = vfork())) {
404: if (pid == -1)
405: return -1;
406: execl("/bin/cp", "cp", frname, toname, (char *) NULL);
407: _exit(1); /* shouldn't happen */
408: }
409: while (wait(&status) != pid);
410: return status == 0;
411: }
412:
413: BUFFER *
414: dired_(dirname)
415: char *dirname;
416: {
417: BUFFER *bp;
418: FILE *dirpipe;
419: char line[256];
420:
421: if ((dirname = adjustname(dirname)) == NULL) {
422: ewprintf("Bad directory name");
423: return NULL;
424: }
425: if (dirname[strlen(dirname) - 1] != '/')
426: (VOID) strcat(dirname, "/");
427: if ((bp = findbuffer(dirname)) == NULL) {
428: ewprintf("Could not create buffer");
429: return NULL;
430: }
431: if (bclear(bp) != TRUE)
432: return FALSE;
433: (VOID) strcpy(line, "ls -al ");
434: (VOID) strcpy(&line[7], dirname);
435: if ((dirpipe = popen(line, "r")) == NULL) {
436: ewprintf("Problem opening pipe to ls");
437: return NULL;
438: }
439: line[0] = line[1] = ' ';
440: while (fgets(&line[2], 254, dirpipe) != NULL) {
441: line[strlen(line) - 1] = '\0'; /* remove ^J */
442: (VOID) addline(bp, line);
443: }
444: if (pclose(dirpipe) == -1) {
445: ewprintf("Problem closing pipe to ls");
446: return NULL;
447: }
448: bp->b_dotp = lforw(bp->b_linep); /* go to first line */
449: (VOID) strncpy(bp->b_fname, dirname, NFILEN);
450: if ((bp->b_modes[0] = name_mode("dired")) == NULL) {
451: bp->b_modes[0] = &map_table[0];
452: ewprintf("Could not find mode dired");
453: return NULL;
454: }
455: bp->b_nmodes = 0;
456: return bp;
1.1 deraadt 457: }
458:
1.7 millert 459: int
1.1 deraadt 460: d_makename(lp, fn)
1.7 millert 461: LINE *lp;
462: char *fn;
1.1 deraadt 463: {
1.7 millert 464: char *cp;
1.1 deraadt 465:
1.7 millert 466: if (llength(lp) <= 56)
467: return ABORT;
468: (VOID) strcpy(fn, curbp->b_fname);
469: cp = fn + strlen(fn);
470: bcopy(&lp->l_text[56], cp, llength(lp) - 56);
471: cp[llength(lp) - 56] = '\0';
472: return lgetc(lp, 2) == 'd';
1.1 deraadt 473: }
1.7 millert 474: #endif /* NO_DIRED */
1.1 deraadt 475:
476: struct filelist {
1.7 millert 477: LIST fl_l;
478: char fl_name[NFILEN + 2];
1.1 deraadt 479: };
480:
1.7 millert 481: /*
482: * these things had better be contiguous, because we're going to refer to the
483: * end of dirbuf + 1 byte
484: */
485: struct dirent dirbuf;
486: char dirdummy;
1.1 deraadt 487:
488: /*
489: * return list of file names that match the name in buf.
490: * System V version. listing is a flag indicating whether the
491: * list is being used for printing a listing rather than
492: * completion. In that case, trailing * and / are put on
493: * for executables and directories. The list is not sorted.
494: */
495:
1.7 millert 496: LIST *
497: make_file_list(buf, listing)
498: char *buf;
499: int listing;
500: {
501: char *dir, *file, *cp;
502: int len, i, preflen;
503: int fp;
504: LIST *last;
505: struct filelist *current;
506: char prefixx[NFILEN + 1];
507: struct stat statbuf;
508: char statname[NFILEN + 2];
509:
510: /*
511: * We need three different strings: dir - the name of the directory
512: * containing what the user typed. Must be a real unix file name,
513: * e.g. no ~user, etc.. Must not end in /. prefix - the portion of
514: * what the user typed that is before the names we are going to find
515: * in the directory. Must have a trailing / if the user typed it.
516: * names from the directory. we open dir, and return prefix
517: * concatenated with names.
518: */
519:
520: /* first we get a directory name we can look up */
521: /*
522: * Names ending in . are potentially odd, because adjustname will
523: * treat foo/.. as a reference to another directory, whereas we are
524: * interested in names starting with ..
525: */
526: len = strlen(buf);
527: if (buf[len - 1] == '.') {
528: buf[len - 1] = 'x';
529: dir = adjustname(buf);
530: buf[len - 1] = '.';
531: } else
532: dir = adjustname(buf);
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 = "/";
543: } else {
544: return (NULL);
545: }
1.1 deraadt 546: }
1.7 millert 547: /* Now we get the prefix of the name the user typed. */
548: strcpy(prefixx, buf);
549: cp = strrchr(prefixx, '/');
550: if (cp == NULL)
551: prefixx[0] = 0;
552: else
553: cp[1] = 0;
554:
555: preflen = strlen(prefixx);
556: /* cp is the tail of buf that really needs to be compared */
557: cp = buf + preflen;
558: len = strlen(cp);
559:
560: /*
561: * Now make sure that file names will fit in the buffers allocated.
562: * SV files are fairly short. For BSD, something more general would
563: * be required.
564: */
565: if ((preflen + MAXNAMLEN) > NFILEN)
566: return (NULL);
567: if ((strlen(dir) + MAXNAMLEN) > NFILEN)
568: listing = 0;
569:
570: /* loop over the specified directory, making up the list of files */
571:
572: /*
573: * Note that it is worth our time to filter out names that don't
574: * match, even though our caller is going to do so again, and to
575: * avoid doing the stat if completion is being done, because stat'ing
576: * every file in the directory is relatively expensive.
577: */
1.1 deraadt 578:
1.7 millert 579: fp = open(dir, 0);
580: if (fp < 0) {
581: return (NULL);
582: }
583: last = NULL;
584: /* clear entry after last so we can treat d_name as ASCIZ */
585: dirbuf.d_name[MAXNAMLEN] = 0;
586: while (1) {
587: if (read(fp, &dirbuf, sizeof(struct dirent)) <= 0) {
1.1 deraadt 588: break;
1.7 millert 589: }
590: if (dirbuf.d_ino == 0) /* entry not allocated */
591: continue;
592: for (i = 0; i < len; ++i) {
593: if (cp[i] != dirbuf.d_name[i])
594: break;
595: }
596: if (i < len)
597: continue;
598: current = (struct filelist *) malloc(sizeof(struct filelist));
599: current->fl_l.l_next = last;
600: current->fl_l.l_name = current->fl_name;
601: last = (LIST *) current;
602: strcpy(current->fl_name, prefixx);
603: strcat(current->fl_name, dirbuf.d_name);
604: if (listing) {
605: statbuf.st_mode = 0;
606: strcpy(statname, dir);
607: strcat(statname, "/");
608: strcat(statname, dirbuf.d_name);
609: stat(statname, &statbuf);
610: if (statbuf.st_mode & 040000)
611: strcat(current->fl_name, "/");
612: else if (statbuf.st_mode & 0100)
613: strcat(current->fl_name, "*");
614: }
1.1 deraadt 615: }
1.7 millert 616: close(fp);
1.1 deraadt 617:
1.7 millert 618: return (last);
1.1 deraadt 619: }