[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.18

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