Annotation of src/usr.bin/less/edit.c, Revision 1.24
1.1 etheisen 1: /*
1.10 shadchin 2: * Copyright (C) 1984-2012 Mark Nudelman
1.13 nicm 3: * Modified for use with illumos by Garrett D'Amore.
4: * Copyright 2014 Garrett D'Amore <garrett@damore.org>
1.1 etheisen 5: *
1.4 millert 6: * You may distribute under the terms of either the GNU General Public
7: * License or the Less License, as specified in the README file.
1.1 etheisen 8: *
1.10 shadchin 9: * For more information, see the README file.
1.12 nicm 10: */
1.1 etheisen 11:
1.22 mmcc 12: #include <sys/stat.h>
13:
1.1 etheisen 14: #include "less.h"
15:
1.12 nicm 16: static int fd0 = 0;
1.1 etheisen 17:
18: extern int new_file;
19: extern int errmsgs;
20: extern char *every_first_cmd;
21: extern int any_display;
22: extern int force_open;
23: extern int is_tty;
1.9 millert 24: extern volatile sig_atomic_t sigs;
1.1 etheisen 25: extern IFILE curr_ifile;
26: extern IFILE old_ifile;
27: extern struct scrpos initial_scrpos;
1.12 nicm 28: extern void *ml_examine;
1.4 millert 29: extern char openquote;
30: extern char closequote;
1.12 nicm 31: extern int less_is_more;
1.1 etheisen 32: extern int logfile;
33: extern int force_logfile;
34: extern char *namelogfile;
35:
1.12 nicm 36: dev_t curr_dev;
37: ino_t curr_ino;
1.8 shadchin 38:
1.1 etheisen 39: char *curr_altfilename = NULL;
40: static void *curr_altpipe;
41:
1.12 nicm 42:
1.1 etheisen 43: /*
44: * Textlist functions deal with a list of words separated by spaces.
45: * init_textlist sets up a textlist structure.
46: * forw_textlist uses that structure to iterate thru the list of
47: * words, returning each one as a standard null-terminated string.
48: * back_textlist does the same, but runs thru the list backwards.
49: */
1.12 nicm 50: void
51: init_textlist(struct textlist *tlist, char *str)
1.1 etheisen 52: {
53: char *s;
1.4 millert 54: int meta_quoted = 0;
55: int delim_quoted = 0;
56: char *esc = get_meta_escape();
57: int esclen = strlen(esc);
1.12 nicm 58:
1.1 etheisen 59: tlist->string = skipsp(str);
60: tlist->endstring = tlist->string + strlen(tlist->string);
1.23 deraadt 61: for (s = str; s < tlist->endstring; s++) {
1.12 nicm 62: if (meta_quoted) {
1.4 millert 63: meta_quoted = 0;
64: } else if (esclen > 0 && s + esclen < tlist->endstring &&
1.12 nicm 65: strncmp(s, esc, esclen) == 0) {
1.4 millert 66: meta_quoted = 1;
67: s += esclen - 1;
1.12 nicm 68: } else if (delim_quoted) {
1.4 millert 69: if (*s == closequote)
70: delim_quoted = 0;
1.12 nicm 71: } else /* (!delim_quoted) */ {
1.4 millert 72: if (*s == openquote)
73: delim_quoted = 1;
74: else if (*s == ' ')
75: *s = '\0';
76: }
1.1 etheisen 77: }
78: }
79:
1.12 nicm 80: char *
81: forw_textlist(struct textlist *tlist, char *prev)
1.1 etheisen 82: {
83: char *s;
1.12 nicm 84:
1.1 etheisen 85: /*
86: * prev == NULL means return the first word in the list.
87: * Otherwise, return the word after "prev".
88: */
89: if (prev == NULL)
90: s = tlist->string;
91: else
92: s = prev + strlen(prev);
93: if (s >= tlist->endstring)
94: return (NULL);
95: while (*s == '\0')
96: s++;
97: if (s >= tlist->endstring)
98: return (NULL);
99: return (s);
100: }
101:
1.12 nicm 102: char *
103: back_textlist(struct textlist *tlist, char *prev)
1.1 etheisen 104: {
105: char *s;
1.12 nicm 106:
1.1 etheisen 107: /*
108: * prev == NULL means return the last word in the list.
109: * Otherwise, return the word before "prev".
110: */
111: if (prev == NULL)
112: s = tlist->endstring;
113: else if (prev <= tlist->string)
114: return (NULL);
115: else
116: s = prev - 1;
117: while (*s == '\0')
118: s--;
119: if (s <= tlist->string)
120: return (NULL);
121: while (s[-1] != '\0' && s > tlist->string)
122: s--;
123: return (s);
124: }
125:
126: /*
127: * Close the current input file.
128: */
1.12 nicm 129: static void
130: close_file(void)
1.1 etheisen 131: {
132: struct scrpos scrpos;
1.12 nicm 133:
1.19 deraadt 134: if (curr_ifile == NULL)
1.1 etheisen 135: return;
1.4 millert 136:
1.1 etheisen 137: /*
138: * Save the current position so that we can return to
139: * the same position if we edit this file again.
140: */
141: get_scrpos(&scrpos);
1.12 nicm 142: if (scrpos.pos != -1) {
1.1 etheisen 143: store_pos(curr_ifile, &scrpos);
144: lastmark();
145: }
146: /*
147: * Close the file descriptor, unless it is a pipe.
148: */
149: ch_close();
150: /*
151: * If we opened a file using an alternate name,
152: * do special stuff to close it.
153: */
1.12 nicm 154: if (curr_altfilename != NULL) {
1.1 etheisen 155: close_altfile(curr_altfilename, get_filename(curr_ifile),
1.12 nicm 156: curr_altpipe);
1.1 etheisen 157: free(curr_altfilename);
158: curr_altfilename = NULL;
159: }
1.19 deraadt 160: curr_ifile = NULL;
1.8 shadchin 161: curr_ino = curr_dev = 0;
1.1 etheisen 162: }
163:
164: /*
165: * Edit a new file (given its name).
166: * Filename == "-" means standard input.
167: * Filename == NULL means just close the current file.
168: */
1.12 nicm 169: int
170: edit(char *filename)
1.1 etheisen 171: {
172: if (filename == NULL)
1.19 deraadt 173: return (edit_ifile(NULL));
1.1 etheisen 174: return (edit_ifile(get_ifile(filename, curr_ifile)));
175: }
1.12 nicm 176:
1.1 etheisen 177: /*
178: * Edit a new file (given its IFILE).
179: * ifile == NULL means just close the current file.
180: */
1.12 nicm 181: int
182: edit_ifile(IFILE ifile)
1.1 etheisen 183: {
184: int f;
185: int answer;
186: int no_display;
187: int chflags;
188: char *filename;
189: char *open_filename;
1.4 millert 190: char *qopen_filename;
1.1 etheisen 191: char *alt_filename;
192: void *alt_pipe;
193: IFILE was_curr_ifile;
194: PARG parg;
1.12 nicm 195:
196: if (ifile == curr_ifile) {
1.1 etheisen 197: /*
198: * Already have the correct file open.
199: */
200: return (0);
201: }
202:
203: /*
204: * We must close the currently open file now.
205: * This is necessary to make the open_altfile/close_altfile pairs
206: * nest properly (or rather to avoid nesting at all).
207: * {{ Some stupid implementations of popen() mess up if you do:
208: * fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }}
209: */
210: end_logfile();
1.4 millert 211: was_curr_ifile = save_curr_ifile();
1.19 deraadt 212: if (curr_ifile != NULL) {
1.4 millert 213: chflags = ch_getflags();
1.1 etheisen 214: close_file();
1.12 nicm 215: if ((chflags & CH_HELPFILE) &&
216: held_ifile(was_curr_ifile) <= 1) {
1.8 shadchin 217: /*
218: * Don't keep the help file in the ifile list.
219: */
220: del_ifile(was_curr_ifile);
221: was_curr_ifile = old_ifile;
222: }
1.1 etheisen 223: }
224:
1.19 deraadt 225: if (ifile == NULL) {
1.1 etheisen 226: /*
227: * No new file to open.
228: * (Don't set old_ifile, because if you call edit_ifile(NULL),
229: * you're supposed to have saved curr_ifile yourself,
230: * and you'll restore it if necessary.)
231: */
1.4 millert 232: unsave_ifile(was_curr_ifile);
1.1 etheisen 233: return (0);
234: }
235:
1.14 tedu 236: filename = estrdup(get_filename(ifile));
1.1 etheisen 237: /*
238: * See if LESSOPEN specifies an "alternate" file to open.
239: */
240: alt_pipe = NULL;
241: alt_filename = open_altfile(filename, &f, &alt_pipe);
242: open_filename = (alt_filename != NULL) ? alt_filename : filename;
1.4 millert 243: qopen_filename = shell_unquote(open_filename);
1.1 etheisen 244:
245: chflags = 0;
1.11 schwarze 246: if (strcmp(open_filename, helpfile()) == 0)
1.8 shadchin 247: chflags |= CH_HELPFILE;
1.12 nicm 248: if (alt_pipe != NULL) {
1.1 etheisen 249: /*
250: * The alternate "file" is actually a pipe.
251: * f has already been set to the file descriptor of the pipe
252: * in the call to open_altfile above.
1.12 nicm 253: * Keep the file descriptor open because it was opened
1.1 etheisen 254: * via popen(), and pclose() wants to close it.
255: */
256: chflags |= CH_POPENED;
1.12 nicm 257: } else if (strcmp(open_filename, "-") == 0) {
258: /*
1.1 etheisen 259: * Use standard input.
260: * Keep the file descriptor open because we can't reopen it.
261: */
262: f = fd0;
263: chflags |= CH_KEEPOPEN;
1.12 nicm 264: } else if (strcmp(open_filename, FAKE_EMPTYFILE) == 0) {
1.10 shadchin 265: f = -1;
266: chflags |= CH_NODATA;
1.12 nicm 267: } else if ((parg.p_string = bad_file(open_filename)) != NULL) {
1.1 etheisen 268: /*
269: * It looks like a bad file. Don't try to open it.
270: */
271: error("%s", &parg);
272: free(parg.p_string);
1.12 nicm 273: err1:
274: if (alt_filename != NULL) {
1.1 etheisen 275: close_altfile(alt_filename, filename, alt_pipe);
276: free(alt_filename);
277: }
278: del_ifile(ifile);
1.4 millert 279: free(qopen_filename);
280: free(filename);
1.1 etheisen 281: /*
282: * Re-open the current file.
283: */
1.12 nicm 284: if (was_curr_ifile == ifile) {
1.8 shadchin 285: /*
1.12 nicm 286: * Whoops. The "current" ifile is the one we just
287: * deleted. Just give up.
1.8 shadchin 288: */
289: quit(QUIT_ERROR);
290: }
1.4 millert 291: reedit_ifile(was_curr_ifile);
1.1 etheisen 292: return (1);
1.24 ! deraadt 293: } else if ((f = open(qopen_filename, O_RDONLY)) == -1) {
1.1 etheisen 294: /*
295: * Got an error trying to open it.
296: */
297: parg.p_string = errno_message(filename);
298: error("%s", &parg);
299: free(parg.p_string);
1.12 nicm 300: goto err1;
301: } else {
1.4 millert 302: chflags |= CH_CANSEEK;
1.12 nicm 303: if (!force_open && !opened(ifile) && bin_file(f)) {
1.4 millert 304: /*
1.12 nicm 305: * Looks like a binary file.
1.4 millert 306: * Ask user if we should proceed.
307: */
308: parg.p_string = filename;
1.12 nicm 309: answer = query("\"%s\" may be a binary file. "
310: "See it anyway? ", &parg);
311: if (answer != 'y' && answer != 'Y') {
312: (void) close(f);
1.4 millert 313: goto err1;
314: }
1.1 etheisen 315: }
316: }
317:
318: /*
319: * Get the new ifile.
320: * Get the saved position for the file.
321: */
1.19 deraadt 322: if (was_curr_ifile != NULL) {
1.1 etheisen 323: old_ifile = was_curr_ifile;
1.4 millert 324: unsave_ifile(was_curr_ifile);
325: }
1.1 etheisen 326: curr_ifile = ifile;
327: curr_altfilename = alt_filename;
328: curr_altpipe = alt_pipe;
329: set_open(curr_ifile); /* File has been opened */
330: get_pos(curr_ifile, &initial_scrpos);
331: new_file = TRUE;
332: ch_init(f, chflags);
1.4 millert 333:
1.12 nicm 334: if (!(chflags & CH_HELPFILE)) {
335: struct stat statbuf;
336: int r;
337:
1.8 shadchin 338: if (namelogfile != NULL && is_tty)
339: use_logfile(namelogfile);
1.12 nicm 340: /* Remember the i-number and device of opened file. */
341: r = stat(qopen_filename, &statbuf);
342: if (r == 0) {
343: curr_ino = statbuf.st_ino;
344: curr_dev = statbuf.st_dev;
1.8 shadchin 345: }
346: if (every_first_cmd != NULL)
347: ungetsc(every_first_cmd);
348: }
349: free(qopen_filename);
1.1 etheisen 350: no_display = !any_display;
1.20 nicm 351: flush(0);
1.1 etheisen 352: any_display = TRUE;
353:
1.12 nicm 354: if (is_tty) {
1.1 etheisen 355: /*
356: * Output is to a real tty.
357: */
358:
359: /*
360: * Indicate there is nothing displayed yet.
361: */
362: pos_clear();
363: clr_linenum();
364: clr_hilite();
1.4 millert 365: cmd_addhist(ml_examine, filename);
1.12 nicm 366: if (no_display && errmsgs > 0) {
1.1 etheisen 367: /*
368: * We displayed some messages on error output
369: * (file descriptor 2; see error() function).
370: * Before erasing the screen contents,
371: * display the file name and wait for a keystroke.
372: */
373: parg.p_string = filename;
374: error("%s", &parg);
375: }
376: }
1.4 millert 377: free(filename);
1.1 etheisen 378: return (0);
379: }
380:
381: /*
382: * Edit a space-separated list of files.
383: * For each filename in the list, enter it into the ifile list.
384: * Then edit the first one.
385: */
1.12 nicm 386: int
387: edit_list(char *filelist)
1.1 etheisen 388: {
1.4 millert 389: IFILE save_ifile;
1.1 etheisen 390: char *good_filename;
391: char *filename;
392: char *gfilelist;
393: char *gfilename;
394: struct textlist tl_files;
395: struct textlist tl_gfiles;
396:
1.4 millert 397: save_ifile = save_curr_ifile();
1.1 etheisen 398: good_filename = NULL;
1.12 nicm 399:
1.1 etheisen 400: /*
401: * Run thru each filename in the list.
1.12 nicm 402: * Try to glob the filename.
1.1 etheisen 403: * If it doesn't expand, just try to open the filename.
404: * If it does expand, try to open each name in that list.
405: */
406: init_textlist(&tl_files, filelist);
407: filename = NULL;
1.12 nicm 408: while ((filename = forw_textlist(&tl_files, filename)) != NULL) {
1.4 millert 409: gfilelist = lglob(filename);
1.1 etheisen 410: init_textlist(&tl_gfiles, gfilelist);
411: gfilename = NULL;
1.12 nicm 412: while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) !=
413: NULL) {
1.1 etheisen 414: if (edit(gfilename) == 0 && good_filename == NULL)
415: good_filename = get_filename(curr_ifile);
416: }
417: free(gfilelist);
418: }
419: /*
420: * Edit the first valid filename in the list.
421: */
1.12 nicm 422: if (good_filename == NULL) {
1.4 millert 423: unsave_ifile(save_ifile);
1.1 etheisen 424: return (1);
1.4 millert 425: }
1.12 nicm 426: if (get_ifile(good_filename, curr_ifile) == curr_ifile) {
1.1 etheisen 427: /*
428: * Trying to edit the current file; don't reopen it.
429: */
1.4 millert 430: unsave_ifile(save_ifile);
1.1 etheisen 431: return (0);
1.4 millert 432: }
433: reedit_ifile(save_ifile);
1.1 etheisen 434: return (edit(good_filename));
435: }
436:
437: /*
438: * Edit the first file in the command line (ifile) list.
439: */
1.12 nicm 440: int
441: edit_first(void)
1.1 etheisen 442: {
1.19 deraadt 443: curr_ifile = NULL;
1.1 etheisen 444: return (edit_next(1));
445: }
446:
447: /*
448: * Edit the last file in the command line (ifile) list.
449: */
1.12 nicm 450: int
451: edit_last(void)
1.1 etheisen 452: {
1.19 deraadt 453: curr_ifile = NULL;
1.1 etheisen 454: return (edit_prev(1));
455: }
456:
457:
458: /*
1.8 shadchin 459: * Edit the n-th next or previous file in the command line (ifile) list.
1.1 etheisen 460: */
1.12 nicm 461: static int
462: edit_istep(IFILE h, int n, int dir)
1.1 etheisen 463: {
464: IFILE next;
465:
466: /*
467: * Skip n filenames, then try to edit each filename.
468: */
1.12 nicm 469: for (;;) {
1.4 millert 470: next = (dir > 0) ? next_ifile(h) : prev_ifile(h);
1.12 nicm 471: if (--n < 0) {
1.1 etheisen 472: if (edit_ifile(h) == 0)
473: break;
474: }
1.19 deraadt 475: if (next == NULL) {
1.1 etheisen 476: /*
477: * Reached end of the ifile list.
478: */
479: return (1);
480: }
1.12 nicm 481: if (ABORT_SIGS()) {
1.4 millert 482: /*
483: * Interrupt breaks out, if we're in a long
484: * list of files that can't be opened.
485: */
486: return (1);
487: }
1.1 etheisen 488: h = next;
1.12 nicm 489: }
1.1 etheisen 490: /*
491: * Found a file that we can edit.
492: */
493: return (0);
494: }
495:
1.12 nicm 496: static int
497: edit_inext(IFILE h, int n)
1.4 millert 498: {
1.8 shadchin 499: return (edit_istep(h, n, +1));
1.4 millert 500: }
501:
1.12 nicm 502: int
503: edit_next(int n)
1.1 etheisen 504: {
1.12 nicm 505: return (edit_istep(curr_ifile, n, +1));
1.4 millert 506: }
507:
1.12 nicm 508: static int
509: edit_iprev(IFILE h, int n)
1.4 millert 510: {
511: return (edit_istep(h, n, -1));
512: }
1.1 etheisen 513:
1.12 nicm 514: int
515: edit_prev(int n)
1.4 millert 516: {
1.12 nicm 517: return (edit_istep(curr_ifile, n, -1));
1.1 etheisen 518: }
519:
520: /*
521: * Edit a specific file in the command line (ifile) list.
522: */
1.12 nicm 523: int
524: edit_index(int n)
1.1 etheisen 525: {
526: IFILE h;
527:
1.19 deraadt 528: h = NULL;
1.12 nicm 529: do {
1.19 deraadt 530: if ((h = next_ifile(h)) == NULL) {
1.1 etheisen 531: /*
532: * Reached end of the list without finding it.
533: */
534: return (1);
535: }
536: } while (get_index(h) != n);
537:
538: return (edit_ifile(h));
539: }
540:
1.12 nicm 541: IFILE
542: save_curr_ifile(void)
1.4 millert 543: {
1.19 deraadt 544: if (curr_ifile != NULL)
1.4 millert 545: hold_ifile(curr_ifile, 1);
546: return (curr_ifile);
547: }
548:
1.12 nicm 549: void
550: unsave_ifile(IFILE save_ifile)
1.4 millert 551: {
1.19 deraadt 552: if (save_ifile != NULL)
1.4 millert 553: hold_ifile(save_ifile, -1);
554: }
555:
556: /*
557: * Reedit the ifile which was previously open.
558: */
1.12 nicm 559: void
560: reedit_ifile(IFILE save_ifile)
1.4 millert 561: {
562: IFILE next;
563: IFILE prev;
564:
565: /*
566: * Try to reopen the ifile.
567: * Note that opening it may fail (maybe the file was removed),
568: * in which case the ifile will be deleted from the list.
569: * So save the next and prev ifiles first.
570: */
571: unsave_ifile(save_ifile);
572: next = next_ifile(save_ifile);
573: prev = prev_ifile(save_ifile);
574: if (edit_ifile(save_ifile) == 0)
575: return;
576: /*
577: * If can't reopen it, open the next input file in the list.
578: */
1.19 deraadt 579: if (next != NULL && edit_inext(next, 0) == 0)
1.4 millert 580: return;
581: /*
582: * If can't open THAT one, open the previous input file in the list.
583: */
1.19 deraadt 584: if (prev != NULL && edit_iprev(prev, 0) == 0)
1.4 millert 585: return;
586: /*
587: * If can't even open that, we're stuck. Just quit.
588: */
589: quit(QUIT_ERROR);
1.8 shadchin 590: }
591:
1.12 nicm 592: void
593: reopen_curr_ifile(void)
1.8 shadchin 594: {
595: IFILE save_ifile = save_curr_ifile();
596: close_file();
597: reedit_ifile(save_ifile);
1.4 millert 598: }
599:
1.1 etheisen 600: /*
601: * Edit standard input.
602: */
1.12 nicm 603: int
604: edit_stdin(void)
1.1 etheisen 605: {
1.12 nicm 606: if (isatty(fd0)) {
607: if (less_is_more) {
608: error("Missing filename (\"more -h\" for help)",
1.17 deraadt 609: NULL);
1.12 nicm 610: } else {
611: error("Missing filename (\"less --help\" for help)",
1.17 deraadt 612: NULL);
1.12 nicm 613: }
1.1 etheisen 614: quit(QUIT_OK);
615: }
616: return (edit("-"));
617: }
618:
619: /*
620: * Copy a file directly to standard output.
621: * Used if standard output is not a tty.
622: */
1.12 nicm 623: void
624: cat_file(void)
1.1 etheisen 625: {
1.12 nicm 626: int c;
1.1 etheisen 627:
628: while ((c = ch_forw_get()) != EOI)
629: putchr(c);
1.20 nicm 630: flush(0);
1.1 etheisen 631: }
632:
633: /*
634: * If the user asked for a log file and our input file
1.12 nicm 635: * is standard input, create the log file.
1.1 etheisen 636: * We take care not to blindly overwrite an existing file.
637: */
1.12 nicm 638: void
639: use_logfile(char *filename)
1.1 etheisen 640: {
1.12 nicm 641: int exists;
642: int answer;
1.1 etheisen 643: PARG parg;
644:
645: if (ch_getflags() & CH_CANSEEK)
646: /*
647: * Can't currently use a log file on a file that can seek.
648: */
649: return;
650:
651: /*
652: * {{ We could use access() here. }}
653: */
1.4 millert 654: filename = shell_unquote(filename);
1.15 deraadt 655: exists = open(filename, O_RDONLY);
1.1 etheisen 656: close(exists);
657: exists = (exists >= 0);
658:
659: /*
660: * Decide whether to overwrite the log file or append to it.
661: * If it doesn't exist we "overwrite" it.
662: */
1.12 nicm 663: if (!exists || force_logfile) {
1.1 etheisen 664: /*
665: * Overwrite (or create) the log file.
666: */
667: answer = 'O';
1.12 nicm 668: } else {
1.1 etheisen 669: /*
670: * Ask user what to do.
671: */
672: parg.p_string = filename;
1.12 nicm 673: answer = query("Warning: \"%s\" exists; "
674: "Overwrite, Append or Don't log? ", &parg);
1.1 etheisen 675: }
676:
677: loop:
1.12 nicm 678: switch (answer) {
1.1 etheisen 679: case 'O': case 'o':
680: /*
681: * Overwrite: create the file.
682: */
1.16 deraadt 683: logfile = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0644);
1.1 etheisen 684: break;
685: case 'A': case 'a':
686: /*
687: * Append: open the file and seek to the end.
688: */
1.15 deraadt 689: logfile = open(filename, O_WRONLY | O_APPEND);
1.18 deraadt 690: if (lseek(logfile, (off_t)0, SEEK_END) == (off_t)-1) {
1.1 etheisen 691: close(logfile);
692: logfile = -1;
693: }
694: break;
695: case 'D': case 'd':
696: /*
697: * Don't do anything.
698: */
1.4 millert 699: free(filename);
1.1 etheisen 700: return;
701: case 'q':
702: quit(QUIT_OK);
703: default:
704: /*
705: * Eh?
706: */
1.12 nicm 707: answer = query("Overwrite, Append, or Don't log? "
1.17 deraadt 708: "(Type \"O\", \"A\", \"D\" or \"q\") ", NULL);
1.1 etheisen 709: goto loop;
710: }
711:
1.12 nicm 712: if (logfile < 0) {
1.1 etheisen 713: /*
714: * Error in opening logfile.
715: */
716: parg.p_string = filename;
717: error("Cannot write to \"%s\"", &parg);
1.4 millert 718: free(filename);
719: return;
1.1 etheisen 720: }
1.4 millert 721: free(filename);
1.1 etheisen 722: }