home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / top2src.zip / OUTPUT.C < prev    next >
C/C++ Source or Header  |  2000-07-13  |  33KB  |  894 lines

  1. /******************************************************************************
  2. OUTPUT.C     Screen output function.
  3.  
  4.     Copyright 1993 - 2000 Paul J. Sidorsky
  5.  
  6.     This program is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License, version 2, as
  8.     published by the Free Software Foundation.
  9.  
  10.     This program is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.  
  15.     You should have received a copy of the GNU General Public License
  16.     along with this program; if not, write to the Free Software
  17.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18.  
  19. This module contains TOP's custom screen output function, which is capable
  20. of processing PubColour codes and language tokens.  It is also capable of
  21. outputting to strings for log output or message sending.
  22. ******************************************************************************/
  23.  
  24. #include "top.h"
  25.  
  26. /* top_output() - Screen output function.
  27.    Parameters:  outmode - Output mode (OUT_ constant).
  28.                 string - String to output.
  29.                 ... - Language item parameter(s) (see notes).
  30.    Returns:  Pointer to global outputstr variable.
  31.    Notes:  This is a variable-parameter function.  After string (the main
  32.            output string, up to 9 additional strings may be specified.
  33.            These will be inserted into the output wherever language
  34.            parameter tokens (@1, @2, etc.) appear.  They may not be used
  35.            at all depending on the contents of string.  Integers or other
  36.            number CANNOT BE SPECIFIED!  To include an int as a parameter,
  37.            convert it with itoa() or a similar function.  The global array
  38.            of outnum strings can be used to hold these conversions without
  39.            having to allocate extra space.  Only two parameters are
  40.            required.  Although this function always returns a pointer to
  41.            outputstr, outputstr will only be updated with the new output if
  42.            OUT_STRING or OUT_STRINGNF modes were specified.  This function
  43.            uses the outproc... global flags to control code output.
  44. */
  45. unsigned char *top_output(char outmode, unsigned char *string, ...)
  46. {
  47. va_list oap; /* System pointer to variable arguments. */
  48. unsigned char *argptrs[9] = /* Variable argument holders. */
  49.     { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
  50. /* Counter, number of variable arguments, argument number holder. */
  51. char odd, nargs, argnum;
  52. /* Counter, counter, word wrap counter, memory/length counter, temporary
  53.    attribute holder. */
  54. XINT d, e, owwc, uu, tatr;
  55. unsigned long lentmp; /* Temporary length holder. */
  56. unsigned char oww[55]; /* Output word wrap buffer. */
  57. unsigned char *lenbuf = NULL; /* Parameter length restriction buffer. */
  58. XINT tmulti; /* Not used. */
  59. struct text_info ti; /* Screen attribute information. */
  60. /* OUT_STRINGNF mode flag, OUT_EMULATE mode flag. */
  61. char stringnf = 0, emu = 0;
  62.  
  63. /* This is a complicated and extemely lengthy function that should not be
  64.    messed with unless you have a thorough understanding of all of TOP's
  65.    codes, the door kit output functions, and string and memory handling. */
  66.  
  67. /* OUT_STRINGNF functions identically to OUT_STRING in most places so
  68.    a flag is set and OUT_STRING mode is forced. */
  69. if (outmode == OUT_STRINGNF)
  70.     {
  71.     stringnf = 1;
  72.     outmode = OUT_STRING;
  73.     }
  74. /* OUT_EMULATE functions identically to OUT_SCREEN in most places so
  75.    a flag is set and OUT_SCREEN mode is forced. */
  76. if (outmode == OUT_EMULATE)
  77.     {
  78.     emu = 1;
  79.     outmode = OUT_SCREEN;
  80.     }
  81.  
  82. /* Initialize the argument holders. */
  83. for (d = 0; d < 9; d++)
  84.     {
  85.     argptrs[d] = NULL;
  86.     }
  87.  
  88. /* This loop ascertains the highest number argument the output string
  89.    requires. */
  90. for (d = 0, nargs = 0; d < strlen(string); d++)
  91.     {
  92.     /* Look for @# codes that are higher than the current highest known
  93.        code to exist. */
  94.     if (string[d] == '@' && isdigit(string[d + 1]) &&
  95.         (string[d + 1] - '0' > nargs))
  96.         {
  97.         /* Set the new high arg number. */
  98.         nargs = string[d + 1] - '0';
  99.         }
  100.     }
  101.  
  102. /* Grab all of the arguments. */
  103. va_start(oap, string);
  104. for (odd = 0; odd < nargs; odd++)
  105.     {
  106.     argptrs[odd] = va_arg(oap, unsigned char *);
  107.     }
  108. va_end(oap);
  109.  
  110. /* Clear the word wrap buffer if a screen mode or local mode is being used. */
  111. if (outmode == OUT_SCREEN || outmode == OUT_LOCAL)
  112.     {
  113.     memset(oww, 0, 55);
  114.     }
  115. /* Clear the output string if a string mode is being used. */
  116. if (outmode == OUT_STRING)
  117.     {
  118.     memset(outputstr, 0, 513);
  119.     }
  120.  
  121. /* Loop through and process the entire string. */
  122. for (d = 0, owwc = 0; d < strlen(string); d++)
  123.     {
  124.     /* PubColour codes.  Only process if the global flag is on. */
  125.     if (string[d] == '^' && outproccol)
  126.         {
  127.         /* Copy the ^ character in non-filtered mode. */
  128.         if (outmode == OUT_STRING && stringnf)
  129.             {
  130.             strncat(outputstr, &string[d], 1);
  131.             }
  132.         /* We're now working with the character after the ^. */
  133.         d++;
  134.         /* Foreground colour change (lower case). */
  135.         if (string[d] >= 'a' && string[d] <= 'p')
  136.             {
  137.             /* Change the screen colour in screen mode, preserving the
  138.                current background. */
  139.             if (outmode == OUT_SCREEN)
  140.                 {
  141.                 od_set_attrib((od_control.od_cur_attrib & 0xF0) +
  142.                               (string[d] - 'a'));
  143.                 }
  144.             /* Force the attribute colour in local mode, preserving the
  145.                current background. */
  146.             if (outmode == OUT_LOCAL)
  147.                 {
  148.                 gettextinfo(&ti);
  149.                 textattr((ti.attribute & 0xF0) +
  150.                          (string[d] - 'a'));
  151.                 }
  152.             /* Copy the code in non-filtered mode. */
  153.             if (outmode == OUT_STRING && stringnf)
  154.                 {
  155.                 strncat(outputstr, &string[d], 1);
  156.                 }
  157.             continue;
  158.             }
  159.         /* Background colour change (upper case). */
  160.         if (string[d] >= 'A' && string[d] <= 'P')
  161.             {
  162.             /* Change the screen colour in screen mode, preserving the
  163.                current foreground. */
  164.             if (outmode == OUT_SCREEN)
  165.                 {
  166.                 od_set_attrib((od_control.od_cur_attrib & 0x0F) +
  167.                               ((string[d] - 'A') << 4));
  168.                 }
  169.             /* Force the attribute colour in local mode, preserving the
  170.                current foreground. */
  171.             if (outmode == OUT_LOCAL)
  172.                 {
  173.                 gettextinfo(&ti);
  174.                 textattr((ti.attribute & 0x0F) +
  175.                          ((string[d] - 'A') << 4));
  176.                 }
  177.             /* Copy the code in non-filtered mode. */
  178.             if (outmode == OUT_STRING && stringnf)
  179.                 {
  180.                 strncat(outputstr, &string[d], 1);
  181.                 }
  182.             continue;
  183.             }
  184.         if (string[d] != '^')
  185.             {
  186.             /* Any character but another ^ forces the default attribute.
  187.                The default attribute is not configurable at this time. */
  188.             if (outmode == OUT_SCREEN)
  189.                 {
  190.                 od_set_attrib(outdefattrib);
  191.                 }
  192.             continue;
  193.             }
  194.         /* A second ^ passes through to be displayed to the screen. */
  195.         }
  196.     /* Language codes.  Only process if the global flag is on. */
  197.     if (string[d] == '@' && outproclang)
  198.         {
  199.         /* We're now working with the character after the @. */
  200.         d++;
  201.         /* Language parameter. */
  202.         if (string[d] >= '1' && string[d] <= '9')
  203.             {
  204.             /* Grab the requested parameter number. */
  205.             argnum = string[d] - '1';
  206.             /* By definition argnum will be less than nargs because we
  207.                tested all @# codes above, but it's checked in case. */
  208.             if (argnum < nargs)
  209.                 {
  210.                 /* Process all following & codes which control code
  211.                    processing during parameter display. */
  212.                 while(string[d + 1] == '&' && d < strlen(string))
  213.                     {
  214.                     /* Jump to the character after the &. */
  215.                     d += 2;
  216.                     /* String modes copy the & code directly. */
  217.                     if (outmode == OUT_STRING)
  218.                         {
  219.                         char ttss[5];
  220.                         sprintf(ttss, "%c%c", string[d - 1], string[d]);
  221.                         strcat(outputstr, ttss);
  222.                         }
  223.                     else
  224.                         {
  225.                         /* Action code disable (not currently used in this
  226.                            function). */
  227.                         if (toupper(string[d]) == 'A') outprocact = FALSE;
  228.                         /* Language code disable. */
  229.                         if (toupper(string[d]) == 'L') outproclang = FALSE;
  230.                         /* PubColour code disable. */
  231.                         if (toupper(string[d]) == 'C') outproccol = FALSE;
  232.                         }
  233.                     }
  234.  
  235.                 /* Find out if the language item calls for a fixed or
  236.                    limited length. */
  237.                 lentmp = outchecksuffix(&string[d + 1]);
  238.                 if (lentmp > 0L)
  239.                     {
  240.                     /* Length wanted. */
  241.                     unsigned XINT lenwant = lentmp & 0x000000FFL;
  242.                     XINT xmem = 0; /* Extra memory counter. */
  243.  
  244.                     /* Loop through the parameter string and count all of
  245.                        the colour codes, adding 2 extra memory bytes for
  246.                        each. */
  247.                     for (uu = 0; uu < strlen(argptrs[argnum]);
  248.                          uu++)
  249.                         {
  250.                         if (argptrs[argnum][uu] == '^')
  251.                             {
  252.                             /* Skip to the character after the ^. */
  253.                             uu++;
  254.                             xmem += 2;
  255.                             }
  256.                         }
  257.  
  258.                     /* Increase the size of the wanted length. */
  259.                     lenwant += xmem;
  260.                     /* Grab the requested length of memory. */
  261.                     lenbuf = malloc(lenwant + 1);
  262.                     if (lenbuf)
  263.                         {
  264.                         /* Clear the temporary buffer.  It's filled with
  265.                            zeros if that was requested, otherwise spaces
  266.                            are used. */
  267.                         if (lentmp & 0x00020000L)
  268.                             {
  269.                             memset(lenbuf, '0', lenwant);
  270.                             }
  271.                         else
  272.                             {
  273.                             memset(lenbuf, ' ', lenwant);
  274.                             }
  275.                         lenbuf[lenwant] = '\0';
  276.                         if (lentmp & 0x00010000L)
  277.                             {
  278.                             /* Left justification. */
  279.                             if (strlen(argptrs[argnum]) >
  280.                                 lenwant)
  281.                                 {
  282.                                 /* Truncate the parameter string if it is
  283.                                    too long. */
  284.                                 memcpy(lenbuf, argptrs[argnum],
  285.                                        lenwant);
  286.                                 }
  287.                             else
  288.                                 {
  289.                                 /* Copy the string in its entirety.
  290.                                    memcpy() is used because we don't want a
  291.                                    \0 after the string is copied. */
  292.                                 memcpy(lenbuf, argptrs[argnum],
  293.                                        strlen(argptrs[argnum]));
  294.                                 }
  295.                             }
  296.                         else
  297.                             {
  298.                             /* Right justification. */
  299.                             if (strlen(argptrs[argnum]) >
  300. 7                                lenwant)
  301.                                 {
  302.                                 /* Truncate the parameter string if it is
  303.                                    too long.  No compensation for right
  304.                                    justification is needed since the
  305.                                    parameter will fill the entire buffer. */
  306.                                 memcpy(lenbuf, argptrs[argnum],
  307.                                        lenwant);
  308.                                 }
  309.                             else
  310.                                 {
  311.                                 /* Copy the entire string right justified. */
  312.                                 memcpy(&lenbuf[lenwant -
  313.                                        strlen(argptrs[argnum])],
  314.                                        argptrs[argnum],
  315.                                        strlen(argptrs[argnum]));
  316.                                 }
  317.                             }
  318.                         /* Screen and local modes make a recursive call to
  319.                            top_output() to output the parameter. */
  320.                         if (outmode == OUT_SCREEN)
  321.                             {
  322.                             top_output(OUT_SCREEN, lenbuf);
  323.                             }
  324.                         if (outmode == OUT_LOCAL)
  325.                             {
  326.                             top_output(OUT_LOCAL, lenbuf);
  327.                             }
  328.                         /* String modes copy the parameter directly. */
  329.                         if (outmode == OUT_STRING)
  330.                             {
  331.                             strcat(outputstr, lenbuf);
  332.                             }
  333.                         dofree(lenbuf);
  334.                         }
  335.                     /* Increment the counter the length of the actual
  336.                        parameter code. */
  337.                     d += ((lentmp & 0xFF00) >> 8);
  338.                     /* Reactivate all code processing flags. */
  339.                     outproccol = outproclang = outprocact = TRUE;
  340.                     continue;
  341.                     }
  342.                 else
  343.                     {
  344.                     /* User doesn't care about length. */
  345.  
  346.                     /* Screen and local modes make a recursive call to
  347.                        top_output() to output the parameter. */
  348.                     if (outmode == OUT_SCREEN)
  349.                         {
  350.                         top_output(OUT_SCREEN, argptrs[argnum]);
  351.                         }
  352.                     if (outmode == OUT_LOCAL)
  353.                         {
  354.                         top_output(OUT_LOCAL, argptrs[argnum]);
  355.                         }
  356.                     /* String modes copy the parameter directly. */
  357.                     if (outmode == OUT_STRING)
  358.                         {
  359.                         strcat(outputstr, argptrs[argnum]);
  360.                         }
  361.                     /* Reactivate all code processing flags. */
  362.                     outproccol = outproclang = outprocact = TRUE;
  363.                     continue;
  364.                     }
  365.                 }
  366.             else
  367.                 {
  368.                 /* Skip invalid parameter numbers. */
  369.                 continue;
  370.                 }
  371.             }
  372.         /* Destructive backspace. */
  373.         if (toupper(string[d]) == '<')
  374.             {
  375.             /* Screen and local modes display a destructive backspace. */
  376.             if (outmode == OUT_SCREEN)
  377.                 {
  378.                 od_disp_str("\b \b");
  379.                 }
  380.             if (outmode == OUT_LOCAL)
  381.                 {
  382.                 cputs("\b \b");
  383.                 }
  384.             /* String modes just copy the backspace, assuming it will
  385.                be properly handled later by whatever uses it. */
  386.             if (outmode == OUT_STRING)
  387.                 {
  388.                 strcat(outputstr, "\b");
  389.                 }
  390.             continue;
  391.             }
  392.         /* Beep. */
  393.         if (toupper(string[d]) == 'B')
  394.             {
  395.             char locecho = 0; /* Whether to echo beep locally. */
  396.  
  397.             /* Screen modes use the configured setting to determine if the
  398.                beep should be sounded on the local console. */
  399.             if (outmode == OUT_SCREEN)
  400.                 {
  401.                 /* Remote node type. */
  402.                 if (!localmode && !lanmode && od_control.baud != 0)
  403.                     {
  404.                     locecho = (cfg.localbeeping & BEEP_REMOTE);
  405.                     }
  406.                 /* Local node type, or a local user on any type of
  407.                    node. */
  408.                 if (localmode || od_control.baud == 0)
  409.                     {
  410.                     locecho = (cfg.localbeeping & BEEP_LOCAL);
  411.                     }
  412.                 /* LAN node type. */
  413.                 if (lanmode)
  414.                     {
  415.                     locecho = (cfg.localbeeping & BEEP_LAN);
  416.                     }
  417.                 /* Send the beep to the user, echoing locally if
  418.                    requested. */
  419.                 od_disp("\a", 1, locecho);
  420.                 }
  421.             /* Local mode always beeps on the local console, as there is
  422.                nowhere else to beep to! */
  423.             if (outmode == OUT_LOCAL)
  424.                 {
  425.                 cputs("\a");
  426.                 }
  427.             /* String modes copy the beep directly. */
  428.             if (outmode == OUT_STRING)
  429.                 {
  430.                 strcat(outputstr, "\a");
  431.                 }
  432.             continue;
  433.             }
  434.         /* CRLF (carriage return and line feed. */
  435.         if (toupper(string[d]) == 'C')
  436.             {
  437.             /* All modes output or copy an ASCII 13 and 10 pair. */
  438.             if (outmode == OUT_SCREEN)
  439.                 {
  440.                 top_output(OUT_SCREEN, "\r\n");
  441.                 }
  442.             if (outmode == OUT_LOCAL)
  443.                 {
  444.                 top_output(OUT_LOCAL, "\r\n");
  445.                 }
  446.             if (outmode == OUT_STRING)
  447.                 {
  448.                 strcat(outputstr, "\r\n");
  449.                 }
  450.             continue;
  451.             }
  452.         /* Delete to end of line.  Mostly used in dual-window chat mode. */
  453.         if (toupper(string[d]) == 'D')
  454.             {
  455.             /* Screen and local modes clear to end of line. */
  456.             if (outmode == OUT_SCREEN)
  457.                 {
  458.                 od_clr_line();
  459.                 }
  460.             if (outmode == OUT_LOCAL)
  461.                 {
  462.                 clreol();
  463.                 }
  464.             /* String modes ignore this code at this time. */
  465.             if (outmode == OUT_STRING) // Support this later
  466.                 {
  467.                 }
  468.             continue;
  469.             }
  470.         /* Erase screen. */
  471.         if (toupper(string[d]) == 'E')
  472.             {
  473.             /* Screen and local modes clear the screen. */
  474.             if (outmode == OUT_SCREEN)
  475.                 {
  476.                 od_clr_scr();
  477.                 }
  478.             if (outmode == OUT_LOCAL)
  479.                 {
  480.                 clrscr();
  481.                 }
  482.             /* String modes ignore this code at this time. */
  483.             if (outmode == OUT_STRING) // Support this later
  484.                 {
  485.                 }
  486.             continue;
  487.             }
  488.         /* Include language item. */
  489.         if (toupper(string[d]) == 'L')
  490.             {
  491.             /* Name end marker, name buffer. */
  492.             unsigned char *lnmend = NULL, *lnm = NULL;
  493.  
  494.             /* Must have a quote mark right after the @L. */
  495.             if (string[d + 1] != '\"')
  496.                 {
  497.                 continue;
  498.                 }
  499.             /* Must have a closing quote mark as well.  If it's found
  500.                it is marked as the end for later use. */
  501.             if ((lnmend = strchr(&string[d + 2], '\"')) == NULL)
  502.                 {
  503.                 continue;
  504.                 }
  505.             /* Allocate enough space for the name. */
  506.             lnm = malloc(lnmend - &string[d + 1]);
  507.             if (!lnm)
  508.                 {
  509.                 /* Ignore the code if the memory can't be gotten. */
  510.                 continue;
  511.                 }
  512.             /* Clear the name buffer then copy the name, using pointer
  513.                arithmetic. */
  514.             memset(lnm, 0, (lnmend - &string[d + 1]));
  515.             strncpy(lnm, &string[d + 2], (lnmend - &string[d + 2]));
  516.             /* Screen and local modes use a recursive call to top_output()
  517.                to display the language item.  Note that there is no way
  518.                to provide the display with any parameters that might be
  519.                needed, so only non-parameter codes should be used. */
  520.             if (outmode == OUT_SCREEN)
  521.                 {
  522.                 top_output(OUT_SCREEN, getlang(lnm));
  523.                 }
  524.             if (outmode == OUT_LOCAL)
  525.                 {
  526.                 top_output(OUT_LOCAL, getlang(lnm));
  527.                 }
  528.             /* String modes copy the item directly. */
  529.             if (outmode == OUT_STRING)
  530.                 {
  531.                 strcat(outputstr, getlang(lnm));
  532.                 }
  533.             /* Skip past the code and name. */
  534.             d += (lnmend - &string[d]);
  535.             dofree(lnm);
  536.             continue;
  537.             }
  538.         /* Position cursor on screen.  Usually used with dual-window chat
  539.            mode, and should only be used when ANSI or AVATAR mode is
  540.            known. */
  541.         if (toupper(string[d]) == 'P')
  542.             {
  543.             char txxx[5] = "\0\0\0\0"; /* Temporary position holder. */
  544.             XINT txx, txy; /* X position, Y position. */
  545.  
  546.             /* Copy the X position then convert it to an int. */
  547.             strncpy(txxx, &string[d + 1], 2);
  548.             txy = atoi(txxx);
  549.             /* Copy the Y position then convert it to an int. */
  550.             strncpy(txxx, &string[d + 3], 2);
  551.             txx = atoi(txxx); // Below also screen size checks
  552.             if (txx < 1 || txy < 1 || txx > 80 || txy > 23)
  553.                 {
  554.                 /* Ignore invalid values. */
  555.                 continue;
  556.                 }
  557.             /* Screen and local modes position the cursor. */
  558.             if (outmode == OUT_SCREEN)
  559.                 {
  560.                 od_set_cursor(txy, txx);
  561.                 }
  562.             if (outmode == OUT_LOCAL)
  563.                 {
  564.                 gotoxy(txx, txy);
  565.                 }
  566.             /* String modes ignore this code at this time. */
  567.             if (outmode == OUT_STRING) // Support this later
  568.                 {
  569.                 }
  570.             d += 4;
  571.             continue;
  572.             }
  573.         /* Repeat character. */
  574.         if (toupper(string[d]) == 'R')
  575.             {
  576.             char txxx[5] = "\0\0\0\0"; /* Temporary repeat value holder. */
  577.  
  578.             /* Copy the repeat value, which is always 3 digits. */
  579.             strncpy(txxx, &string[d + 1], 3);
  580.             /* Skip ahead to the character to repeat. */
  581.             d += 4;
  582.             /* Screen modes use the door kit to repeat. */
  583.             if (outmode == OUT_SCREEN)
  584.                 {
  585.                 od_repeat(string[d], atoi(txxx));
  586.                 }
  587.             /* Local mode uses outputstr as a temporary holder for the
  588.                repeated characters, then shows them directly. */
  589.             if (outmode == OUT_LOCAL)
  590.                 {
  591.                 memset(outputstr, string[d], atoi(txxx));
  592.                 outputstr[atoi(txxx)] = '\0';
  593.                 cputs(outputstr);
  594.                 }
  595.             if (outmode == OUT_STRING)
  596.                 {
  597.                 if (stringnf)
  598.                     {
  599.                     /* Non-filtered mode copies the entire code for later
  600.                        use. */
  601.                     strncat(outputstr, &string[d - 5], 5);
  602.                     }
  603.                 else
  604.                     {
  605.                     /* Filtered mode appends the repeated characters to
  606.                        outputstr. */
  607.                     uu = strlen(outputstr);
  608.                     memset(&outputstr[uu], string[d], atoi(txxx));
  609.                     outputstr[uu + atoi(txxx)] = '\0';
  610.                     }
  611.                 }
  612.             continue;
  613.             }
  614.         /* Space character.  Generally used at the beginning or end of an
  615.            item. */
  616.         if (toupper(string[d]) == 'S')
  617.             {
  618.             /* All modes simply show or copy a single space. */
  619.             if (outmode == OUT_SCREEN)
  620.                 {
  621.                 top_output(OUT_SCREEN, " ");
  622.                 }
  623.             if (outmode == OUT_LOCAL)
  624.                 {
  625.                 top_output(OUT_LOCAL, " ");
  626.                 }
  627.             if (outmode == OUT_STRING)
  628.                 {
  629.                 strcat(outputstr, " ");
  630.                 }
  631.             continue;
  632.             }
  633.         /* Display any ASCII value. */
  634.         if (toupper(string[d]) == 'X')
  635.             {
  636.             char xxxn[4] = "\0\0\0\0"; /* Temporary code holder. */
  637.             char xxxc[2] = "\0\0"; /* ASCII code number to display. */
  638.  
  639.             /* Copy the string representation of the code. */
  640.             strncpy(xxxn, &string[d + 1], 3);
  641.             /* Store the actual character in a small string. */
  642.             xxxc[0] = atoi(xxxn);
  643.             /* All modes display or copy the character directly. */
  644.             if (outmode == OUT_SCREEN)
  645.                 {
  646.                 top_output(OUT_SCREEN, xxxc);
  647.                 }
  648.             if (outmode == OUT_LOCAL)
  649.                 {
  650.                 top_output(OUT_LOCAL, xxxc);
  651.                 }
  652.             if (outmode == OUT_STRING)
  653.                 {
  654.                 strcat(outputstr, xxxc);
  655.                 }
  656.             /* Skip ahead to the code. */
  657.             d += 3;
  658.             continue;
  659.             }
  660.         }
  661.  
  662.     /* Actual backspace character, not handled in string modes. */
  663.     if (string[d] == 8 && outmode != OUT_STRING)
  664.         {
  665.         /* Screen and local modes display a desctructive backspace. */
  666.         if (outmode == OUT_SCREEN)
  667.             {
  668.             od_disp_str("\b \b");
  669.             }
  670.         if (outmode == OUT_LOCAL)
  671.             {
  672.             cputs("\b \b");
  673.             }
  674.         /* Remove the last character from the word wrap buffer, if anything
  675.            is in it. */
  676.         if (owwc > 0)
  677.             {
  678.             oww[--owwc] = '\0';
  679.             }
  680.         continue;
  681.         }
  682.     /* Actual newline character, not handled in string modes. */
  683.     if (string[d] == '\n' && outmode != OUT_STRING)
  684.         {
  685.         /* This was an attempt to bypass the well-known newline colour bar
  686.            bug that occurs with ANSI.SYS, where the next line gets filled
  687.            with the background colour whether we want it to be or not.  It
  688.            doesn't work. */
  689.         if (outmode == OUT_SCREEN)
  690.             {
  691.             tatr = od_control.od_cur_attrib;
  692.             od_set_attrib(0x07);
  693.             }
  694.         if (outmode == OUT_LOCAL)
  695.             {
  696.             textcolor(0x07);
  697.             }
  698.         }
  699.     /* End of special code processing. */
  700.  
  701.     /* Display the character in screen modes. */
  702.     if (outmode == OUT_SCREEN)
  703.         {
  704.         if (emu)
  705.             {
  706.             /* In emulate mode, use the door kit to handle raw ANSI, AVATAR,
  707.                RIP, or RA codes. */
  708.             od_emulate(string[d]);
  709.             }
  710.         else
  711.             {
  712.             /* In screen mode, dump the character to the screen directly,
  713.                which is way faster. */
  714.             od_putch(string[d]);
  715.             }
  716.         }
  717.     /* Display the character in local mode. */
  718.     if (outmode == OUT_LOCAL)
  719.         {
  720.         putch(string[d]);
  721.         }
  722.     /* Copy the character in string modes. */
  723.     if (outmode == OUT_STRING)
  724.         {
  725.         strncat(outputstr, &string[d], 1);
  726.         }
  727.  
  728.     /* End of the ANSI newline fix described above. */
  729.     if (string[d] == '\n' && outmode == OUT_SCREEN)
  730.         {
  731.         /* Restore the previous attribute. */
  732.         if (outmode == OUT_SCREEN)
  733.             {
  734.             od_set_attrib(tatr);
  735.             }
  736.         if (outmode == OUT_LOCAL)
  737.             {
  738.             textattr(tatr);
  739.             }
  740.         }
  741.  
  742.     /* Word wrap all displayable characters. */
  743.     if (string[d] >= 32 && string[d] <= 254 && outmode != OUT_STRING)
  744.         {
  745.         /* Add the character to the word wrap buffer. */
  746.         oww[owwc++] = string[d];
  747.         /* Reset the buffer if a space, hyphen, or slash is encountered. */
  748.         if (string[d] == ' ' || string[d] == '-' || string[d] == '/')
  749.             {
  750.             owwc = 0;
  751.             memset(oww, 0, 55);
  752.             }
  753.         /* Perform word wrapping if needed. */
  754.         outwordwrap(outmode, &owwc, oww);
  755.         }
  756.     }
  757.  
  758. /* As mentioned, outputstr is always returned though it may not be updated
  759.    with the new output. */
  760. return outputstr;
  761. }
  762.  
  763. /* outwordwrap() - Performs word wrap during output.
  764.    Parameters:  omode - Output mode (OUT_ constant).
  765.                 wwwc - Pointer to word wrap counter.
  766.                 www - Word wrap string.
  767.    Returns:  Nothing.
  768. */
  769. void outwordwrap(XINT omode, XINT *wwwc, char *www)
  770. {
  771. XINT e; /* Counter. */
  772.  
  773. /* Only word wrap if the cursor is at the end of a line. */
  774. if (wherex() == 80)
  775.     {
  776.     /* Only word wrap if the buffer is less than 50 characters.  Longer
  777.        buffers are too long to be words so wrapping is pointless. */
  778.     if (*wwwc < 50)
  779.         {
  780.         /* Backspace out the last word. */
  781.         for (e = 0; e < *wwwc; e++)
  782.             {
  783.             if (omode == OUT_SCREEN)
  784.                 {
  785.                 od_disp_str("\b \b");
  786.                 }
  787.             if (omode == OUT_LOCAL)
  788.                 {
  789.                 cputs("\b \b");
  790.                 }
  791.             }
  792.         }
  793.     /* Go to the next line. */
  794.     top_output(omode, "\r\n");
  795.     /* Redisplay the word, only if it is short enough. */
  796.     if (*wwwc < 50)
  797.         {
  798.         top_output(omode, www);
  799.         }
  800.     /* Reset the word wrap buffer and counter. */
  801.     *wwwc = 0;
  802.     memset(www, 0, 55);
  803.     }
  804.  
  805. return;
  806. }
  807.  
  808. /* outchecksuffix() - Process period modifier for output parameters.
  809.    Parameters:  strstart - Start of string where the period is expected.
  810.    Returns:  A dword that contains several bits of information.  See notes.
  811.    Notes:  The return code takes the following format, in hex:
  812.            abcdCCll
  813.            The first four hex digits, abcd, are bitwise codes (4 in each
  814.            digit) that indicate requested modes.  Only d is used right now.
  815.            Bit one is set to indicate left justification.  Bit two is set
  816.            if the string is to be filled with zeros.
  817.            The next two hex digits, CC, contain the length of the processed
  818.            code.
  819.            The last two hex ditgits, ll, contain the length requested by
  820.            the user.
  821. */
  822. unsigned long outchecksuffix(unsigned char *strstart)
  823. {
  824. /* Desired length, code (string) length, start of length. */
  825. XINT dlen, slen, numstart;
  826. unsigned long outres = 0; /* Return code holder. */
  827.  
  828. /* As a sidebar, the reason all of the other language tokens use fixed-
  829.    length numbers is because otherwise the large amount of processing done
  830.    in this function would be needed for each code. */
  831.  
  832. /* Abort if no dot is found at the start of the string. */
  833. if (strstart[0] != '.')
  834.     {
  835.     return 0x00000000L;
  836.     }
  837. /* Abort if the character after the dot is invalid (i.e. not a digit or
  838.    dash. */
  839. if (!isdigit(strstart[1]) && strstart[1] != '-')
  840.     {
  841.     return 0x00000000L;
  842.     }
  843.  
  844. /* Initialize variables. */
  845. slen = 1;
  846. /* The length can only start at the second character or later.  Remember,
  847.    numstart is holding a character index for a string so it is 0-based. */
  848. numstart = 1;
  849.  
  850. /* Left justification. */
  851. if (strstart[1] == '-')
  852.     {
  853.     /* Set the left justification flag. */
  854.     outres += 0x00010000L;
  855.     /* Increase code length. */
  856.     slen++;
  857.     /* The requested length now begins at the third character. */
  858.     numstart = 2;
  859.     }
  860. /* Fill with zeros. */
  861. if (strstart[1] == '0')
  862.     {
  863.     /* Set the zeros flag. */
  864.     outres += 0x00020000L;
  865.     /* The requested length now begins at the third character. */
  866.     numstart = 2;
  867.     }
  868.  
  869. /* Count the number of following digits, which make up the rest of the
  870.    code. */
  871. while(isdigit(strstart[slen]))
  872.     {
  873.     slen++;
  874.     }
  875.  
  876. /* Grab the requested length from the string, starting at the appropriate
  877.    character. */
  878. dlen = atoi(&strstart[numstart]);
  879.  
  880. if (slen == 0 || dlen == 0)
  881.     {
  882.     /* Abort the whole procedure if a 0 length was given. */
  883.     outres = 0x00000000L;
  884.     }
  885. else
  886.     {
  887.     /* Format the return code (see function notes). */
  888.     outres += (slen << 8) + dlen;
  889.     }
  890.  
  891. return outres;
  892. }
  893.  
  894.