home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_100 / 183_01 / letter.c < prev    next >
C/C++ Source or Header  |  1986-02-04  |  21KB  |  817 lines

  1. /*
  2.  *  letter      A short, savage, document processor designed to serve one
  3.  *              function; format a one page letter.
  4.  *
  5.  *              I don't know about anybody else, but I write about 25 one-
  6.  *              pagers a week and I like my letters to look pretty. This
  7.  *              requires vertical centering and I've not found a word
  8.  *              processor yet that can do it (although qnxs' document
  9.  *              processor 'doc' did provide sufficient primitives to
  10.  *              write a REALLY complex macro that did and UNIXs' nroff and
  11.  *              a whole bunch of shell commands could do the job as well).
  12.  *
  13.  *              letter will vertically center a one page letter, will
  14.  *              divert the address to a file (suitable for printing on
  15.  *              an envelope by address) and support a small set of document
  16.  *              processing functions. It is weak on error checking and
  17.  *              semantic analysis (so don't push it!). See the separate
  18.  *              document 'letter.doc' for more detail.
  19.  *
  20.  *              This program took less than six hours to write. It is
  21.  *              probably far from perfect. I'm not offering this as a
  22.  *              replacement for nroff (or even WordStar), but it does
  23.  *              what I need acceptably.
  24.  *
  25.  *              This is thrown freely into the public domain and can be
  26.  *              used, modified, and distributed by any person or agency
  27.  *              that wants to. I would appreciate it if the original
  28.  *              author credit remains and would also request that no
  29.  *              compensation be charged by anyone passing this turkey
  30.  *              around.
  31.  *
  32.  *      This program was written by Jon Simkins and tested using the DeSmet C
  33.  *      compiler and MS-DOS 3.0.
  34.  *
  35.  *  Revision Notes
  36.  *
  37.  *      Date        Revision
  38.  *      --------------------
  39.  *      86.02.04    Added UL_ON and BF_ON control strings for printers that
  40.  *                  directly support underlining and boldface (most if them
  41.  *                  these days).
  42.  *                  Changed setDate() function for Wizard C and recompiled. js.
  43.  *      
  44.  *      Copyright (c) 1985 SoftFocus
  45.  *                         1343 Stanbury Dr.,
  46.  *                         Oakville, Ontario, Canada
  47.  *                         L6L 2J5
  48.  *                         (416)825-0903
  49.  *
  50.  *                      Compuserve: 76566, 1172
  51.  */
  52. #include <stdio.h>
  53. #include <bdos.h>      /* for getdate() structure                     */
  54.  
  55. #ifndef TRUE
  56. #define TRUE 1
  57. #define FALSE 0
  58. #endif
  59.  
  60. #define MAXLINE 200     /* maximum input line. Assumed larger than     */
  61.                         /* maximum print line.                         */
  62. #define LPP     66      /* maximum lines per page.                     */
  63. #define ERROR -1
  64. #define HEAD 6       /* lines left at top of page when paper is loaded */
  65.  
  66. /*
  67.  *  program defaults and printer controls.
  68.  *  If your printer cannot backspace one character, don't use underline or
  69.  *  boldface.
  70.  */
  71. #define DEFIN           0       /* default indent                          */
  72. #define DEFRM           65      /* default right margin                    */
  73. #define DEFPO           5      /* default page offset                     */
  74. #define DEFJU           FALSE   /* justification off                       */
  75. #define DEFFI           TRUE    /* fill on                                 */
  76.  
  77. #define FF              "\014"  /* string to formfeed the printer          */
  78. #define BACKSPACE       8       /* character to backspace the printer      */
  79. /*
  80.  *  Printer Control Strings
  81.  *  -----------------------
  82.  *  If your printer supports such niceties as automatic underlining and
  83.  *  boldface, then fill in the following defines in the logical manner.
  84.  *  If they are left undefined, then letter will underline by backspacing
  85.  *  and printing a '_' and boldface by doublestriking.
  86.  *  The given examples are for a Brother HR-35 daisywheel. This is disabled
  87.  *  in the distribution version.
  88.  */
  89. /* #define UL_ON    "\033E" *//* enable auto underline                  */
  90. /* #define UL_OFF   "\033R" *//* disable auto underline                 */
  91. /* #define BF_ON    "\033O" *//* enable auto boldface                   */
  92. /* #define BF_OFF   "\033&" *//* disable auto boldface                  */
  93.  
  94. FILE *fd;               /* main input file */
  95. FILE *fdAdd;            /* address file    */
  96. FILE *printer;          /* printer device  */
  97. int hardCopy;           /* true if printed */
  98. #define PRINTER "LPT1"  /* hardcopy output */
  99. #define CONSOLE "CON"   /* console output  */
  100.  
  101. /*
  102.  *  letter array, line buffer, and counters
  103.  */                  
  104. char *lines[LPP];           /* build the page here                      */
  105. int lineCount;
  106. char pLine[MAXLINE];        /* build the current line here              */
  107. int pCount;                                                              
  108. int saveJu, saveFi;         
  109.  
  110. /*
  111.  *  command array & defines
  112.  */
  113. #define COMCOUNT 13
  114. char *commands[] = {". ", "in", "po", "ju", "rm", "ti", "br",
  115.                     "as", "ae", "sp", "dt", "sg", "ce", "fi"};
  116. #define COMMENT     0
  117. #define INDENT      1
  118. #define PAGEOFF     2
  119. #define JUSTIFY     3
  120. #define RM          4
  121. #define TI          5
  122. #define BR          6
  123. #define AS          7
  124. #define AE          8
  125. #define SP          9
  126. #define DT          10
  127. #define SG          11
  128. #define CENTER      12
  129. #define FILL        13
  130.  
  131. /*
  132.  *  margins, switches.
  133.  *  There is currently no way to disable the interpretation of the characters
  134.  *  listed below.
  135.  */
  136. #define BC '^'      /* boldface toggle  */
  137. #define UC '_'      /* underline toggle */
  138. #define HS '~'      /* 'hard' space (not spread by justification)          */
  139.  
  140. int rm, in, ti, ju, po, fi, br, sp, ce;
  141. int addFlag;                    /* whether address redirection in effect   */
  142. int ul, bf;                     /* underline, boldface currently selected  */
  143.  
  144.  
  145. /*
  146.  *  off and away
  147.  */
  148. main(argc, argv)
  149. int argc;
  150. char *argv[];
  151. {
  152.     char buff[MAXLINE], word[MAXLINE];
  153.     int index, len;
  154.     char *pr;
  155.     register j;
  156.  
  157.     /*
  158.      *  check argument count
  159.      */
  160.     if (argc > 3 || argc < 2)
  161.         erExit("usage: letter [-p] <fName>");
  162.  
  163.     /*
  164.      *  get command line option(s)
  165.      */
  166.     j=1;
  167.     if (argv[j][0] == '-' && argv[j][1] == 'p') {
  168.         pr = PRINTER;
  169.         hardCopy = TRUE;
  170.         j++;
  171.     }
  172.     else {
  173.         pr = CONSOLE;
  174.         hardCopy = FALSE;
  175.     }
  176.  
  177.     /*
  178.      *  open input file
  179.      */
  180.     if ( (fd=fopen(argv[j], "r")) == 0) {
  181.         printf("Could not open '%s'\n", argv[j]);
  182.         exit(ERROR);
  183.     }
  184.  
  185.     /*
  186.      *  set defaults
  187.      */
  188.     ul = bf = addFlag = FALSE;
  189.     in = DEFIN;
  190.     rm = DEFRM;
  191.     ju = DEFJU;
  192.     fi = DEFFI;
  193.     if (hardCopy)
  194.         po = DEFPO;
  195.     else
  196.         po = 0;
  197.  
  198.     ti = ce = lineCount = 0;
  199.  
  200.     /*
  201.      *  input/process loop
  202.      */
  203.     pCount = 0;
  204.     pLine[0] = '\0';
  205.     while( fgets(buff, MAXLINE, fd) != NULL) {
  206.         trim(buff);
  207.         /*
  208.          *  blank line?
  209.          */
  210.         if (buff[0] == '\0') {
  211.             pFlush(FALSE);
  212.             pCount=1;
  213.             pFlush(FALSE);
  214.             continue;
  215.         }
  216.  
  217.         /*
  218.          *  check for any command lines
  219.          */
  220.         if (buff[0] == '.') {
  221.             dotComm(buff);
  222.             continue;
  223.         }
  224.  
  225.         /*
  226.          *  if fill mode is off, just pass the line on through.
  227.          */
  228.         if ( !(fi)) {
  229.             strcpy(pLine, buff);
  230.             pCount = lenCal(pLine);
  231.             pFlush(ju);
  232.         }
  233.         else {
  234.         /*
  235.          *  must be text; add it (word at a time) to the current print
  236.          *  line.
  237.          */
  238.             index=0;
  239.             do { 
  240.                 index = getWord(index, buff, word);
  241.                 if (index == 0)
  242.                     continue;
  243.                 len = lenCal(word);
  244.                 /*
  245.                  *  if there's still room, add word to current line
  246.                  */
  247.                 if ( ( len + pCount) < (rm-in-ti) ) {
  248.                     if (pCount != 0) {
  249.                         strcat( pLine, " ");
  250.                         pCount++;
  251.                     }
  252.                     strcat( pLine, word);
  253.                     pCount += len;
  254.                 }
  255.                 /*
  256.                  * 'print' current line and start new one.
  257.                  */
  258.                 else {
  259.                     pFlush(ju);
  260.                     strcpy(pLine, word);
  261.                     pCount=len;
  262.                 }
  263.             } while (index);
  264.         }
  265.     }
  266.     pFlush(FALSE);
  267.     fclose(fd);
  268.  
  269.     /*
  270.      *  all text is in internal text buffer nicely formatted.
  271.      *  Now center it on the page and print it.
  272.      */
  273.  
  274.     /*
  275.      *  strip off any trailing blank lines
  276.      */
  277.     while ( trim(lines[lineCount][0]) == '\0')
  278.         lineCount--;
  279.  
  280.     /*
  281.      *  center the text vertically
  282.      */
  283.     printer = fopen(pr, "w");
  284.     len = ( (LPP-lineCount) / 2) - HEAD + 1;
  285.     if (hardCopy)
  286.         for(j=0; j<=len; j++)
  287.             lprint("\n");
  288.     
  289.     /*
  290.      *  ... and print it all
  291.      */
  292.     for(j=0; j<= lineCount; j++) {
  293.         lprint(lines[j]);
  294.         lprint("\015\n");
  295.         free(lines[j]);
  296.     }
  297.     if (hardCopy)
  298.         lprint(FF);
  299.     fclose(printer);
  300. }
  301.  
  302. /*
  303.  *  trim    Strip off all trailing white spaces
  304.  */
  305. trim(str)
  306. char *str;
  307. {
  308.     register j;
  309.  
  310.     for( j=strlen(str); (str[j] <= ' ') && (j > -1); j--);
  311.     str[++j] = '\0';
  312. }
  313.  
  314. /*
  315.  *  strip       Pull off all leading spaces.
  316.  *              Stripped string is returned in place.
  317.  */
  318. strip(str)
  319. char *str;
  320. {
  321.     register j, k;
  322.  
  323.     for(j=0; (str[j] <= ' ') && (str[j]); j++);
  324.     if ( !(j) )
  325.         return;
  326.     k=0;
  327.     do {
  328.         str[k++] = str[j];
  329.     } while (str[j++]);
  330. }
  331.  
  332. /*
  333.  *  erExit      Print an error message and die
  334.  */
  335. erExit(str)
  336. char *str;
  337. {
  338.     printf("%s\n", str);
  339.     exit(ERROR);
  340. }
  341.  
  342. /*
  343.  * getWord  Pull the next word off the passed string. Return an index into
  344.  *          the source string pointing to the next word.
  345.  */
  346. getWord(index, buff, word)
  347. int index;
  348. char *buff, *word;
  349. {
  350.     register j = 0;
  351.     
  352.     while ( buff[index] <= ' ' ) {
  353.         if (buff[index] == '\0') {
  354.             word[0] = '\0';
  355.             return 0;
  356.         }
  357.         index++;
  358.     }
  359.     while ( (buff[index] != ' ') && (buff[index] != '\0') )
  360.         word[j++] = buff[index++];
  361.     word[j++] = '\0';
  362.     return index;
  363. }
  364.  
  365. /*
  366.  *  pFlush  Add the current print line to lines[].
  367.  */
  368. pFlush(just)
  369. int just;
  370. {
  371.     register j;
  372.     char *malloc();
  373.  
  374.     if (pCount == 0)
  375.         return;
  376.     if (lineCount == LPP)
  377.         erExit("Page overflow! This utility only works for one page letters.");
  378.  
  379.     /*
  380.      *  insert page offset, indent
  381.      */
  382.     trim(pLine);
  383.     if (ce) {
  384.         strip(pLine);
  385.         center(pLine, rm-in-ti);
  386.         ce--;
  387.     }
  388.     else
  389.     if (just)
  390.         justify(pLine, rm-in-ti);
  391.  
  392.     lines[lineCount] = malloc( MAXLINE );
  393.     for(j=0; j<po+in+ti; j++)
  394.         lines[lineCount][j] = ' ';
  395.     ti=0;
  396.     strcpy( &lines[lineCount++][j], pLine);     
  397.     if (addFlag)
  398.         fprintf(fdAdd, "%s\n", pLine);
  399.  
  400.     pCount = 0;
  401.     pLine[0] = '\0';
  402. }
  403.  
  404. /*
  405.  *  justify     Right justify a line in place. This is a fairly sleazy
  406.  *              algorithm, but I don't often need justification. Hackers
  407.  *              note!
  408.  */
  409. justify(line, margin)
  410. char *line;
  411. int margin;
  412. {
  413.     int lp=0, rp=0, sp, len;
  414.  
  415.     len=strlen(line);
  416.     margin += len - lenCal(line);
  417.     sp = margin - len;
  418.     lp=0; rp=len;
  419.  
  420.     while (sp) {
  421.  
  422.         /*
  423.          *  find first space from current left position
  424.          */
  425.         while(line[lp] != ' ') {
  426.             if (line[lp] == '\0') {
  427.                 lp=0;
  428.                 break;
  429.             }
  430.             lp++;
  431.         }
  432.         /*
  433.          *  add a space and find next non-blank
  434.          */
  435.         if (lp) {
  436.             addSpace(line, lp, 1);
  437.             len++;
  438.             sp--;
  439.             while ( (line[lp] <=' ') && (line[lp]) )
  440.                 lp++;
  441.         }
  442.         if (sp) {
  443.             while (line[rp] != ' ') {
  444.                 rp--;
  445.                 if (rp < 0) {
  446.                     rp=len;
  447.                     break;
  448.                 }
  449.             }
  450.             if (rp != len) {
  451.                 addSpace(line, rp, 1);
  452.                 len++;
  453.                 sp--;
  454.                 while (line[rp] <=' ' && (rp > -1) )
  455.                     rp--;
  456.                 if (rp < 0)
  457.                     rp=len;
  458.             }
  459.         }
  460.     }
  461. }
  462.  
  463. /*
  464.  *  Center      Center the given string in place using the supplied
  465.  *              line length.
  466.  */
  467. center(str, len)
  468. char *str;
  469. int len;
  470. {
  471.     len = (len - lenCal(str)) / 2;
  472.     addSpace(str, 0, len);
  473. }
  474.  
  475. /*
  476.  *  addSpace    Add <count> spaces at the given position.
  477.  */
  478. addSpace(str, pos, count)
  479. char *str;
  480. int pos;
  481. int count;
  482. {
  483.     register j;
  484.  
  485.     if (count <= 0)
  486.         return;
  487.  
  488.     for ( j=strlen(str)+count; j>pos; j--)
  489.         str[j] = str[j-count];
  490.     for (j=0; j<count; j++)
  491.         str[pos++]=' ';
  492. }
  493.  
  494. /*
  495.  *  dotComm     Process a command line
  496.  */
  497. dotComm(buff)
  498. char *buff;
  499. {
  500.     char scratch[3];
  501.     register j;
  502.     int action = ERROR;
  503.     int relative, value;
  504.  
  505.     for(j=1; j<3; j++)
  506.         scratch[j-1] = buff[j];
  507.     scratch[2] = '\0';
  508.     for(j=0; j <= COMCOUNT; j++) {
  509.         if (strcmp(commands[j], scratch) == 0) {
  510.             action = j;
  511.             break;
  512.         }
  513.     }
  514.     if (action == ERROR)
  515.         return;
  516.  
  517.     if (buff[4] == '+' || buff[4] == '-') {
  518.         relative = TRUE;
  519.         j=atoi(&buff[5]);
  520.         if (buff[4] == '-')
  521.             j *= -1;
  522.     }
  523.     else {
  524.         relative = FALSE;
  525.         j = atoi(&buff[4]);
  526.     }
  527.  
  528.     switch (action) {
  529.  
  530.         case COMMENT:
  531.             break;
  532.  
  533.         case INDENT:
  534.             if (relative)
  535.                 value = in+j;
  536.             else
  537.                 value = j;
  538.             if ( !(value <0 || value >rm)) {
  539.                 pFlush(FALSE);
  540.                 in = value;
  541.             }
  542.             break;           
  543.                 
  544.         case TI:
  545.             if (relative)
  546.                 ti = j;
  547.             else
  548.                 ti = in - j;
  549.             pFlush(FALSE);
  550.             break;           
  551.  
  552.         case PAGEOFF:
  553.             if ( !(hardCopy) )
  554.                 break;
  555.             if (relative)
  556.                 value = po+j;
  557.             else
  558.                 value = j;
  559.             if ( !(value<0 || value > 100)) {
  560.                 pFlush(FALSE);
  561.                 po = value;
  562.             }
  563.             break;
  564.  
  565.         case CENTER:
  566.             pFlush(FALSE);
  567.             strip(&buff[3]);
  568.             ce = j;
  569.             if (j == 0 && (buff[3] == '\0') )
  570.                 ce = 1;
  571.             break;
  572.         
  573.         case JUSTIFY:
  574.             pFlush(FALSE);
  575.             strip(&buff[3]);
  576.             trim( &buff[3]);
  577.             if (strcmp( &buff[3], "on") )
  578.                 ju = FALSE;
  579.             else
  580.                 ju = TRUE;
  581.             break;
  582.  
  583.         case RM:
  584.             if (relative)
  585.                 value = rm+j;
  586.             else
  587.                 value = j;
  588.             if ( !(value<10 || value >100) ) {
  589.                 pFlush(FALSE);
  590.                 rm=value;
  591.             }
  592.             break;
  593.  
  594.         case BR:
  595.             pFlush(FALSE);
  596.             break;
  597.  
  598.         case AS:
  599.             if (addFlag)
  600.                 break;
  601.             pFlush(FALSE);
  602.             saveFi = fi;
  603.             saveJu = ju;
  604.             fi = FALSE;
  605.             ju = FALSE;
  606.             if ( (fdAdd = fopen("address", "w")) == NULL)
  607.                 erExit("Could not open address file.");
  608.             addFlag = TRUE;
  609.             break;
  610.  
  611.         case AE:
  612.             if (addFlag != TRUE)
  613.                 erExit("Address redirection not in effect!");
  614.             fclose(fdAdd);
  615.             pFlush(FALSE);
  616.             fi = saveFi;
  617.             ju = saveJu;
  618.             addFlag = FALSE;
  619.             break;
  620.  
  621.         case FILL:
  622.             pFlush(FALSE);
  623.             strip(&buff[3]);
  624.             trim( &buff[3]);
  625.             if (strcmp( &buff[3], "on") )
  626.                 fi = FALSE;
  627.             else
  628.                 fi = TRUE;
  629.             break;
  630.  
  631.         case SP:
  632.             pFlush(FALSE);
  633.             value=0;
  634.             do {
  635.                 pCount=1;
  636.                 pFlush(FALSE);
  637.             } while (++value < j);
  638.             break;
  639.  
  640.         case DT:
  641.             pFlush(FALSE);
  642.             setDate(TRUE);
  643.             pFlush(FALSE);
  644.             break;
  645.  
  646.         case SG:
  647.             pFlush(FALSE);
  648.             strcpy(pLine, "Sincerely yours,");
  649.             pCount=1;
  650.             pFlush(FALSE);
  651.             pCount=1;
  652.             pFlush(FALSE);
  653.             pCount=1;
  654.             pFlush(FALSE);
  655.             pCount=1;
  656.             pFlush(FALSE);
  657.             trim(&buff[4]);
  658.             strip(&buff[4]);
  659.             strcpy(pLine, &buff[4]);
  660.             pCount=1;
  661.             pFlush(FALSE);
  662.             break;
  663.         
  664.         default:
  665.             break;
  666.     }
  667. }
  668.  
  669. /*
  670.  *  setDate     Fill in the current system date in pLine.
  671.  *              The DeSmet C function dates() is used here to snag
  672.  *              the system date; other compilers will need other functions.
  673.  */
  674. setDate(right)
  675. int right;
  676. {
  677.     int month, day, year;
  678.     struct date db;
  679.     char dt[25];
  680.     register j;
  681.     static char *mTable[] = {"January", "February", "March",
  682.                              "April", "May", "June",
  683.                              "July", "August", "September",
  684.                              "October", "November", "December"};
  685.     getdate(&db);
  686.     month = db.da_mon;
  687.     day = db.da_day;
  688.     year = db.da_year;
  689.     sprintf(dt, "%s %d, %d", mTable[month-1], day, year);
  690.     if (right) {
  691.         for(j=0; j<rm; j++)
  692.             pLine[j] = ' ';
  693.         strcpy( &pLine[ rm - strlen(dt) ], dt);
  694.     }
  695.     else
  696.         strcpy(pLine, dt);
  697.     pCount = strlen(pLine);
  698. }
  699.  
  700. /*
  701.  *  lenCal      Calculate the length of a string disregarding boldface
  702.  *              and underline command characters.
  703.  */
  704. lenCal(str)
  705. char *str;
  706. {
  707.     register j, len;
  708.  
  709.     for(j=0, len=0; str[j] != '\0'; j++)
  710.         if ( (str[j] != UC) && (str[j] != BC) )
  711.             len++;
  712.  
  713.     return len;
  714. }
  715.  
  716. /*
  717.  *  lprint  Print the string; enable/disable necessary print controls
  718.  */
  719. lprint(str)
  720. char *str;
  721. {
  722.     register j;
  723.  
  724.     /*
  725.      *  some printers disable all print attributes at the beginning
  726.      *  of each new line. To handle this, boldface and underline are
  727.      *  reasserted at the beginning of each print line if they're
  728.      *  currently enabled.
  729.      */
  730. #ifdef UL_ON
  731.     if (ul)
  732.         pstr(UL_ON);
  733. #endif
  734.  
  735. #ifdef BF_ON
  736.     if (bf)
  737.         pstr(BF_ON);
  738. #endif
  739.  
  740.     /*
  741.      *  print the line
  742.      */
  743.     for(j=0; str[j]; j++) {
  744.  
  745.         switch (str[j]) {
  746.  
  747.             case UC:
  748.                 if (ul) {
  749. #ifdef UL_ON
  750.                     pstr(UL_OFF);
  751. #endif
  752.                     ul=FALSE;
  753.                 }
  754.                 else {
  755. #ifdef UL_ON
  756.                     pstr(UL_ON);
  757. #endif
  758.                     ul=TRUE;
  759.                 }
  760.                 break;
  761.  
  762.             case BC:
  763.                 if (bf) {
  764. #ifdef BF_ON
  765.                     pstr(BF_OFF);
  766. #endif
  767.                     bf = FALSE;
  768.                 }
  769.                 else {
  770. #ifdef BF_ON
  771.                     pstr(BF_ON);
  772. #endif
  773.  
  774.                     bf = TRUE;
  775.                 }
  776.                 break;
  777.  
  778.             case HS:
  779.                 putc(' ', printer);
  780.                 break;
  781.             
  782.             default:
  783.                 if ( (str[j] > ' ') ) {
  784. #ifndef BF_ON
  785.                     if (bf) {
  786.                         putc(str[j], printer);
  787.                         putc(BACKSPACE, printer);
  788.                     }
  789. #endif
  790. #ifndef UL_ON
  791.                     if (ul) {
  792.                         putc('_', printer);
  793.                         putc(BACKSPACE, printer);
  794.                     }
  795. #endif
  796.                 }
  797.                 putc(str[j], printer);
  798.                 break;
  799.         }
  800.     }
  801. }
  802.  
  803. /*
  804.  *  pstr    Print the given string to the hardcopy device or not at all
  805.  *          if we're hitting the console.
  806.  */
  807. pstr(str)
  808. char *str;
  809. {
  810.     if ( !(hardCopy) )
  811.         return;
  812.  
  813.     while (*str)
  814.         putc(*str++, printer);
  815. }
  816.         
  817.