Annotation of src/usr.bin/mg/fileio.c, Revision 1.12
1.12 ! art 1: /* $OpenBSD: fileio.c,v 1.11 2001/05/01 13:26:59 art 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.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: /*
347: * Find a startup file for the user and return its name. As a service
348: * to other pieces of code that may want to find a startup file (like
349: * the terminal driver in particular), accepts a suffix to be appended
350: * to the startup file name.
351: */
352: char *
353: startupfile(suffix)
1.7 millert 354: char *suffix;
1.1 deraadt 355: {
1.12 ! art 356: static char file[NFILEN];
! 357: char *home;
! 358:
! 359: if ((home = getenv("HOME")) == NULL || *home == '\0')
! 360: goto nohome;
1.1 deraadt 361:
1.12 ! art 362: if (suffix == NULL) {
! 363: if (snprintf(file, sizeof(file), "%s/.mg", home)
! 364: >= sizeof(file))
! 365: return NULL;
! 366: } else {
! 367: if (snprintf(file, sizeof(file), "%s/.mg-%s", home, suffix)
! 368: >= sizeof(file))
! 369: return NULL;
1.1 deraadt 370: }
1.12 ! art 371:
! 372: if (access(file, R_OK) == 0)
! 373: return file;
! 374: nohome:
! 375: #ifdef STARTUPFILE
! 376: if (suffix == NULL)
! 377: if (snprintf(file, sizeof(file), "%s", STARTUPFILE)
! 378: >= sizeof(file))
! 379: return NULL;
! 380: } else {
! 381: if (snprintf(file, sizeof(file), "%s%s", STARTUPFILE, suffix)
! 382: >= sizeof(file))
! 383: return NULL;
! 384: }
! 385:
! 386: if (access(file, R_OK) == 0)
1.7 millert 387: return file;
1.1 deraadt 388: #endif
389: return NULL;
390: }
391: #endif
392:
393: #ifndef NO_DIRED
1.4 millert 394: #include <sys/wait.h>
1.1 deraadt 395: #include "kbd.h"
396:
1.7 millert 397: int
1.1 deraadt 398: copy(frname, toname)
1.7 millert 399: char *frname;
400: char *toname;
1.1 deraadt 401: {
1.7 millert 402: pid_t pid;
403: int status;
1.1 deraadt 404:
1.7 millert 405: if ((pid = vfork())) {
406: if (pid == -1)
407: return -1;
408: execl("/bin/cp", "cp", frname, toname, (char *) NULL);
409: _exit(1); /* shouldn't happen */
410: }
1.12 ! art 411: waitpid(pid, &status, 0);
! 412: return (WIFEXITED(status) && WEXITSTATUS(status) == 0);
1.7 millert 413: }
414:
415: BUFFER *
416: dired_(dirname)
417: char *dirname;
418: {
419: BUFFER *bp;
420: FILE *dirpipe;
421: char line[256];
422:
423: if ((dirname = adjustname(dirname)) == NULL) {
424: ewprintf("Bad directory name");
425: return NULL;
426: }
427: if (dirname[strlen(dirname) - 1] != '/')
428: (VOID) strcat(dirname, "/");
429: if ((bp = findbuffer(dirname)) == NULL) {
430: ewprintf("Could not create buffer");
431: return NULL;
432: }
433: if (bclear(bp) != TRUE)
434: return FALSE;
435: (VOID) strcpy(line, "ls -al ");
436: (VOID) strcpy(&line[7], dirname);
437: if ((dirpipe = popen(line, "r")) == NULL) {
438: ewprintf("Problem opening pipe to ls");
439: return NULL;
440: }
441: line[0] = line[1] = ' ';
442: while (fgets(&line[2], 254, dirpipe) != NULL) {
443: line[strlen(line) - 1] = '\0'; /* remove ^J */
444: (VOID) addline(bp, line);
445: }
446: if (pclose(dirpipe) == -1) {
447: ewprintf("Problem closing pipe to ls");
448: return NULL;
449: }
450: bp->b_dotp = lforw(bp->b_linep); /* go to first line */
451: (VOID) strncpy(bp->b_fname, dirname, NFILEN);
452: if ((bp->b_modes[0] = name_mode("dired")) == NULL) {
453: bp->b_modes[0] = &map_table[0];
454: ewprintf("Could not find mode dired");
455: return NULL;
456: }
457: bp->b_nmodes = 0;
458: return bp;
1.1 deraadt 459: }
460:
1.7 millert 461: int
1.1 deraadt 462: d_makename(lp, fn)
1.7 millert 463: LINE *lp;
464: char *fn;
1.1 deraadt 465: {
1.7 millert 466: char *cp;
1.1 deraadt 467:
1.7 millert 468: if (llength(lp) <= 56)
469: return ABORT;
470: (VOID) strcpy(fn, curbp->b_fname);
471: cp = fn + strlen(fn);
472: bcopy(&lp->l_text[56], cp, llength(lp) - 56);
473: cp[llength(lp) - 56] = '\0';
474: return lgetc(lp, 2) == 'd';
1.1 deraadt 475: }
1.7 millert 476: #endif /* NO_DIRED */
1.1 deraadt 477:
478: struct filelist {
1.7 millert 479: LIST fl_l;
480: char fl_name[NFILEN + 2];
1.1 deraadt 481: };
482:
1.7 millert 483: /*
1.1 deraadt 484: * return list of file names that match the name in buf.
485: */
486:
1.7 millert 487: LIST *
1.12 ! art 488: make_file_list(buf)
1.7 millert 489: char *buf;
490: {
491: char *dir, *file, *cp;
1.11 art 492: int len, preflen;
493: DIR *dirp;
494: struct dirent *dent;
1.7 millert 495: LIST *last;
496: struct filelist *current;
497: char prefixx[NFILEN + 1];
498: struct stat statbuf;
499: char statname[NFILEN + 2];
500:
501: /*
502: * We need three different strings: dir - the name of the directory
503: * containing what the user typed. Must be a real unix file name,
504: * e.g. no ~user, etc.. Must not end in /. prefix - the portion of
505: * what the user typed that is before the names we are going to find
506: * in the directory. Must have a trailing / if the user typed it.
507: * names from the directory. we open dir, and return prefix
508: * concatenated with names.
509: */
510:
511: /* first we get a directory name we can look up */
512: /*
513: * Names ending in . are potentially odd, because adjustname will
514: * treat foo/.. as a reference to another directory, whereas we are
515: * interested in names starting with ..
516: */
517: len = strlen(buf);
518: if (buf[len - 1] == '.') {
519: buf[len - 1] = 'x';
520: dir = adjustname(buf);
521: buf[len - 1] = '.';
522: } else
523: dir = adjustname(buf);
524: /*
525: * If the user typed a trailing / or the empty string
526: * he wants us to use his file spec as a directory name.
527: */
528: if (buf[0] && buf[strlen(buf) - 1] != '/') {
529: file = strrchr(dir, '/');
530: if (file) {
531: *file = 0;
532: if (*dir == 0)
533: dir = "/";
534: } else {
535: return (NULL);
536: }
1.1 deraadt 537: }
1.7 millert 538: /* Now we get the prefix of the name the user typed. */
539: strcpy(prefixx, buf);
540: cp = strrchr(prefixx, '/');
541: if (cp == NULL)
542: prefixx[0] = 0;
543: else
544: cp[1] = 0;
545:
546: preflen = strlen(prefixx);
547: /* cp is the tail of buf that really needs to be compared */
548: cp = buf + preflen;
549: len = strlen(cp);
550:
551: /*
552: * Now make sure that file names will fit in the buffers allocated.
553: * SV files are fairly short. For BSD, something more general would
554: * be required.
555: */
556: if ((preflen + MAXNAMLEN) > NFILEN)
557: return (NULL);
558:
559: /* loop over the specified directory, making up the list of files */
560:
561: /*
562: * Note that it is worth our time to filter out names that don't
563: * match, even though our caller is going to do so again, and to
564: * avoid doing the stat if completion is being done, because stat'ing
565: * every file in the directory is relatively expensive.
566: */
1.1 deraadt 567:
1.11 art 568: dirp = opendir(dir);
569: if (dirp == NULL)
1.7 millert 570: return (NULL);
571: last = NULL;
1.11 art 572:
573: while ((dent = readdir(dirp)) != NULL) {
574: if (dent->d_namlen < len || memcmp(cp, dent->d_name, len) != 0)
575: continue;
576:
577: current = (struct filelist *) malloc(sizeof(struct filelist));
1.12 ! art 578: if (current == NULL)
! 579: break;
1.11 art 580: if (snprintf(current->fl_name, sizeof(current->fl_name),
581: "%s%s", prefixx, dent->d_name) > sizeof(current->fl_name)) {
582: free(current);
1.7 millert 583: continue;
584: }
585: current->fl_l.l_next = last;
586: current->fl_l.l_name = current->fl_name;
587: last = (LIST *) current;
1.12 ! art 588: if (dent->d_type == DT_DIR) {
! 589: strcat(current->fl_name, "/");
! 590: continue;
! 591: } else if (dent->d_type != DT_UNKNOWN)
! 592: continue;
1.11 art 593:
1.12 ! art 594: statbuf.st_mode = 0;
! 595: if (snprintf(statname, sizeof(statname), "%s/%s",
! 596: dir, dent->d_name) > sizeof(statname) - 1) {
! 597: continue;
1.7 millert 598: }
1.12 ! art 599: if (stat(statname, &statbuf) < 0)
! 600: continue;
! 601: if (statbuf.st_mode & 040000)
! 602: strcat(current->fl_name, "/");
1.1 deraadt 603: }
1.11 art 604: closedir(dirp);
1.1 deraadt 605:
1.7 millert 606: return (last);
1.1 deraadt 607: }