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