home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c034 / 4.ddi / SOURCE / TXTFMT.C$ / TXTFMT.bin
Encoding:
Text File  |  1990-01-08  |  26.0 KB  |  755 lines

  1. /* TXTFMT.C contains several new editor functions related to text
  2.  * formatting. They include:
  3.  *
  4.  *  ucase       Uppercases text
  5.  *  lcase       Lowercases text
  6.  *  tcase       Toggles case of text
  7.  *  ptab        Inserting tab that can handle variable-space tabs
  8.  *  mtab        Corresponding backtab
  9.  *  justify     Justifies text paragraphs
  10.  *  CenterLine  Centers a line of text (from Advanced Programming
  11.  *              Techniques manual)
  12.  *
  13.  * To compile for DOS or OS/2:
  14.  *
  15.  *   cl /c /Lr /Gs /ACw txtfmt.c
  16.  *
  17.  * To link for DOS:
  18.  *
  19.  *   link /NOI exthdr txtfmt,txtfmt.mxt;
  20.  *
  21.  * To link for OS/2:
  22.  *
  23.  *   link /NOI exthdrp txtfmt,txtfmt.pxt,,,ext.def;
  24.  *
  25.  * To load, put the following command in your TOOLS.INI (or execute with
  26.  * the assign command):
  27.  *
  28.  *   load:[d:\path\]txtfmt
  29.  *
  30.  * Documentation for each command is given in the function headers.
  31.  */
  32.  
  33. #include <string.h>
  34. #include <stdlib.h>
  35. #include <ext.h>
  36.  
  37. /* Function prototypes, starting with new commands */
  38. PWBFUNC SetTabMask( char far *mask );
  39. PWBFUNC ucase( unsigned argData, ARG far *pArg, flagType fMeta );
  40. PWBFUNC lcase( unsigned argData, ARG far *pArg, flagType fMeta );
  41. PWBFUNC tcase( unsigned argData, ARG far *pArg, flagType fMeta );
  42. PWBFUNC ptab( unsigned argData, ARG far *pArg, flagType fMeta );
  43. PWBFUNC mtab( unsigned argData, ARG far *pArg, flagType fMeta );
  44. PWBFUNC justify( unsigned argData, ARG far *pArg, flagType fMeta );
  45. PWBFUNC CenterLine( unsigned argData, ARG _far *pArg, flagType fMeta );
  46.  
  47. /* Internal functions used by commands */
  48. flagType ulcase( ARG far *pArg, int fAction );
  49. void     DumpLine( char far *, PFILE, COL, COL, LINE, char far *, int );
  50. int      NextLine( char far *, PFILE, LINE, LINE );
  51. void     GetMinLine( LINE y, COL xMin, char *pch, PFILE pFile );
  52.  
  53. /* Constant */
  54. #define MAXCOL  32765
  55. #define STARTTABMASK 10
  56.  
  57. /* Global variables for switches - initialized to default values,
  58.  * but switch overrides.
  59.  */
  60. flagType j2space = FALSE;
  61. int jmargin = 70;
  62. int cTab = 8;
  63. char achTabMask[81] =
  64. {
  65. "----+--T-1----+T---2---T+----3-T--+----T----+--T-5----+T---6---T+----7-T--+-"
  66. };
  67.  
  68. /* New switches. */
  69. struct swiDesc swiTable[] =
  70. {
  71.     { "j2space",    toPIF( j2space ),    SWI_BOOLEAN },
  72.     { "jmargin",    toPIF( jmargin ),    SWI_NUMERIC | RADIX10 },
  73.     { "tabmask",    toPIF( SetTabMask ), SWI_SPECIAL },
  74.     { NULL,         NULL,                0 }
  75. };
  76.  
  77. /* New commands. */
  78. struct cmdDesc cmdTable[] =
  79. {
  80.     { "ptab",    ptab,    0, KEEPMETA | NOARG | LINEARG | NULLARG | BOXARG },
  81.     { "mtab",    mtab,    0, KEEPMETA | NOARG | LINEARG | NULLARG | BOXARG },
  82.     { "ucase",   ucase,   0, KEEPMETA | NOARG | LINEARG | NULLARG | BOXARG },
  83.     { "lcase",   lcase,   0, KEEPMETA | NOARG | LINEARG | NULLARG | BOXARG },
  84.     { "tcase",   tcase,   0, KEEPMETA | NOARG | LINEARG | NULLARG | BOXARG },
  85.     { "justify", justify, 0, TEXTARG  | NOARG | LINEARG | NULLARG | BOXARG },
  86.     { "CenterLine", CenterLine, 0, NOARG | LINEARG },
  87.     { NULL,      NULL,    0, 0 }
  88. };
  89.  
  90. /* Initialize tab mask. */
  91. PWBFUNC SetTabMask( char far *mask )
  92. {
  93.     strcpy( achTabMask, mask );
  94.     return TRUE;
  95. }
  96.  
  97. /* Initializations and commands to be performed when extension is loaded. */
  98. void EXTERNAL WhenLoaded()
  99. {
  100.     SetKey( "tcase",      "alt+x" );
  101.     SetKey( "justify",    "alt+j" );
  102.     SetKey( "CenterLine", "alt+c" );
  103.     SetKey( "ptab",       "ctrl+i" );
  104.     return;
  105. }
  106.  
  107. /* ptab - New tab function for variable or regular tabs. Can push text
  108.  * over to the new tab position. The function handles various argument
  109.  * types:
  110.  *
  111.  *      ptab                Moves to next tab stop
  112.  *      arg ptab            Pushes text from cursor to next tab stop
  113.  *      arg boxarg ptab     Pushes all lines in box from cursor to next
  114.  *                          tab stop
  115.  *      arg linearg ptab    Pushes all marked lines from left margin to
  116.  *                          next tab stop
  117.  *
  118.  * You can press arg multiple times to push text over multiple tab stops.
  119.  *
  120.  * If the Tabstops switch value is 10 or less, tabstops are handled
  121.  * normally. If larger, variable tabs as defined in the tabmask switch
  122.  * are used. The tab mask switch is a string in which Ts represent
  123.  * tab positions and other characters are ignored.
  124.  */
  125. PWBFUNC ptab( unsigned argData, ARG far *pArg, flagType fMeta )
  126. {
  127.     PFILE   pFile;
  128.     LINE    yStart, yEnd, yT;
  129.     COL     xStart, xTab, xT;
  130.     PSWI    pswi;
  131.     int     c, cT, fInsert;
  132.  
  133.     /* Get switch value for "tabstops" and assign to variables. */
  134.     pswi = FindSwitch( "tabstops" );
  135.     cTab = *(pswi->act.ival);
  136.  
  137.     pFile = FileNameToHandle( "", "" );
  138.  
  139.     switch( pArg->argType )
  140.     {
  141.         case NOARG:     // Move from cursor
  142.             c = 1;
  143.             fInsert = FALSE;
  144.             yEnd   = yStart = pArg->arg.noarg.y;
  145.             xStart = pArg->arg.noarg.x;
  146.             break;
  147.  
  148.         case NULLARG:   // Push from cursor
  149.             c = 1;
  150.             fInsert = TRUE;
  151.             yEnd   = yStart = pArg->arg.nullarg.y;
  152.             xStart = pArg->arg.nullarg.x;
  153.             break;
  154.  
  155.         case BOXARG:    // Push block from cursor
  156.             c = pArg->arg.boxarg.cArg - 1;
  157.             fInsert = TRUE;
  158.             yStart = pArg->arg.boxarg.yTop;
  159.             yEnd   = pArg->arg.boxarg.yBottom;
  160.             xStart = pArg->arg.boxarg.xLeft;
  161.             break;
  162.  
  163.         case LINEARG:   // Push block from start of line
  164.             fInsert = TRUE;
  165.             c = pArg->arg.linearg.cArg - 1;
  166.             yStart = pArg->arg.linearg.yStart;
  167.             yEnd   = pArg->arg.linearg.yEnd;
  168.             xStart = 0;
  169.             break;
  170.     }
  171.  
  172.     /* If a tab size is defined, calculate the next tab position.
  173.      * If size is greater than STARTTABMASK, find the next tab position
  174.      * in the tab mask. For line and block arguments, do this as many
  175.      * times as arg was pressed.
  176.      */
  177.     if( cTab <= STARTTABMASK )
  178.         xTab = xStart + (c * cTab) - (xStart % cTab);
  179.     else {
  180.         xT = xStart;
  181.         for( cT = 1; cT <= c; cT++ )
  182.         {
  183.             while( achTabMask[++xT] != 'T' )    // Skip to next 'T'
  184.             {
  185.                 if( !achTabMask[xT] )
  186.                 {
  187.                     xT = xStart;                // Restore if end of mask
  188.                     break;
  189.                 }
  190.             }
  191.         }
  192.         xTab = xT;
  193.     }
  194.  
  195.     /* If arg pressed, slide text over. Otherwise, just move cursor over. */
  196.     if( fInsert || (yEnd > yStart) )
  197.     {
  198.         for( yT = yStart; yT <= yEnd; yT++ )
  199.         {
  200.             for( xT = xStart; xT < xTab; xT++ )
  201.             {
  202.                 Replace( ' ', xT, yT, pFile, TRUE );
  203.             }
  204.         }
  205.     }
  206.     MoveCur( xTab, yStart );
  207.  
  208.     return TRUE;
  209. }
  210.  
  211. /* mtab - New tab function for variable or regular tabs. can pull text
  212.  * back to the previous tab position. The function handles various
  213.  * argument types:
  214.  *
  215.  *      mtab                Moves to last tab stop
  216.  *      arg mtab            Pulls text from cursor to last tab stop
  217.  *      arg boxarg mtab     Pulls all lines in box from cursor to last
  218.  *                          tab stop
  219.  *      arg linearg mtab    Same as boxarg
  220.  *
  221.  * Uses same tabmask as ptab if the Tabstops switch value is 10 or less.
  222.  */
  223. PWBFUNC mtab( unsigned argData, ARG far *pArg, flagType fMeta )
  224. {
  225.     PFILE   pFile;
  226.     LINE    yStart, yEnd, yT;
  227.     COL     xStart, xTab, xT;
  228.     char    buf[BUFLEN];
  229.     PSWI    pswi;
  230.  
  231.     /* Get switch values and assign to globals. */
  232.     pswi = FindSwitch( "tabstops" );
  233.     cTab = *(pswi->act.ival);
  234.  
  235.     pFile = FileNameToHandle( "", "" );
  236.  
  237.     switch( pArg->argType )
  238.     {
  239.         case NOARG:             /* Move cursor back one tab position */
  240.             yEnd = yStart = pArg->arg.noarg.y;
  241.             xStart = pArg->arg.noarg.x;
  242.             break;
  243.  
  244.         case NULLARG:           /* Move line back one tab position   */
  245.             yEnd = yStart = pArg->arg.nullarg.y;
  246.             xStart = pArg->arg.nullarg.x;
  247.             break;
  248.  
  249.         case LINEARG:           /* Move block back one tab position  */
  250.             yStart = pArg->arg.linearg.yStart;
  251.             yEnd = pArg->arg.linearg.yEnd;
  252.             GetCursor( &xStart, &yT );
  253.             break;
  254.  
  255.         case BOXARG:            /* Move block back one tab position  */
  256.             yStart = pArg->arg.boxarg.yTop;
  257.             yEnd = pArg->arg.boxarg.yBottom;
  258.             xStart = pArg->arg.boxarg.xLeft;
  259.             break;
  260.     }
  261.  
  262.     /* If a tab size is defined, calculate the last tab position.
  263.      * If size is 0, find the last tab position in the tab mask.
  264.      */
  265.     if( cTab <= STARTTABMASK )
  266.     {
  267.         xTab = xStart - (xStart % cTab);
  268.         if (xTab && (xTab == xStart))
  269.             xTab -= cTab;
  270.     }
  271.     else
  272.     {
  273.         xT = xStart;
  274.         while( xT && achTabMask[--xT] != 'T' )  // Skip to last 'T'
  275.         {
  276.             if (!achTabMask[xT])
  277.             {
  278.                 xT = xStart;                    // Restore if end of mask
  279.                 break;
  280.             }
  281.         }
  282.         xTab = xT;
  283.     }
  284.  
  285.     /* Slide everthing back to the last tab, but check to make sure
  286.      * nothing is overwritten. If no arg, just move cursor back.
  287.      */
  288.     if( pArg->argType != NOARG )
  289.     {
  290.         yT = yStart;
  291.         do
  292.         {
  293.             GetLine( yT, buf, pFile );
  294.             for( xT = xStart; (xT > xTab) && (buf[xT - 1] == ' '); xT-- )
  295.                 ;
  296.             DelStream( pFile, xT, yT, xStart, yT );
  297.             ++yT;
  298.         } while( yT <= yEnd );
  299.     }
  300.     MoveCur( xTab, yStart );
  301.  
  302.     return TRUE;
  303. }
  304.  
  305. /* ucase, lcase, and tcase - Functions to uppercase, lowercase, or
  306.  * toggle text. Handles different arguments:
  307.  *
  308.  *   xcase              Changes case of cursor character
  309.  *   arg xcase          Changes case to end of word
  310.  *   arg arg xcase      Changes case to end of line
  311.  *   arg linearg xcase  Changes case of lines
  312.  *   arg boxarg xcase   Changes case of box
  313.  *
  314.  * Assign your favorite, or all three.
  315.  */
  316.  
  317. enum cases { UPPER, LOWER, TOGGLE };
  318.  
  319. PWBFUNC ucase( unsigned argData, ARG far *pArg, flagType fMeta )
  320. {
  321.     return ulcase( pArg, UPPER );
  322. }
  323.  
  324. /* Convert arg to lower case.
  325.  */
  326. PWBFUNC lcase( unsigned argData, ARG far *pArg, flagType fMeta )
  327. {
  328.     return ulcase( pArg, LOWER );
  329. }
  330.  
  331. /* Toggle case of arg.
  332.  */
  333. PWBFUNC tcase( unsigned argData, ARG far *pArg, flagType fMeta )
  334. {
  335.     return ulcase( pArg, TOGGLE );
  336. }
  337.  
  338. /* Change case: toggle, upper, lower.
  339.  */
  340. flagType ulcase( ARG far *pArg, int fAction )
  341. {
  342.     PFILE   pFile;
  343.     COL     xStart, xEnd, xT;
  344.     LINE    yStart, yEnd;
  345.     int     i;
  346.     char    buf[BUFLEN];
  347.  
  348.     pFile = FileNameToHandle( "", "" );
  349.  
  350.     switch( pArg->argType )
  351.     {
  352.         case NOARG:                 /* Switch case at cursor */
  353.             xStart = xEnd = pArg->arg.noarg.x;
  354.             yStart = yEnd = pArg->arg.noarg.y;
  355.             MoveCur( xStart + 1, yStart );
  356.             break;
  357.  
  358.         case NULLARG:               /* One arg: switch case to end of word  */
  359.             if( pArg->arg.nullarg.cArg == 1 )
  360.             {
  361.                 xStart = pArg->arg.nullarg.x;
  362.                 yStart = pArg->arg.nullarg.y;
  363.                 fExecute( "pword" );
  364.                 GetCursor( &xEnd, &yEnd );
  365.                 if( yEnd > yStart )
  366.                 {
  367.                     --yEnd;
  368.                     xEnd = MAXCOL;
  369.                 } else
  370.                     xEnd--;
  371.                 break;
  372.             } else {                /* Two args: switch case to end of line */
  373.                 xStart = pArg->arg.nullarg.x;
  374.                 xEnd = MAXCOL;
  375.                 yStart = yEnd = pArg->arg.nullarg.y;
  376.                 break;
  377.             }
  378.  
  379.  
  380.         case LINEARG:               /* Switch case of line range */
  381.             xStart = 0;
  382.             xEnd = MAXCOL;
  383.             yStart = pArg->arg.linearg.yStart;
  384.             yEnd = pArg->arg.linearg.yEnd;
  385.             break;
  386.  
  387.         case BOXARG:                /* Switch case of box  */
  388.             xStart = pArg->arg.boxarg.xLeft;
  389.             xEnd   = pArg->arg.boxarg.xRight;
  390.             yStart = pArg->arg.boxarg.yTop;
  391.             yEnd   = pArg->arg.boxarg.yBottom;
  392.             break;
  393.     }
  394.  
  395.     /* Get each line. Convert case from starting to ending column.
  396.      * Put modified line back in file.
  397.      */
  398.     while( yStart <= yEnd )
  399.     {
  400.         i = GetLine( yStart, buf, pFile );
  401.         xT = xStart;
  402.         while( (xT <= i) && (xT <= xEnd) )
  403.         {
  404.             switch( fAction )
  405.             {
  406.                 case TOGGLE:
  407.                     if( buf[xT] >= 'A' && buf[xT] <= 'Z' )
  408.                     {
  409.                         buf[xT] += 'a' - 'A';   /* Convert to lower      */
  410.                         break;                  /* else fall through     */
  411.                     }
  412.                 case UPPER:
  413.                     if( buf[xT] >= 'a' && buf[xT] <= 'z' )
  414.                         buf[xT] += 'A' - 'a';   /* Convert to upper      */
  415.                     break;
  416.                 case LOWER:
  417.                     if( buf[xT] >= 'A' && buf[xT] <= 'Z' )
  418.                         buf[xT] += 'a' - 'A';   /* Convert to lower      */
  419.                     break;
  420.             }
  421.             xT++;
  422.         }
  423.         PutLine( yStart++, buf, pFile );
  424.     }
  425.  
  426.     return TRUE;
  427. }
  428.  
  429.  
  430. /* justify - Justifies paragraphs. Handles variout arguments:
  431.  *
  432.  * justify              Justify between first nonspace column and jmargin,
  433.  *                      from the current line to blank line.
  434.  *
  435.  * arg justify          Justify between current column and jmargin, from
  436.  *                      the current line to blank line.
  437.  *
  438.  * arg linearg justify  Justify the specified lines between column 0 and
  439.  *                      jmargin.
  440.  *
  441.  * arg boxarg justify   Justify the specified rows between the specified
  442.  *                      columns the specified rows.
  443.  *
  444.  * arg textarg justify  Justify between columns 0 and jmargin, from the
  445.  *                      current line to blank line, prepending each
  446.  *                      resulting line with the textarg. This is useful
  447.  *                      for justifying comment blocks preceded by *'s.
  448.  */
  449. PWBFUNC justify( unsigned argData, ARG far *pArg, flagType fMeta )
  450. {
  451.     static  char    inbuf[512]; // Input buffer
  452.     PFILE   pFile;              // File handle
  453.     char far *pText;            // Pointer to prepending text
  454.     COL     x1, x2;             // Justify to right/left column
  455.     LINE    y1, y2, yOut;       // Start/end/output line
  456.     char    buf[BUFLEN];
  457.     int     i;
  458.     PSWI    pswi;
  459.  
  460.     pFile = FileNameToHandle( "", "" );
  461.  
  462.     switch( pArg->argType )
  463.     {
  464.         case NOARG:                         // Justify paragraph
  465.             y1 = pArg->arg.noarg.y;         // from current line...
  466.             y2 = -1;                        //  ...to blank line
  467.             i = GetLine( y1, buf, pFile );
  468.             for( x1 = 0; (buf[x1] == ' ') && (x1 < i); x1++ )
  469.                 ;                           // from first nonspace to
  470.             x2 = jmargin;                   //  ...jmargin
  471.             pText = 0;                      // There is no text
  472.             break;
  473.  
  474.         case NULLARG:                       // Justify indented
  475.             x1 = pArg->arg.nullarg.x;       // between cur col...
  476.             x2 = jmargin;                   //  ...and jmargin
  477.             y1 = pArg->arg.nullarg.y;       // current line...
  478.             y2 = -1;                        //  ...to blank line
  479.             pText = 0;                      // There is no text
  480.             break;
  481.  
  482.         case LINEARG:                       // Justify line range
  483.             x1 = 0;                         // between cols 0...
  484.             x2 = jmargin;                   //  ...and jmargin
  485.             y1 = pArg->arg.linearg.yStart;  // and range of lines
  486.             y2 = pArg->arg.linearg.yEnd;
  487.             pText = 0;                      // There is no text
  488.             break;
  489.  
  490.         case BOXARG:                        // Justify box
  491.             x1 = pArg->arg.boxarg.xLeft;    // from left corner...
  492.             x2 = pArg->arg.boxarg.xRight;   //      ...to right
  493.             y1 = pArg->arg.boxarg.yTop;     // from top...
  494.             y2 = pArg->arg.boxarg.yBottom;  //     ...to bottom
  495.             pText = 0;                      // There is no text
  496.             break;
  497.  
  498.         case TEXTARG:                       // Justify and prepend
  499.             x1 = 0;                         // between 0...
  500.             x2 = jmargin;                   //    ...and jmargin
  501.             y1 = pArg->arg.textarg.y;       // current line...
  502.             y2 = -1;                        //     ...to blank line
  503.             pText = pArg->arg.textarg.pText;// There IS text
  504.             break;
  505.     }
  506.  
  507.     if( y1 == y2 )                          // If same line, then
  508.         y2 = -1;                            // just to blank line
  509.  
  510.     if( x1 == x2 )                          // If same column
  511.         x2 = jmargin;                       // then just to default
  512.  
  513.     if( x2 < x1 )                           // If bas-ackwards
  514.     {
  515.         x1 = 0;                             // revert to default
  516.         x2 = jmargin;
  517.     }
  518.  
  519.     /* While we can get data within the specified limits, format
  520.      * each new line and output back to the file.
  521.      */
  522.     inbuf[0] = 0;
  523.     yOut = y1;                              // Line being output
  524.     while( NextLine(inbuf,pFile,y1,y2 ) )   // While data to get
  525.     {
  526.         if( y2 != -1 )
  527.             y2--;                           // End moves with deletion
  528.         while( strlen( inbuf ) > (x2 - x1) )// While data to output
  529.         {
  530.             y1++;                           // Line moves with insert
  531.             if( y2 != -1 )
  532.                 y2++;
  533.             DumpLine(inbuf,pFile,x1,x2,yOut++,pText,fMeta );
  534.         }
  535.     }
  536.     /* Dump any partial last line. Then if we were formatting to
  537.      * a blank line, dump out one of those too.
  538.      */
  539.     if( strlen( inbuf ) > 0 )               // Dump last line
  540.         DumpLine( inbuf, pFile, x1, x2, yOut++, pText, 0 );
  541.     if( y2 == -1 )                          // Dump blank line
  542.         DumpLine( "", pFile, x1, x2, yOut++, pText, 0 );
  543.     return TRUE;
  544. }
  545.  
  546. /* NextLine - Get next line from file. Like GetLine, except removes
  547.  * leading and trailing spaces.
  548.  */
  549.  
  550. int NextLine( char   far *pBuf,     // Input buffer
  551.               PFILE  pFile,         // File pointer
  552.               LINE   y1,            // Line # to read
  553.               LINE   y2 )           // Line # to stop at
  554. {
  555.     char    far *pT;                // Working pointer
  556.     int     rv;                     // Return value
  557.     char    achWork[512];           // Working buffer
  558.  
  559.     achWork[0] = 0;
  560.     rv = 0;
  561.     if( (y2 == -1) || (y1 <= y2) )
  562.     {
  563.         rv = GetLine(y1, achWork, pFile );       // Get a line
  564.         DelLine(pFile, y1, y1 );                 // Remove it
  565.     }
  566.  
  567.     /* If there was data, strip leading spaces in newly input line. */
  568.     if( rv )
  569.     {
  570.         pT = achWork;                            // Point into line
  571.         while( *pT == ' ' )
  572.             pT++;                                // Skip leading spaces
  573.  
  574.         /* If existing buffer is non-empty, append a space and set
  575.          * pointer to end.
  576.          */
  577.         if( strlen(pBuf ) )                      // If non-null string
  578.         {
  579.             pBuf += strlen( pBuf );              // point to null
  580.             *pBuf++ = ' ';                       // Append space
  581.             if( j2space & ( *(pBuf - 2) == '.') )// If period...
  582.                 *pBuf++ = ' ';                   // append another
  583.         }
  584.  
  585.         /* Append new line, but compress multiple spaces into one. */
  586.         while( *pT )                             // Copy line over
  587.         {
  588.             if( j2space & (*pT == '.') )         // If period...
  589.                 if( *(pT + 1) == ' ' )           //   ...space
  590.                 {
  591.                     *pBuf++ = *pT++;             // Copy period
  592.                     *pBuf++ = *pT;               // Copy space
  593.                 }
  594.             if( (*pBuf++ = *pT++) == ' ' )       // Copy a char
  595.                 while( *pT == ' ' )
  596.                     pT++;                        // Skip multiple spaces
  597.         }
  598.         if( *(pBuf-1) == ' ' )                   // If a trailing space
  599.             pBuf--;                              // Remove it
  600.         *pBuf = 0;
  601.     }
  602.  
  603.     return rv;
  604. }
  605.  
  606. /* DumpLine - Dump one line of text to the file. Prepend any required
  607.  * text or spaces, and perform word break/cut at right hand column.
  608.  */
  609. void DumpLine( char     far *pBuf,              // Data to output
  610.                PFILE    pFile,                  // File to output to
  611.                COL      x1,                     // Left-hand column
  612.                COL      x2,                     // Right-hand column
  613.                LINE     yOut,                   // Line to output to
  614.                char     far *pText,             // Text to prepend
  615.                int      fFlush )                // Flush both sides
  616. {
  617.     int     i;
  618.     char    far *pT;
  619.     char    far *pT2;
  620.     char    achWork[512];                       // Working buffer
  621.     char    achFlush[512];                      // Working buffer
  622.     char    fSpace;                             // Space seen flag
  623.  
  624.     /* Start by prepending any text, and then filling out
  625.      * to the left hand column to justify to.
  626.      */
  627.     achWork[0] = 0;                             // Start with null
  628.     if( pText )
  629.         strcpy( achWork, pText );               // If starting with text
  630.     i = strlen( achWork );                      // length of line-so-far
  631.     while( i++ < x1 )
  632.         strcat( achWork, " " );                 // Fill out with spaces
  633.  
  634.     /* Append the data to be output, and then starting at the right
  635.      * column, scan back for a space to break at. If one is not
  636.      * found before the left hand column, then break at the right
  637.      * hand column. Copy any line left over back to the passed in
  638.      * buffer.
  639.      */
  640.     strcat( achWork, pBuf );                    // Get total line
  641.     *pBuf = 0;                                  // Empty input buffer
  642.     if( strlen( achWork ) > x2 )                // If we need to cut
  643.     {
  644.  
  645.         pT = &achWork[x2];                      // Point at potential cut
  646.         while( (pT > (char far *)&achWork[0]) && (*pT != ' ') )
  647.             pT--;                               // Back up to space
  648.  
  649.         if( pT <= (char far *)&achWork[x1] )    // If none found in range
  650.         {
  651.             strcpy( pBuf, &achWork[x2] );       // copy remainder of line
  652.             achWork[x2] = 0;                    // and terminate this one
  653.         }
  654.         else
  655.         {
  656.             while( *++pT == ' ' );              // Skip leading spaces
  657.                 strcpy( pBuf, pT );             // Copy remainder of line
  658.             *pT = 0;                            // and terminate this one
  659.         }
  660.     }
  661.  
  662.     /* This code is invoked when the user wants to justify both
  663.      * right and left sides of his text. We determine how many
  664.      * spaces we need to add, and scan through and add one space
  665.      * to each run of spaces until we've added enough
  666.      */
  667.     if( fFlush )                                // Right & left justify?
  668.     {
  669.         while( (i = x2 - strlen( achWork )) > 0 )// Count of spaces to add
  670.         {
  671.             strcpy( achFlush, achWork );        // Start with unmodified
  672.             pT = achWork + x1;
  673.             pT2 = achFlush + x1;                // Skip fixed part
  674.             fSpace = FALSE;                     // Assume no spaces
  675.             while( *pT )                        // While data to copy
  676.             {
  677.                 if( (*pT == ' ') && i )         // Time to insert a space
  678.                 {
  679.                     fSpace = TRUE;              // We've seen a space
  680.                     *pT2++ = ' ';
  681.                     i--;
  682.                     while( *pT == ' ' )
  683.                         *pT2++ = *pT++;         // Copy run of spaces
  684.                 }
  685.                 if( *pT )
  686.                     *pT2++ = *pT++;             // Copy line
  687.                 if( (*pT == 0) && !fSpace)
  688.                     break;                      // No embedded spaces
  689.             }
  690.             *pT2 = 0;
  691.             strcpy( achWork, achFlush );        // Copy back
  692.         }
  693.     }
  694.  
  695.     /* Create new line and output it. */
  696.     CopyLine( (PFILE) NULL, pFile, yOut, yOut, yOut );
  697.     PutLine( yOut, achWork, pFile );
  698. }
  699.  
  700.  
  701. #define LINE_LENGTH 80      // Assumes 80-column screen
  702.  
  703. /* CenterLine - Centers a line or sequence of lines. Handles two argument
  704.  * types:
  705.  *
  706.  * CenterLine               Centers the current line in 80-column field
  707.  * arg linearg CenterLine   Centers the range of lines in 80-column field
  708.  *
  709.  * This is the same same example contained in the Advanced Programming
  710.  * Techniques manual.
  711.  */
  712. PWBFUNC CenterLine( unsigned argData, ARG _far *pArg, flagType fMeta )
  713. {
  714.     PFILE pFile;
  715.     LINE  yStart, yEnd;
  716.     int   len;
  717.     char *pBuf, buf[BUFLEN];
  718.  
  719.     /* Get a handle to the current file */
  720.     pFile = FileNameToHandle( "", "" );
  721.  
  722.     /* Handle various argument types */
  723.     switch( pArg->argType )
  724.     {
  725.         case NOARG:  /* No argument. Center current line */
  726.             yStart = yEnd = pArg->arg.noarg.y;
  727.             break;
  728.  
  729.         case LINEARG:  /*  Center range of lines */
  730.             yStart = pArg->arg.linearg.yStart;
  731.             yEnd = pArg->arg.linearg.yEnd;
  732.             break;
  733.     }
  734.  
  735.     /* Center current line or range of lines */
  736.     for( ; yStart <= yEnd; yStart++ )
  737.     {
  738.        /* Get a line from the current file */
  739.         len = GetLine( yStart, buf, pFile );
  740.  
  741.         if( len > 0 )
  742.         {
  743.             /* Center the text in this line */
  744.             pBuf = buf + strspn( buf, " \t" );
  745.             len = strlen( pBuf );
  746.             memmove( buf+(LINE_LENGTH-len) / 2, pBuf, len+1 );
  747.             memset( buf, ' ', (LINE_LENGTH - len) / 2 );
  748.  
  749.             /* Write modified line back to the current file */
  750.             PutLine( yStart, buf, pFile );
  751.         }
  752.     }
  753.     return TRUE;
  754. }
  755.