[BACK]Return to edit.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / less

Annotation of src/usr.bin/less/edit.c, Revision 1.22

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.12      nicm       61:        for (s = str;  s < tlist->endstring;  s++) {
                     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.12      nicm      293:        } else if ((f = open(qopen_filename, O_RDONLY)) < 0) {
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: }