home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / CMDS / less_332.lzh / less_332 / edit.c < prev    next >
Text File  |  1998-03-03  |  16KB  |  782 lines

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