Annotation of src/usr.bin/mg/fileio.c, Revision 1.2
1.1 deraadt 1: /*
2: * sys V fileio.c
3: */
4: #include "def.h"
5:
6: static FILE *ffp;
7: extern char *getenv();
8: char *adjustname();
9:
10: #include <sys/types.h>
11: #include <sys/stat.h>
12: #include <sys/dir.h>
13:
14: /*
15: * Open a file for reading.
16: */
17: ffropen(fn, bp) char *fn; BUFFER *bp; {
18: struct stat statbuf;
19:
20: if ((ffp=fopen(fn, "r")) == NULL)
21: return (FIOFNF);
22: if (bp && fstat(fileno(ffp), &statbuf) == 0) {
23: /* set highorder bit to make sure this isn't all zero */
24: bp->b_fi.fi_mode = statbuf.st_mode | 0x8000;
25: bp->b_fi.fi_uid = statbuf.st_uid;
26: bp->b_fi.fi_gid = statbuf.st_gid;
27: }
28: return (FIOSUC);
29: }
30:
31: /*
32: * Open a file for writing.
33: * Return TRUE if all is well, and
34: * FALSE on error (cannot create).
35: */
36: ffwopen(fn, bp) char *fn; BUFFER *bp; {
37: if ((ffp=fopen(fn, "w")) == NULL) {
38: ewprintf("Cannot open file for writing");
39: return (FIOERR);
40: }
41: /*
42: * If we have file information, use it. We don't bother
43: * to check for errors, because there's no a lot we can do
44: * about it. Certainly trying to change ownership will fail
45: * if we aren' root. That's probably OK. If we don't have
46: * info, no need to get it, since any future writes will do
47: * the same thing.
48: */
49: if (bp && bp->b_fi.fi_mode) {
50: chmod(fn, bp->b_fi.fi_mode & 07777);
51: chown(fn, bp->b_fi.fi_uid, bp->b_fi.fi_gid);
52: }
53: return (FIOSUC);
54: }
55:
56: /*
57: * Close a file.
58: * Should look at the status.
59: */
60: ffclose() {
61: (VOID) fclose(ffp);
62: return (FIOSUC);
63: }
64:
65: /*
66: * Write a buffer to the already
67: * opened file. bp points to the
68: * buffer. Return the status.
69: * Check only at the newline and
70: * end of buffer.
71: */
72: ffputbuf(bp)
73: BUFFER *bp;
74: {
75: register char *cp;
76: register char *cpend;
77: register LINE *lp;
78: register LINE *lpend;
79:
80: lpend = bp->b_linep;
81: lp = lforw(lpend);
82: do {
83: cp = <ext(lp)[0]; /* begining of line */
84: cpend = &cp[llength(lp)]; /* end of line */
85: while(cp != cpend) {
86: putc(*cp, ffp);
87: cp++; /* putc may evalualte arguments more than once */
88: }
89: lp = lforw(lp);
90: if(lp == lpend) break; /* no implied newline on last line */
91: putc('\n', ffp);
92: } while(!ferror(ffp));
93: if(ferror(ffp)) {
94: ewprintf("Write I/O error");
95: return FIOERR;
96: }
97: return FIOSUC;
98: }
99:
100: /*
101: * Read a line from a file, and store the bytes
102: * in the supplied buffer. Stop on end of file or end of
103: * line. When FIOEOF is returned, there is a valid line
104: * of data without the normally implied \n.
105: */
106: ffgetline(buf, nbuf, nbytes)
107: register char *buf;
108: register int nbuf;
109: register int *nbytes;
110: {
111: register int c;
112: register int i;
113:
114: i = 0;
115: while((c = getc(ffp))!=EOF && c!='\n') {
116: buf[i++] = c;
117: if (i >= nbuf) return FIOLONG;
118: }
119: if (c == EOF && ferror(ffp) != FALSE) {
120: ewprintf("File read error");
121: return FIOERR;
122: }
123: *nbytes = i;
124: return c==EOF ? FIOEOF : FIOSUC;
125: }
126:
127: #ifndef NO_BACKUP
128: /*
129: * Rename the file "fname" into a backup
130: * copy. On Unix the backup has the same name as the
131: * original file, with a "~" on the end; this seems to
132: * be newest of the new-speak. The error handling is
133: * all in "file.c". The "unlink" is perhaps not the
134: * right thing here; I don't care that much as
135: * I don't enable backups myself.
136: */
137: fbackupfile(fn) char *fn; {
138: register char *nname;
139: char *malloc(), *strrchr();
140: int i;
141: char *lastpart;
142:
143: if ((nname=malloc((unsigned)(strlen(fn)+1+1))) == NULL) {
144: ewprintf("Can't get %d bytes", strlen(fn) + 1);
145: return (ABORT);
146: }
147: (void) strcpy(nname, fn);
148: /*
149: * with BSD, just strcat the ~. But SV has a max file name of 14, so
150: * we have to check.
151: */
152: lastpart = strrchr(nname, '/');
153: if (lastpart)
154: lastpart++;
155: else
156: lastpart = nname;
157: i = strlen(lastpart);
158: if (i > 13)
159: if (lastpart[13] == '~') { /* already a backup name */
160: free(nname);
161: return(FALSE);
162: }
163: else
164: lastpart[13] = '~';
165: else {
166: lastpart[i] = '~';
167: lastpart[i+1] = 0;
168: }
169: (void) unlink(nname); /* Ignore errors. */
170: if (link(fn, nname) != 0 || unlink(fn) != 0) {
171: free(nname);
172: return (FALSE);
173: }
174: free(nname);
175: return (TRUE);
176: }
177: #endif
178:
179: /*
180: * The string "fn" is a file name.
181: * Perform any required appending of directory name or case adjustments.
182: * If NO_DIR is not defined, the same file should be refered to even if the
183: * working directory changes.
184: */
185: #ifdef SYMBLINK
186: #include <sys/types.h>
187: #include <sys/stat.h>
188: #ifndef MAXLINK
189: #define MAXLINK 8 /* maximum symbolic links to follow */
190: #endif
191: #endif
192: #include <pwd.h>
193: #ifndef NO_DIR
194: extern char *wdir;
195: #endif
196:
197: char *adjustname(fn)
198: register char *fn;
199: {
200: register char *cp;
201: static char fnb[NFILEN];
202: extern struct passwd *getpwnam();
203: struct passwd *pwent;
204: #ifdef SYMBLINK
205: struct stat statbuf;
206: int i, j;
207: char linkbuf[NFILEN];
208: #endif
209:
210: switch(*fn) {
211: case '/':
212: cp = fnb;
213: *cp++ = *fn++;
214: break;
215: case '~':
216: fn++;
217: if(*fn == '/' || *fn == '\0') {
218: (VOID) strcpy(fnb, getenv("HOME"));
219: cp = fnb + strlen(fnb);
220: if(*fn) fn++;
221: break;
222: } else {
223: cp = fnb;
224: while(*fn && *fn != '/') *cp++ = *fn++;
225: *cp = '\0';
226: if((pwent = getpwnam(fnb)) != NULL) {
227: (VOID) strcpy(fnb, pwent->pw_dir);
228: cp = fnb + strlen(fnb);
229: break;
230: } else {
231: fn -= strlen(fnb) + 1;
232: /* can't find ~user, continue to default case */
233: }
234: }
235: default:
236: #ifndef NODIR
237: strcpy(fnb, wdir);
238: cp = fnb + strlen(fnb);
239: break;
240: #else
241: return fn; /* punt */
242: #endif
243: }
244: if(cp != fnb && cp[-1] != '/') *cp++ = '/';
245: while(*fn) {
246: switch(*fn) {
247: case '.':
248: switch(fn[1]) {
249: case '\0':
250: *--cp = '\0';
251: return fnb;
252: case '/':
253: fn += 2;
254: continue;
255: case '.':
256: if(fn[2]=='/' || fn[2] == '\0') {
257: #ifdef SYMBLINK
258: cp[-1] = '\0';
259: for(j = MAXLINK; j-- &&
260: lstat(fnb, &statbuf) != -1 &&
261: (statbuf.st_mode&S_IFMT) == S_IFLNK &&
262: (i = readlink(fnb, linkbuf, sizeof linkbuf))
263: != -1 ;) {
264: if(linkbuf[0] != '/') {
265: --cp;
266: while(cp > fnb && *--cp != '/') {}
267: ++cp;
268: (VOID) strncpy(cp, linkbuf, i);
269: cp += i;
270: } else {
271: (VOID) strncpy(fnb, linkbuf, i);
272: cp = fnb + i;
273: }
274: if(cp[-1]!='/') *cp++ = '\0';
275: else cp[-1] = '\0';
276: }
277: cp[-1] = '/';
278: #endif
279: --cp;
280: while(cp > fnb && *--cp != '/') {}
281: ++cp;
282: if(fn[2]=='\0') {
283: *--cp = '\0';
284: return fnb;
285: }
286: fn += 3;
287: continue;
288: }
289: break;
290: default:
291: break;
292: }
293: break;
294: case '/':
295: fn++;
296: continue;
297: default:
298: break;
299: }
300: while(*fn && (*cp++ = *fn++) != '/') {}
301: }
302: if(cp[-1]=='/') --cp;
303: *cp = '\0';
304: return fnb;
305: }
306:
307: #ifndef NO_STARTUP
308: #include <sys/file.h>
1.2 ! deraadt 309: #ifndef F_OK
1.1 deraadt 310: #define F_OK 04 /* for stupid Sys V */
1.2 ! deraadt 311: #endif
1.1 deraadt 312:
313: /*
314: * Find a startup file for the user and return its name. As a service
315: * to other pieces of code that may want to find a startup file (like
316: * the terminal driver in particular), accepts a suffix to be appended
317: * to the startup file name.
318: */
319: char *
320: startupfile(suffix)
321: char *suffix;
322: {
323: register char *file;
324: static char home[NFILEN];
325: char *getenv();
326:
327: if ((file = getenv("HOME")) == NULL) goto notfound;
328: if (strlen(file)+7 >= NFILEN - 1) goto notfound;
329: (VOID) strcpy(home, file);
330: (VOID) strcat(home, "/.mg");
331: if (suffix != NULL) {
332: (VOID) strcat(home, "-");
333: (VOID) strcat(home, suffix);
334: }
335: if (access(home, F_OK) == 0) return home;
336:
337: notfound:
338: #ifdef STARTUPFILE
339: file = STARTUPFILE;
340: if (suffix != NULL) {
341: (VOID) strcpy(home, file);
342: (VOID) strcat(home, "-");
343: (VOID) strcat(home, suffix);
344: file = home;
345: }
346: if (access(file, F_OK ) == 0) return file;
347: #endif
348:
349: return NULL;
350: }
351: #endif
352:
353: #ifndef NO_DIRED
354: #include "kbd.h"
355:
356: /*
357: * It's sort of gross to call system commands in a subfork. However
358: * that's by far the easiest way. These things are used only in
359: * dired, so they are not performance-critical. The cp program is
360: * almost certainly faster at copying files than any stupid code that
361: * we would write. In fact there is no other way to do unlinkdir.
362: * You don't want to do a simple unlink. To do the whole thing in
363: * a general way requires root, and rmdir is setuid. We don't really
364: * want microemacs to have to run setuid. rename is easy to do with
365: * unlink, link, unlink. However that code will fail if the destination
366: * is on a different file system. mv will copy in that case. It seems
367: * easier to let mv worry about this stuff.
368: */
369:
370: copy(frname, toname)
371: char *frname, *toname;
372: {
373: int pid;
374: int status;
375:
376: if(pid = fork()) {
377: if(pid == -1) return -1;
378: execl("/bin/cp", "cp", frname, toname, (char *)NULL);
379: _exit(1); /* shouldn't happen */
380: }
381: while(wait(&status) != pid)
382: ;
383: return status == 0;
384: }
385:
386: BUFFER *dired_(dirname)
387: char *dirname;
388: {
389: register BUFFER *bp;
390: char line[256];
391: BUFFER *findbuffer();
392: FILE *dirpipe;
393: FILE *popen();
394: char *strncpy();
395:
396: if((dirname = adjustname(dirname)) == NULL) {
397: ewprintf("Bad directory name");
398: return NULL;
399: }
400: if((bp = findbuffer(dirname)) == NULL) {
401: ewprintf("Could not create buffer");
402: return NULL;
403: }
404: if(bclear(bp) != TRUE) return FALSE;
405: (VOID) strcpy(line, "ls -al ");
406: (VOID) strcpy(&line[7], dirname);
407: if((dirpipe = popen(line, "r")) == NULL) {
408: ewprintf("Problem opening pipe to ls");
409: return NULL;
410: }
411: line[0] = line[1] = ' ';
412: while(fgets(&line[2], 254, dirpipe) != NULL) {
413: line[strlen(line) - 1] = '\0'; /* remove ^J */
414: (VOID) addline(bp, line);
415: }
416: if(pclose(dirpipe) == -1) {
417: ewprintf("Problem closing pipe to ls");
418: return NULL;
419: }
420: bp->b_dotp = lforw(bp->b_linep); /* go to first line */
421: (VOID) strncpy(bp->b_fname, dirname, NFILEN);
422: if((bp->b_modes[0] = name_mode("dired")) == NULL) {
423: bp->b_modes[0] = &map_table[0];
424: ewprintf("Could not find mode dired");
425: return NULL;
426: }
427: bp->b_nmodes = 0;
428: return bp;
429: }
430:
431: d_makename(lp, fn)
432: register LINE *lp;
433: register char *fn;
434: {
435: register char *cp;
436:
437: if(llength(lp) <= 56) return ABORT;
438: (VOID) strcpy(fn, curbp->b_fname);
439: cp = fn + strlen(fn);
440: bcopy(&lp->l_text[56], cp, llength(lp) - 56);
441: cp[llength(lp) - 56] = '\0';
442: return lgetc(lp, 2) == 'd';
443: }
444:
445: /*
446: * I, a System V novice, could only figure out how to do unlinkdir()
447: * and rename() as exec's of the appropriate functions. So sue me.
448: * --Stephen Walton, December 1987
449: */
450:
451: unlinkdir(f)
452: char *f;
453: {
454: int status, pid, wpid;
455:
456: if ((pid = fork()) == 0)
457: execl("/bin/rmdir", "rmdir", f, (char *)NULL);
458: else if (pid > 0)
459: while ((wpid = wait(&status)) && wpid != pid)
460: ;
461: else
462: return FALSE;
463: return status == 0;
464: }
465:
466: Xrename(f1, f2)
467: char *f1, *f2;
468: {
469:
470: int status, pid, wpid;
471:
472: if ((pid = fork()) == 0)
473: execl("/bin/mv", "mv", f1, f2, (char *)NULL);
474: else if (pid > 0)
475: while ((wpid = wait(&status)) && wpid != pid)
476: ;
477: else
478: return FALSE;
479: return status == 0;
480: }
481: #endif NO_DIRED
482:
483: struct filelist {
484: LIST fl_l;
485: char fl_name[NFILEN+2];
486: };
487:
488: /* these things had better be contiguous, because we're going
489: * to refer to the end of dirbuf + 1 byte */
490: struct direct dirbuf;
491: char dirdummy;
492:
493: /*
494: * return list of file names that match the name in buf.
495: * System V version. listing is a flag indicating whether the
496: * list is being used for printing a listing rather than
497: * completion. In that case, trailing * and / are put on
498: * for executables and directories. The list is not sorted.
499: */
500:
501: LIST *make_file_list(buf,listing)
502: char *buf;
503: int listing;
504: {
505: char *dir,*file,*cp;
506: int len,i,preflen;
507: int fp;
508: LIST *last,*l1,*l2;
509: struct filelist *current;
510: char prefixx[NFILEN+1];
511: extern int errno;
512: struct stat statbuf;
513: char statname[NFILEN+2];
514:
515: /* We need three different strings:
516: * dir - the name of the directory containing what the user typed.
517: * Must be a real unix file name, e.g. no ~user, etc.. Must
518: * not end in /.
519: * prefix - the portion of what the user typed that is before
520: * the names we are going to find in the directory. Must have
521: * a trailing / if the user typed it.
522: * names from the directory.
523: * we open dir, and return prefix concatenated with names.
524: */
525:
526: /* first we get a directory name we can look up */
527: /* names ending in . are potentially odd, because adjustname
528: * will treat foo/.. as a reference to another directory,
529: * whereas we are interested in names starting with ..
530: */
531: len = strlen(buf);
532: if (buf[len-1] == '.') {
533: buf[len-1] = 'x';
534: dir = adjustname(buf);
535: buf[len-1] = '.';
536: }
537: else
538: dir = adjustname(buf);
539: /*
540: * if the user typed a trailing / or the empty string
541: * he wants us to use his file spec as a directory name
542: */
543: if (buf[0] && buf[strlen(buf)-1] != '/') {
544: file = strrchr(dir, '/');
545: if (file) {
546: *file = 0;
547: if (*dir == 0)
548: dir = "/";
549: }
550: else {
551: return(NULL);
552: }
553: }
554:
555: /* now we get the prefix of the name the user typed */
556: strcpy(prefixx,buf);
557: cp = strrchr(prefixx, '/');
558: if (cp == NULL)
559: prefixx[0] = 0;
560: else
561: cp[1] = 0;
562:
563: preflen = strlen(prefixx);
564: /* cp is the tail of buf that really needs to be compared */
565: cp = buf + preflen;
566: len = strlen(cp);
567:
568: /*
569: * Now make sure that file names will fit in the buffers allocated.
570: * SV files are fairly short. For BSD, something more general
571: * would be required.
572: */
1.2 ! deraadt 573: if ((preflen + MAXNAMLEN) > NFILEN)
1.1 deraadt 574: return(NULL);
1.2 ! deraadt 575: if ((strlen(dir) + MAXNAMLEN) > NFILEN)
1.1 deraadt 576: listing = 0;
577:
578: /* loop over the specified directory, making up the list of files */
579:
580: /*
581: * Note that it is worth our time to filter out names that don't
582: * match, even though our caller is going to do so again, and to
583: * avoid doing the stat if completion is being done, because stat'ing
584: * every file in the directory is relatively expensive.
585: */
586:
587: fp = open(dir,0);
588: if (fp < 0) {
589: return(NULL);
590: }
591:
592: last = NULL;
593: /* clear entry after last so we can treat d_name as ASCIZ */
1.2 ! deraadt 594: dirbuf.d_name[MAXNAMLEN] = 0;
1.1 deraadt 595: while (1) {
596: if (read(fp, &dirbuf, sizeof(struct direct)) <= 0) {
597: break;
598: }
599: if (dirbuf.d_ino == 0) /* entry not allocated */
600: continue;
601: for (i=0; i<len; ++i) {
602: if (cp[i] != dirbuf.d_name[i])
603: break;
604: }
605: if (i < len)
606: continue;
607: current = (struct filelist *)malloc(sizeof(struct filelist));
608: current->fl_l.l_next = last;
609: current->fl_l.l_name = current->fl_name;
610: last = (LIST *)current;
611: strcpy(current->fl_name,prefixx);
612: strcat(current->fl_name,dirbuf.d_name);
613: if (listing) {
614: statbuf.st_mode = 0;
615: strcpy(statname,dir);
616: strcat(statname,"/");
617: strcat(statname,dirbuf.d_name);
618: stat(statname,&statbuf);
619: if (statbuf.st_mode & 040000)
620: strcat(current->fl_name,"/");
621: else if (statbuf.st_mode & 0100)
622: strcat(current->fl_name,"*");
623: }
624:
625: }
626: close(fp);
627:
628: return (last);
629:
630: }