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

1.3     ! mpech       1: /*     $OpenBSD: edit.c,v 1.2 2001/01/29 01:58:01 niklas Exp $ */
1.2       niklas      2:
1.1       etheisen    3: /*
                      4:  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
                      5:  * All rights reserved.
                      6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice in the documentation and/or other materials provided with
                     14:  *    the distribution.
                     15:  *
                     16:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
                     17:  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     18:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     19:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
                     20:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     21:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
                     22:  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
                     23:  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
                     24:  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
                     25:  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
                     26:  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     27:  */
                     28:
                     29:
                     30: #include "less.h"
                     31:
                     32: public int fd0 = 0;
                     33:
                     34: extern int new_file;
                     35: extern int errmsgs;
                     36: extern int quit_at_eof;
                     37: extern int cbufs;
                     38: extern char *every_first_cmd;
                     39: extern int any_display;
                     40: extern int force_open;
                     41: extern int is_tty;
                     42: extern IFILE curr_ifile;
                     43: extern IFILE old_ifile;
                     44: extern struct scrpos initial_scrpos;
                     45:
                     46: #if LOGFILE
                     47: extern int logfile;
                     48: extern int force_logfile;
                     49: extern char *namelogfile;
                     50: #endif
                     51:
                     52: char *curr_altfilename = NULL;
                     53: static void *curr_altpipe;
                     54:
                     55:
                     56: /*
                     57:  * Textlist functions deal with a list of words separated by spaces.
                     58:  * init_textlist sets up a textlist structure.
                     59:  * forw_textlist uses that structure to iterate thru the list of
                     60:  * words, returning each one as a standard null-terminated string.
                     61:  * back_textlist does the same, but runs thru the list backwards.
                     62:  */
                     63:        public void
                     64: init_textlist(tlist, str)
                     65:        struct textlist *tlist;
                     66:        char *str;
                     67: {
                     68:        char *s;
                     69:
                     70:        tlist->string = skipsp(str);
                     71:        tlist->endstring = tlist->string + strlen(tlist->string);
                     72:        for (s = str;  s < tlist->endstring;  s++)
                     73:        {
                     74:                if (*s == ' ')
                     75:                        *s = '\0';
                     76:        }
                     77: }
                     78:
                     79:        public char *
                     80: forw_textlist(tlist, prev)
                     81:        struct textlist *tlist;
                     82:        char *prev;
                     83: {
                     84:        char *s;
                     85:
                     86:        /*
                     87:         * prev == NULL means return the first word in the list.
                     88:         * Otherwise, return the word after "prev".
                     89:         */
                     90:        if (prev == NULL)
                     91:                s = tlist->string;
                     92:        else
                     93:                s = prev + strlen(prev);
                     94:        if (s >= tlist->endstring)
                     95:                return (NULL);
                     96:        while (*s == '\0')
                     97:                s++;
                     98:        if (s >= tlist->endstring)
                     99:                return (NULL);
                    100:        return (s);
                    101: }
                    102:
                    103:        public char *
                    104: back_textlist(tlist, prev)
                    105:        struct textlist *tlist;
                    106:        char *prev;
                    107: {
                    108:        char *s;
                    109:
                    110:        /*
                    111:         * prev == NULL means return the last word in the list.
                    112:         * Otherwise, return the word before "prev".
                    113:         */
                    114:        if (prev == NULL)
                    115:                s = tlist->endstring;
                    116:        else if (prev <= tlist->string)
                    117:                return (NULL);
                    118:        else
                    119:                s = prev - 1;
                    120:        while (*s == '\0')
                    121:                s--;
                    122:        if (s <= tlist->string)
                    123:                return (NULL);
                    124:        while (s[-1] != '\0' && s > tlist->string)
                    125:                s--;
                    126:        return (s);
                    127: }
                    128:
                    129: /*
                    130:  * Close the current input file.
                    131:  */
                    132:        static void
                    133: close_file()
                    134: {
                    135:        struct scrpos scrpos;
                    136:
                    137:        if (curr_ifile == NULL_IFILE)
                    138:                return;
                    139:        /*
                    140:         * Save the current position so that we can return to
                    141:         * the same position if we edit this file again.
                    142:         */
                    143:        get_scrpos(&scrpos);
                    144:        if (scrpos.pos != NULL_POSITION)
                    145:        {
                    146:                store_pos(curr_ifile, &scrpos);
                    147:                lastmark();
                    148:        }
                    149:        /*
                    150:         * Close the file descriptor, unless it is a pipe.
                    151:         */
                    152:        ch_close();
                    153:        /*
                    154:         * If we opened a file using an alternate name,
                    155:         * do special stuff to close it.
                    156:         */
                    157:        if (curr_altfilename != NULL)
                    158:        {
                    159:                close_altfile(curr_altfilename, get_filename(curr_ifile),
                    160:                        curr_altpipe);
                    161:                free(curr_altfilename);
                    162:                curr_altfilename = NULL;
                    163:        }
                    164:        curr_ifile = NULL_IFILE;
                    165: }
                    166:
                    167: /*
                    168:  * Edit a new file (given its name).
                    169:  * Filename == "-" means standard input.
                    170:  * Filename == NULL means just close the current file.
                    171:  */
                    172:        public int
                    173: edit(filename)
                    174:        char *filename;
                    175: {
                    176:        if (filename == NULL)
                    177:                return (edit_ifile(NULL_IFILE));
                    178:        return (edit_ifile(get_ifile(filename, curr_ifile)));
                    179: }
                    180:
                    181: /*
                    182:  * Edit a new file (given its IFILE).
                    183:  * ifile == NULL means just close the current file.
                    184:  */
                    185:        public int
                    186: edit_ifile(ifile)
                    187:        IFILE ifile;
                    188: {
                    189:        int f;
                    190:        int answer;
                    191:        int no_display;
                    192:        int chflags;
                    193:        char *filename;
                    194:        char *open_filename;
                    195:        char *alt_filename;
                    196:        void *alt_pipe;
                    197:        IFILE was_curr_ifile;
                    198:        PARG parg;
                    199:
                    200:        if (ifile == curr_ifile)
                    201:        {
                    202:                /*
                    203:                 * Already have the correct file open.
                    204:                 */
                    205:                return (0);
                    206:        }
                    207:
                    208:        /*
                    209:         * We must close the currently open file now.
                    210:         * This is necessary to make the open_altfile/close_altfile pairs
                    211:         * nest properly (or rather to avoid nesting at all).
                    212:         * {{ Some stupid implementations of popen() mess up if you do:
                    213:         *    fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }}
                    214:         */
                    215: #if LOGFILE
                    216:        end_logfile();
                    217: #endif
                    218:        was_curr_ifile = curr_ifile;
                    219:        if (curr_ifile != NULL_IFILE)
                    220:        {
                    221:                close_file();
                    222:        }
                    223:
                    224:        if (ifile == NULL_IFILE)
                    225:        {
                    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:                 */
                    232:                return (0);
                    233:        }
                    234:
                    235:        filename = get_filename(ifile);
                    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;
                    242:
                    243:        chflags = 0;
                    244:        if (alt_pipe != NULL)
                    245:        {
                    246:                /*
                    247:                 * The alternate "file" is actually a pipe.
                    248:                 * f has already been set to the file descriptor of the pipe
                    249:                 * in the call to open_altfile above.
                    250:                 * Keep the file descriptor open because it was opened
                    251:                 * via popen(), and pclose() wants to close it.
                    252:                 */
                    253:                chflags |= CH_POPENED;
                    254:        } else if (strcmp(open_filename, "-") == 0)
                    255:        {
                    256:                /*
                    257:                 * Use standard input.
                    258:                 * Keep the file descriptor open because we can't reopen it.
                    259:                 */
                    260:                f = fd0;
                    261:                chflags |= CH_KEEPOPEN;
                    262:        } else if ((parg.p_string = bad_file(open_filename)) != NULL)
                    263:        {
                    264:                /*
                    265:                 * It looks like a bad file.  Don't try to open it.
                    266:                 */
                    267:                error("%s", &parg);
                    268:                free(parg.p_string);
                    269:            err1:
                    270:                if (alt_filename != NULL)
                    271:                {
                    272:                        close_altfile(alt_filename, filename, alt_pipe);
                    273:                        free(alt_filename);
                    274:                }
                    275:                del_ifile(ifile);
                    276:                /*
                    277:                 * Re-open the current file.
                    278:                 */
                    279:                (void) edit_ifile(was_curr_ifile);
                    280:                return (1);
                    281:        } else if ((f = open(open_filename, OPEN_READ)) < 0)
                    282:        {
                    283:                /*
                    284:                 * Got an error trying to open it.
                    285:                 */
                    286:                parg.p_string = errno_message(filename);
                    287:                error("%s", &parg);
                    288:                free(parg.p_string);
                    289:                goto err1;
                    290:        } else if (!force_open && !opened(ifile) && bin_file(f))
                    291:        {
                    292:                /*
                    293:                 * Looks like a binary file.  Ask user if we should proceed.
                    294:                 */
                    295:                parg.p_string = filename;
                    296:                answer = query("\"%s\" may be a binary file.  See it anyway? ",
                    297:                        &parg);
                    298:                if (answer != 'y' && answer != 'Y')
                    299:                {
                    300:                        close(f);
                    301:                        goto err1;
                    302:                }
                    303:        }
                    304:
                    305:        /*
                    306:         * Get the new ifile.
                    307:         * Get the saved position for the file.
                    308:         */
                    309:        if (was_curr_ifile != NULL_IFILE)
                    310:                old_ifile = was_curr_ifile;
                    311:        curr_ifile = ifile;
                    312:        curr_altfilename = alt_filename;
                    313:        curr_altpipe = alt_pipe;
                    314:        set_open(curr_ifile); /* File has been opened */
                    315:        get_pos(curr_ifile, &initial_scrpos);
                    316:        new_file = TRUE;
                    317:        ch_init(f, chflags);
                    318: #if LOGFILE
                    319:        if (namelogfile != NULL && is_tty)
                    320:                use_logfile(namelogfile);
                    321: #endif
                    322:
                    323:        if (every_first_cmd != NULL)
                    324:                ungetsc(every_first_cmd);
                    325:
                    326:        no_display = !any_display;
                    327:        flush();
                    328:        any_display = TRUE;
                    329:
                    330:        if (is_tty)
                    331:        {
                    332:                /*
                    333:                 * Output is to a real tty.
                    334:                 */
                    335:
                    336:                /*
                    337:                 * Indicate there is nothing displayed yet.
                    338:                 */
                    339:                pos_clear();
                    340:                clr_linenum();
                    341: #if HILITE_SEARCH
                    342:                clr_hilite();
                    343: #endif
                    344:                if (no_display && errmsgs > 0)
                    345:                {
                    346:                        /*
                    347:                         * We displayed some messages on error output
                    348:                         * (file descriptor 2; see error() function).
                    349:                         * Before erasing the screen contents,
                    350:                         * display the file name and wait for a keystroke.
                    351:                         */
                    352:                        parg.p_string = filename;
                    353:                        error("%s", &parg);
                    354:                }
                    355:        }
                    356:        return (0);
                    357: }
                    358:
                    359: /*
                    360:  * Edit a space-separated list of files.
                    361:  * For each filename in the list, enter it into the ifile list.
                    362:  * Then edit the first one.
                    363:  */
                    364:        public int
                    365: edit_list(filelist)
                    366:        char *filelist;
                    367: {
                    368:        IFILE save_curr_ifile;
                    369:        char *good_filename;
                    370:        char *filename;
                    371:        char *gfilelist;
                    372:        char *gfilename;
                    373:        struct textlist tl_files;
                    374:        struct textlist tl_gfiles;
                    375:
                    376:        save_curr_ifile = curr_ifile;
                    377:        good_filename = NULL;
                    378:
                    379:        /*
                    380:         * Run thru each filename in the list.
                    381:         * Try to glob the filename.
                    382:         * If it doesn't expand, just try to open the filename.
                    383:         * If it does expand, try to open each name in that list.
                    384:         */
                    385:        init_textlist(&tl_files, filelist);
                    386:        filename = NULL;
                    387:        while ((filename = forw_textlist(&tl_files, filename)) != NULL)
                    388:        {
                    389:                gfilelist = glob(filename);
                    390:                init_textlist(&tl_gfiles, gfilelist);
                    391:                gfilename = NULL;
                    392:                while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL)
                    393:                {
                    394:                        if (edit(gfilename) == 0 && good_filename == NULL)
                    395:                                good_filename = get_filename(curr_ifile);
                    396:                }
                    397:                free(gfilelist);
                    398:        }
                    399:        /*
                    400:         * Edit the first valid filename in the list.
                    401:         */
                    402:        if (good_filename == NULL)
                    403:                return (1);
                    404:        if (get_ifile(good_filename, curr_ifile) == curr_ifile)
                    405:                /*
                    406:                 * Trying to edit the current file; don't reopen it.
                    407:                 */
                    408:                return (0);
                    409:        if (edit_ifile(save_curr_ifile))
                    410:                quit(QUIT_ERROR);
                    411:        return (edit(good_filename));
                    412: }
                    413:
                    414: /*
                    415:  * Edit the first file in the command line (ifile) list.
                    416:  */
                    417:        public int
                    418: edit_first()
                    419: {
                    420:        curr_ifile = NULL_IFILE;
                    421:        return (edit_next(1));
                    422: }
                    423:
                    424: /*
                    425:  * Edit the last file in the command line (ifile) list.
                    426:  */
                    427:        public int
                    428: edit_last()
                    429: {
                    430:        curr_ifile = NULL_IFILE;
                    431:        return (edit_prev(1));
                    432: }
                    433:
                    434:
                    435: /*
                    436:  * Edit the next file in the command line (ifile) list.
                    437:  */
                    438:        public int
                    439: edit_next(n)
                    440:        int n;
                    441: {
                    442:        IFILE h;
                    443:        IFILE next;
                    444:
                    445:        h = curr_ifile;
                    446:        /*
                    447:         * Skip n filenames, then try to edit each filename.
                    448:         */
                    449:        for (;;)
                    450:        {
                    451:                next = next_ifile(h);
                    452:                if (--n < 0)
                    453:                {
                    454:                        if (edit_ifile(h) == 0)
                    455:                                break;
                    456:                }
                    457:                if (next == NULL_IFILE)
                    458:                {
                    459:                        /*
                    460:                         * Reached end of the ifile list.
                    461:                         */
                    462:                        return (1);
                    463:                }
                    464:                h = next;
                    465:        }
                    466:        /*
                    467:         * Found a file that we can edit.
                    468:         */
                    469:        return (0);
                    470: }
                    471:
                    472: /*
                    473:  * Edit the previous file in the command line list.
                    474:  */
                    475:        public int
                    476: edit_prev(n)
                    477:        int n;
                    478: {
                    479:        IFILE h;
                    480:        IFILE next;
                    481:
                    482:        h = curr_ifile;
                    483:        /*
                    484:         * Skip n filenames, then try to edit each filename.
                    485:         */
                    486:        for (;;)
                    487:        {
                    488:                next = prev_ifile(h);
                    489:                if (--n < 0)
                    490:                {
                    491:                        if (edit_ifile(h) == 0)
                    492:                                break;
                    493:                }
                    494:                if (next == NULL_IFILE)
                    495:                {
                    496:                        /*
                    497:                         * Reached beginning of the ifile list.
                    498:                         */
                    499:                        return (1);
                    500:                }
                    501:                h = next;
                    502:        }
                    503:        /*
                    504:         * Found a file that we can edit.
                    505:         */
                    506:        return (0);
                    507: }
                    508:
                    509: /*
                    510:  * Edit a specific file in the command line (ifile) list.
                    511:  */
                    512:        public int
                    513: edit_index(n)
                    514:        int n;
                    515: {
                    516:        IFILE h;
                    517:
                    518:        h = NULL_IFILE;
                    519:        do
                    520:        {
                    521:                if ((h = next_ifile(h)) == NULL_IFILE)
                    522:                {
                    523:                        /*
                    524:                         * Reached end of the list without finding it.
                    525:                         */
                    526:                        return (1);
                    527:                }
                    528:        } while (get_index(h) != n);
                    529:
                    530:        return (edit_ifile(h));
                    531: }
                    532:
                    533: /*
                    534:  * Edit standard input.
                    535:  */
                    536:        public int
                    537: edit_stdin()
                    538: {
                    539:        if (isatty(fd0))
                    540:        {
                    541: #if MSOFTC || OS2
                    542:                error("Missing filename (\"less -?\" for help)", NULL_PARG);
                    543: #else
                    544:                error("Missing filename (\"less -\\?\" for help)", NULL_PARG);
                    545: #endif
                    546:                quit(QUIT_OK);
                    547:        }
                    548:        return (edit("-"));
                    549: }
                    550:
                    551: /*
                    552:  * Copy a file directly to standard output.
                    553:  * Used if standard output is not a tty.
                    554:  */
                    555:        public void
                    556: cat_file()
                    557: {
1.3     ! mpech     558:        int c;
1.1       etheisen  559:
                    560:        while ((c = ch_forw_get()) != EOI)
                    561:                putchr(c);
                    562:        flush();
                    563: }
                    564:
                    565: #if LOGFILE
                    566:
                    567: /*
                    568:  * If the user asked for a log file and our input file
                    569:  * is standard input, create the log file.
                    570:  * We take care not to blindly overwrite an existing file.
                    571:  */
                    572:        public void
                    573: use_logfile(filename)
                    574:        char *filename;
                    575: {
1.3     ! mpech     576:        int exists;
        !           577:        int answer;
1.1       etheisen  578:        PARG parg;
                    579:
                    580:        if (ch_getflags() & CH_CANSEEK)
                    581:                /*
                    582:                 * Can't currently use a log file on a file that can seek.
                    583:                 */
                    584:                return;
                    585:
                    586:        /*
                    587:         * {{ We could use access() here. }}
                    588:         */
                    589:        exists = open(filename, OPEN_READ);
                    590:        close(exists);
                    591:        exists = (exists >= 0);
                    592:
                    593:        /*
                    594:         * Decide whether to overwrite the log file or append to it.
                    595:         * If it doesn't exist we "overwrite" it.
                    596:         */
                    597:        if (!exists || force_logfile)
                    598:        {
                    599:                /*
                    600:                 * Overwrite (or create) the log file.
                    601:                 */
                    602:                answer = 'O';
                    603:        } else
                    604:        {
                    605:                /*
                    606:                 * Ask user what to do.
                    607:                 */
                    608:                parg.p_string = filename;
                    609:                answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg);
                    610:        }
                    611:
                    612: loop:
                    613:        switch (answer)
                    614:        {
                    615:        case 'O': case 'o':
                    616:                /*
                    617:                 * Overwrite: create the file.
                    618:                 */
                    619:                logfile = creat(filename, 0644);
                    620:                break;
                    621:        case 'A': case 'a':
                    622:                /*
                    623:                 * Append: open the file and seek to the end.
                    624:                 */
                    625:                logfile = open(filename, OPEN_APPEND);
                    626:                if (lseek(logfile, (off_t)0, 2) == BAD_LSEEK)
                    627:                {
                    628:                        close(logfile);
                    629:                        logfile = -1;
                    630:                }
                    631:                break;
                    632:        case 'D': case 'd':
                    633:                /*
                    634:                 * Don't do anything.
                    635:                 */
                    636:                return;
                    637:        case 'q':
                    638:                quit(QUIT_OK);
                    639:                /*NOTREACHED*/
                    640:        default:
                    641:                /*
                    642:                 * Eh?
                    643:                 */
                    644:                answer = query("Overwrite, Append, or Don't log? (Type \"O\", \"A\", \"D\" or \"q\") ", NULL_PARG);
                    645:                goto loop;
                    646:        }
                    647:
                    648:        if (logfile < 0)
                    649:        {
                    650:                /*
                    651:                 * Error in opening logfile.
                    652:                 */
                    653:                parg.p_string = filename;
                    654:                error("Cannot write to \"%s\"", &parg);
                    655:        }
                    656: }
                    657:
                    658: #endif