home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / dos / edit / point20 / wp.c < prev   
Encoding:
C/C++ Source or Header  |  1991-02-04  |  17.3 KB  |  717 lines

  1. #include "pt.h"
  2. #include "string.h"
  3. #include "direct.h"
  4.  
  5. #define PATTERNSIZE 64
  6.  
  7. static unsigned char *endPattern;
  8. static unsigned char *beginPattern;
  9. static unsigned char filePatterns[PATTERNSIZE];
  10. static unsigned char retFilename[PATTERNSIZE];
  11. static int savedAttribute;
  12. static int dirNameSize;
  13.  
  14. unsigned char * pascal
  15. /* XTAG:findFirstPatternFile */
  16. findFirstPatternFile(patterns, attribute, dta)
  17.     unsigned char *patterns;
  18.     int attribute;
  19.     unsigned char *dta;
  20. {
  21.     unsigned char *fileName;
  22.     unsigned char *endPrefix;
  23.     int i;
  24.  
  25.     /* save patterns for later use in findNextPatternFile */
  26.     strncpy(filePatterns, patterns, PATTERNSIZE);
  27.     savedAttribute = attribute;
  28.     beginPattern = filePatterns;
  29.  
  30.     /* see if this pattern has at least two components */
  31.     endPattern = strchr(beginPattern, '|');
  32.     
  33.     /* find the path prefix and isolate the final pathname component */
  34.     /* NOTE: this fails for full pathnames (starting with "\") */
  35.     /* start looking at the end of the pathname */
  36.     if( endPattern != NULL ) {
  37.         *endPattern = '\0';
  38.         endPrefix = endPattern - 1;
  39.     } else
  40.         endPrefix = beginPattern + strlen(filePatterns) - 1;
  41.  
  42.     /* look until you see a "\" or a ":" */
  43.     for(  ; endPrefix >= beginPattern; --endPrefix)
  44.         if( *endPrefix == ':' || *endPrefix == '\\' )
  45.             break;
  46.     dirNameSize = endPrefix - beginPattern + 1;
  47.  
  48.     /* start looking for matching file names */
  49.     fileName = findFirstFile(beginPattern, savedAttribute, dta);
  50.     
  51.     /* skip "." */
  52.     if( fileName == NULL || strcmp(&fileName[30], ".") == 0 )
  53.         return findNextPatternFile(dta);
  54.  
  55.     /* else contruct the filename */
  56.     for(i = 0; i < dirNameSize; ++i)
  57.         retFilename[i] = beginPattern[i];
  58.     retFilename[i] = '\0';
  59.     strcat(retFilename, &fileName[30]);
  60.     return retFilename;
  61. }
  62.     
  63. unsigned char * pascal
  64. /* XTAG:findNextPatternFile */
  65. findNextPatternFile(dta)
  66.     unsigned char *dta;
  67. {
  68.     unsigned char *fileName;
  69.     unsigned char *endPrefix;
  70.     register int i;
  71.  
  72.     /* find the next matching file name */
  73.     fileName = findNextFile(dta);
  74.     if( fileName != NULL )
  75.         goto ret;
  76.  
  77. tryMorePatterns:
  78.     /* see if this pattern has two or more components left */
  79.     if( endPattern != NULL )
  80.         *endPattern++ = '|';    /* restore pattern as it was */
  81.     else    /* no more file patterns to expand */
  82.         return NULL;
  83.  
  84.     /* next pattern begin where the last one ends */
  85.     beginPattern = endPattern;
  86.     endPattern = strchr(beginPattern, '|');
  87.     if( endPattern != NULL ) {
  88.         *endPattern = '\0';
  89.         endPrefix = endPattern - 1;
  90.     } else
  91.         endPrefix = beginPattern + strlen(beginPattern) - 1;
  92.  
  93.     for(  ; endPrefix >= beginPattern; --endPrefix)
  94.         if( *endPrefix == ':' || *endPrefix == '\\' )
  95.             break;
  96.     dirNameSize = endPrefix - beginPattern + 1;
  97.  
  98.     /* start looking for matching file names */
  99.     fileName = findFirstFile(beginPattern, savedAttribute, dta);
  100.     if( fileName == NULL )
  101.         goto tryMorePatterns;
  102.  
  103. ret:
  104.     /* else contruct the filename */
  105.     for(i = 0; i < dirNameSize; ++i)
  106.         retFilename[i] = beginPattern[i];
  107.     retFilename[i] = '\0';
  108.     strcat(retFilename, &fileName[30]);
  109.     return retFilename;
  110. }
  111.  
  112.  
  113. unsigned char * pascal
  114. /* XTAG:makeFullPathname */
  115. makeFullPathname(origName)
  116.     unsigned char *origName;
  117. {
  118.     extern unsigned char msgBuffer[];
  119.     extern unsigned char scratchFileName[];
  120.  
  121.     int offset, n;
  122.     register unsigned char *p;
  123.     unsigned char *fromPtr, *toPtr, ch;
  124.  
  125.     /* figure out the complete pathname for the file */
  126.     offset = 0;    /* offset into origName */
  127.  
  128.     /* first figure out what we have to do */
  129.     if( origName[1] == ':' ) {
  130.         if( origName[2] != '\\' && origName[2] != '/' ) {
  131.             /* get the current directory on that drive */
  132.             n = getDefaultDrive();    /* save default drive */
  133.             (void)setDefaultDrive(toupper(origName[0])-'A');
  134.  
  135.             /* get the current directory on that drive */
  136.             (void)getcwd(&msgBuffer[0], MSGBUFFERSIZE);
  137.  
  138.             /* restore the orignal defualt drive */
  139.             (void)setDefaultDrive(n);
  140.  
  141.             /* skip the drive spec since getcwd gets it */
  142.             offset = 2;
  143.             goto fixDirectoryName;
  144.  
  145.         } else    /* name begins A:\ or something like it so */
  146.             /* just use the string typed in */
  147.             scratchFileName[0] = '\0';
  148.  
  149.     } else if( origName[0] == '\\' || origName[0] == '/' ) {
  150.         /* add a drive identifier to the string typed in */
  151.         scratchFileName[0] = (unsigned char)'A' + getDefaultDrive();
  152.         scratchFileName[1] = ':';
  153.         scratchFileName[2] = '\0';
  154.  
  155.     } else {    /* must prepend the current drive and directory */
  156.         (void)getcwd(&msgBuffer[0], MSGBUFFERSIZE);
  157.     fixDirectoryName:
  158.         strcpy(scratchFileName, msgBuffer);
  159.         n = strlen(scratchFileName);
  160.         if( scratchFileName[n-1] != '\\' ) {
  161.             scratchFileName[n++] = '\\';
  162.             scratchFileName[n] = '\0';
  163.         }
  164.     }
  165.     strncat(scratchFileName, &(origName[offset]), FILENAMESIZE);
  166.     
  167.     /* now eliminate any ".." components */
  168.     p = &scratchFileName[0];
  169.     while( *p != '\0' ) {
  170.         /* look for a "\..\" */
  171.         if( *p == '.' && *(p+1) == '.'
  172.             && *(p-1) == '\\' && *(p+2) == '\\' ) {
  173.             /* find the previous path component */
  174.             n = 2;
  175.             while( 1 ) {
  176.                 if( (p-n) < &scratchFileName[0] )
  177.                     /* string is "component\..\ ..." */
  178.                     break;
  179.                 ch = *(p-n);
  180.                 if( ch == '\\' || ch == ':' )
  181.                     break;
  182.                 ++n;
  183.             }
  184.             /* eliminate the "component\..\" by copying the */
  185.             /* rest of the  string up n character positions */
  186.             fromPtr = p + 3;
  187.             /* *(p-n) is the last character to keep so: */
  188.             toPtr = p - n + 1;
  189.             /* move p to continue the scan at the beginning */
  190.             /* of the moved part of the string */
  191.             p = toPtr;
  192.             while( 1 ) {
  193.                 *toPtr = *fromPtr++;
  194.                 if( *toPtr++ == '\0' )
  195.                     break;
  196.             }
  197.         } else
  198.             ++p;
  199.     }
  200.  
  201.     return &scratchFileName[0];
  202. }
  203.  
  204.  
  205. struct window * pascal
  206. /* XTAG:findFilenameWindow */
  207. findFilenameWindow(filename)
  208.     unsigned char *filename;
  209. {
  210.     extern struct window *windowList;
  211.     extern struct openFile *files;
  212.  
  213.     register struct window *w;
  214.  
  215.     w = windowList;
  216.     while( w != NULL ) {
  217.         if( stricmp(files[w->fileId].origName, filename) == 0 )
  218.             return w;
  219.         w = w->nextWindow;
  220.     }
  221.     return NULL;
  222. }
  223.  
  224.  
  225. unsigned char * pascal
  226. /* XTAG:findFirstFile */
  227. findFirstFile(pattern, attribute, dta)
  228.     unsigned char *pattern;
  229.     int attribute;
  230.     unsigned char *dta;
  231. {
  232.     extern union REGS rin, rout;
  233.  
  234.     /* set the disk tranfer address */
  235.     rin.h.ah = 0x1A;
  236.     rin.x.dx = (int)dta;
  237.     intdos(&rin, &rout);
  238.     
  239.     /* start looking for matching file names */
  240.     rin.h.ah = 0x4E;
  241.     rin.x.cx = attribute;    /* search attribute */
  242.     rin.x.dx = (int)pattern;
  243.     intdos(&rin, &rout);
  244.  
  245.     if( (rout.x.cflag & 1) == 1 )
  246.         return NULL;
  247.     else
  248.         return dta;
  249. }
  250.  
  251. unsigned char * pascal
  252. /* XTAG:findNextFile */
  253. findNextFile(dta)
  254.     unsigned char *dta;
  255. {
  256.     extern union REGS rin, rout;
  257.  
  258.     /* set the disk tranfer address */
  259.     rin.h.ah = 0x1A;
  260.     rin.x.dx = (int)dta;
  261.     intdos(&rin, &rout);
  262.  
  263.     /* find the next matching file name */
  264.     rin.h.ah = 0x4F;
  265.     intdos(&rin, &rout);
  266.  
  267.     if( (rout.x.cflag & 1) == 1 )
  268.         return NULL;
  269.     else
  270.         return dta;
  271. }
  272.  
  273.  
  274. int pascal
  275. /* XTAG:findTag */
  276. findTag(fn)
  277.     int fn;
  278. {
  279.     extern struct window *selWindow;
  280.     extern long selBegin, selEnd;
  281.     extern int selMode;
  282.     extern unsigned char msgBuffer[];
  283.     extern unsigned char textBuffer[];
  284.     extern unsigned char tagPattern[];
  285.     extern unsigned char tagMarker[];
  286.     extern unsigned char lastTag[STRINGSIZE];
  287.     extern unsigned char tagDTA[64];
  288.     extern long tagLastCp;
  289.     extern int tagLastLine;
  290.     extern int scrRows, scrCols;
  291.     extern int menuLine;
  292.     extern int ignoreCase;
  293.  
  294.     struct window *w;
  295.     register unsigned char  *p;
  296.     unsigned char tagString[STRINGSIZE], *filename, ch;
  297.     int fileId, linesPassed, markerLength;
  298.     long cp, fSize, startCp;
  299.     int row1, row2, col1, col2;
  300.     int ret, saveIgnoreCase, totalLinesPassed;
  301.  
  302.     /* get the string to search for */
  303.     if( fn == FTAGSEL || fn == FCTAGSEL ) {
  304.         getSelection(tagString);
  305.     } else {    /* fn = FTAG || fn == FCTAG */
  306.         filename = getInput("Tag name: ", lastTag, 0);
  307.         if( filename == NULL ) {
  308.             msg("Find tag cancelled", 1);
  309.             ret = 2;
  310.             goto doReturn;
  311.         }
  312.         strncpy(tagString, filename, STRINGSIZE);
  313.     }
  314.  
  315.     /* adjust for ignoring case by forcing all lower case */
  316.     if( ignoreCase != 0 ) {
  317.         p = tagString;
  318.         while( *p != '\0' ) {
  319.             if( isupper(*p) )
  320.                 *p = tolower(*p);
  321.             ++p;
  322.         }
  323.     }
  324.  
  325.     saveIgnoreCase = ignoreCase;
  326.     if( fn == FCTAG || fn == FCTAGSEL ) {
  327.         fileId = getFileId("ptctags.lst");
  328.         cp = searchSpans(fileId, 0L, fileSize(fileId), tagString,
  329.             strlen(tagString), &linesPassed);
  330.         cp += strlen(tagString) + 1;    /* +1 for the ':' */
  331.         p = textBuffer;
  332.         ch = 0;
  333.         while( ch != '\r' ) {
  334.             ch = readChar(fileId, cp++);
  335.             if( ignoreCase && isupper(ch) )
  336.                 ch = tolower(ch);
  337.             *p++ = ch;
  338.         }
  339.         *--p = '\0';    /* -- to backspace to the newline ('\r') */
  340.         filename = textBuffer;
  341. sprintf(msgBuffer, "name=<%s>", filename);
  342. msg(msgBuffer, 1); incon();
  343.         goto ctagsEntry;
  344.     }
  345.  
  346.     /* search for XTAG is faster if it is case sensitive */
  347.     ignoreCase = 0;
  348.     ret = 1;
  349.     if( strcmp(lastTag, tagString) == 0 ) {
  350.         /* continue the last search */
  351.     } else {
  352.         /* new string, start searching again */
  353.         tagLastCp = -1;
  354.         strcpy(lastTag, tagString);
  355.     }
  356.     markerLength = strlen(tagMarker);
  357.  
  358. /* are we starting a new search? */
  359. if( tagLastCp == -1 ) {
  360.     filename = findFirstPatternFile( tagPattern, 0x1, tagDTA );
  361. } else
  362.     filename = retFilename;
  363. while( filename != NULL ) {
  364.     fileId = getFileId(filename);
  365.     if( fileId == -1 ) {
  366.         sprintf(msgBuffer, "Error in tag search: file %s not found",
  367.             filename);
  368.         msg(msgBuffer, 3);
  369.         ret = 5;
  370.         goto doReturn;
  371.     }
  372.     if( tagLastCp == -1 ) {
  373.         startCp = 0L;
  374.         totalLinesPassed = 0;
  375.     } else {
  376.         startCp = tagLastCp;
  377.         totalLinesPassed = tagLastLine;
  378.     }
  379.     fSize = fileSize(fileId);
  380.     sprintf(msgBuffer, "Searching %s for tag \"%s\"", filename,
  381.                                 tagString);
  382.     msg(msgBuffer, 1);
  383. findNextMarker:
  384.     cp = searchSpans(fileId, startCp, fSize, tagMarker, markerLength,
  385.                             &linesPassed);
  386.     /* we found the tag marker, now is it the right tag? */
  387.     if( cp != -1 ) {
  388.         totalLinesPassed += linesPassed;
  389.         cp += markerLength;
  390.         startCp = cp;
  391.         p = tagString;
  392.         while( *p != '\0' ) {
  393.             ch = readChar(fileId, cp++);
  394.             if( saveIgnoreCase && isupper(ch) )
  395.                 ch = tolower(ch);
  396.             if( *p++ != ch ) {
  397.                 startCp = cp;
  398.                 goto findNextMarker;
  399.             }
  400.         }
  401.     }
  402. ctagsEntry:
  403.     closeFile(fileId, 2);
  404.     if( cp != -1L ) {
  405.         p = makeFullPathname(filename);
  406.         w = findFilenameWindow(p);
  407.         if( w != NULL ) {
  408.             doTopWindow(w, 0);
  409.         } else {
  410.             if( 1 ) {
  411.                 row1 = row2 = (menuLine > 0 ? 1 : 0);
  412.                 col1 = col2 = 0;
  413.             } else if(getBox(0, 0, &row1, &col1, &row2, &col2, 0)
  414.                     == 2 ) {
  415.                 msg("Tag window cancelled", 1);
  416.                 ret = 3;
  417.                 goto doReturn;
  418.             }
  419.     
  420.             if( row1 == row2 && col1 == col2 ) {
  421.                 row2 = scrRows-1;
  422.                 if( menuLine < 0 )
  423.                     --row2;
  424.                 col2 = scrCols-1;
  425.             }
  426.             /* do not allow very small windows */
  427.             if( row2 < row1+2 || col2 < col1+10 ) {
  428.                 msg("Size too small, no window created", 1);
  429.                 ret = 4;
  430.                 goto doReturn;
  431.             }
  432.             w = createWindow(filename, row1, col1, row2, col2,
  433.                 CRTOP, 0);
  434.         }
  435.  
  436.         /* make the string we found the selection */
  437.         if( selWindow != w ) {
  438.             eraseSelection();
  439.             selWindow = w;
  440.         }
  441.         selBegin = startCp;
  442.         selEnd = cp - 1;
  443.         selMode = SELCHAR;
  444.  
  445.         doGoto(w, totalLinesPassed);
  446.         ret = 0;
  447.         tagLastCp = cp;
  448.         tagLastLine = totalLinesPassed;
  449.         break;
  450.     } else
  451.         tagLastCp = -1;
  452.     filename = findNextPatternFile(tagDTA);
  453. }
  454.  
  455.     if( ret == 1 ) {
  456.         sprintf(msgBuffer, "Tag \"%s\" not found in \"%s\" files",
  457.             tagString, tagPattern);
  458.         msg(msgBuffer, 1);
  459.         tagLastCp = -1;
  460.     }
  461. doReturn:
  462.     ignoreCase = saveIgnoreCase;
  463.     return ret;
  464. }
  465.  
  466.  
  467. int pascal
  468. /* XTAG:findKeyword */
  469. findKeyword(fn)
  470.     int fn;
  471. {
  472.     extern struct window *selWindow;
  473.     extern long selBegin, selEnd;
  474.     extern int selMode;
  475.     extern unsigned char msgBuffer[];
  476.     extern unsigned char tagPattern[];
  477.     extern unsigned char lastKeyword[STRINGSIZE];
  478.     extern unsigned char keywordDTA[64];
  479.     extern long keywordLastCp;
  480.     extern int keywordLastLine;
  481.     extern int scrRows, scrCols;
  482.     extern int menuLine;
  483.     extern int ignoreCase;
  484.  
  485.     struct window *w;
  486.     unsigned char keywordString[STRINGSIZE], *filename, *p;
  487.     int fileId, linesPassed, keywordLength;
  488.     long cp, fSize, startCp;
  489.     int row1, row2, col1, col2;
  490.     int ret, totalLinesPassed;
  491.  
  492.     /* get the string to search for */
  493.     if( fn == FKEYWORDSEL) {
  494.         getSelection(keywordString);
  495.     } else {    /* fn = FTAG */
  496.         filename = getInput("Keyword: ", lastKeyword, 0);
  497.         if( filename == NULL ) {
  498.             msg("Find keyword cancelled", 1);
  499.             ret = 2;
  500.             goto doReturn;
  501.         }
  502.         strncpy(keywordString, filename, STRINGSIZE);
  503.     }
  504.  
  505.     /* adjust for ignoring case by forcing all lower case */
  506.     if( ignoreCase != 0 ) {
  507.         p = keywordString;
  508.         while( *p != '\0' ) {
  509.             if( isupper(*p) )
  510.                 *p = tolower(*p);
  511.             ++p;
  512.         }
  513.     }
  514.  
  515.     ret = 1;
  516.     if( strcmp(lastKeyword, keywordString) == 0 ) {
  517.         /* continue the last search */
  518.     } else {
  519.         /* new string, start searching again */
  520.         keywordLastCp = -1;
  521.         strcpy(lastKeyword, keywordString);
  522.     }
  523.     keywordLength = strlen(keywordString);
  524.  
  525. /* are we starting a new search? */
  526. if( keywordLastCp == -1 ) {
  527.     filename = findFirstPatternFile( tagPattern, 0x1, keywordDTA );
  528. } else
  529.     filename = retFilename;
  530. while( filename != NULL ) {
  531.     fileId = getFileId(filename);
  532.     if( fileId == -1 ) {
  533.         sprintf(msgBuffer,
  534.             "Error in keyword search: file %s not found",
  535.             filename);
  536.         msg(msgBuffer, 3);
  537.         ret = 5;
  538.         goto doReturn;
  539.     }
  540.     if( keywordLastCp == -1 ) {
  541.         startCp = 0L;
  542.         totalLinesPassed = 0;
  543.     } else {
  544.         startCp = keywordLastCp;
  545.         totalLinesPassed = keywordLastLine;
  546.     }
  547.     fSize = fileSize(fileId);
  548.     sprintf(msgBuffer, "Searching %s for keyword \"%s\"", filename,
  549.                             keywordString);
  550.     msg(msgBuffer, 1);
  551.     cp = searchSpans(fileId, startCp, fSize, keywordString, keywordLength,
  552.                             &linesPassed);
  553.     /* we found the keyword string */
  554.     if( cp != -1 ) {
  555.         totalLinesPassed += linesPassed;
  556.         startCp = cp;
  557.         cp += keywordLength;
  558.     }
  559.     closeFile(fileId, 2);
  560.     if( cp != -1L ) {
  561.         p = makeFullPathname(filename);
  562.         w = findFilenameWindow(p);
  563.         if( w != NULL ) {
  564.             doTopWindow(w, 0);
  565.         } else {
  566.             if( 1 ) {
  567.                 row1 = row2 = (menuLine > 0 ? 1 : 0);
  568.                 col1 = col2 = 0;
  569.             } else if(getBox(0, 0, &row1, &col1, &row2, &col2, 0)
  570.                     == 2 ) {
  571.                 msg("Keyword window cancelled", 1);
  572.                 ret = 3;
  573.                 goto doReturn;
  574.             }
  575.     
  576.             if( row1 == row2 && col1 == col2 ) {
  577.                 row2 = scrRows-1;
  578.                 if( menuLine < 0 )
  579.                     --row2;
  580.                 col2 = scrCols-1;
  581.             }
  582.             /* do not allow very small windows */
  583.             if( row2 < row1+2 || col2 < col1+10 ) {
  584.                 msg("Size too small, no window created", 1);
  585.                 ret = 4;
  586.                 goto doReturn;
  587.             }
  588.             w = createWindow(filename, row1, col1, row2, col2,
  589.                 CRTOP, 0);
  590.         }
  591.  
  592.         /* make the string we found the selection */
  593.         if( selWindow != w ) {
  594.             eraseSelection();
  595.             selWindow = w;
  596.         }
  597.         selBegin = startCp;
  598.         selEnd = cp - 1;
  599.         selMode = SELCHAR;
  600.  
  601.         doGoto(w, totalLinesPassed);
  602.         ret = 0;
  603.         keywordLastCp = cp;
  604.         keywordLastLine = totalLinesPassed;
  605.         break;
  606.     } else
  607.         keywordLastCp = -1;
  608.     filename = findNextPatternFile(keywordDTA);
  609. }
  610.  
  611.     if( ret == 1 ) {
  612.         sprintf(msgBuffer, "Keyword \"%s\" not found in \"%s\" files",
  613.             keywordString, tagPattern);
  614.         msg(msgBuffer, 1);
  615.         keywordLastCp = -1;
  616.     }
  617. doReturn:
  618.     return ret;
  619. }
  620.  
  621. void pascal
  622. /* XTAG:justifyLines */
  623. justifyLines()
  624. {
  625.     /* external declarations */
  626.     extern struct window *selWindow;
  627.     extern long selBegin, selEnd;
  628.     extern int selMode;
  629.     extern int rightMargin;
  630.     extern int tabWidth;
  631.     extern int debug;
  632.  
  633.     long cp, selFirst, selLast;
  634.     int fileId, n, col;
  635.     unsigned char ch, lastCh;
  636.  
  637.     selMode = SELCHAR;
  638.     fileId = selWindow->fileId;
  639.     n = -1;
  640.     selFirst = prevLine(fileId, selBegin, &n);
  641.     selBegin = selFirst;
  642.     n = 1;
  643.     selLast = nextLine(fileId, selEnd, &n);
  644.     col = 0;
  645.     lastCh = 0;
  646.     while( 1 ) {
  647.         ch = readChar(fileId, selBegin);
  648.         if( ch == '\r' ) {
  649.             ch = readChar(fileId, selBegin + 1);
  650.             if( ch == '\n' ) {
  651.                 /* replace the CRLF with a space */
  652.                 selEnd = selBegin + 1;
  653.                 deleteChars(fileId, 0, 0);
  654.                 insertChar(' ');
  655.                 /* point selBegin to the new ' ' */
  656.                 --selBegin;
  657.                 --selLast;
  658.                 ch = ' ';
  659.             } else
  660.                 ch = '\r';
  661.         }
  662.         if( col >= rightMargin ) {
  663.             cp = selBegin;
  664.             while( ch != ' ' && ch != '\n' && ch != '\t' )
  665.                 ch = readChar(fileId, --cp);
  666.             if( ch == ' ' || ch == '\t' ) {
  667.                 selEnd = selBegin = cp;
  668.                 deleteChars(fileId, 0, 0);
  669.                 insertChar('\r');
  670.                 insertChar('\n');
  671.                 /* selLast -1 (for ' ') +2 (for CRLF) */
  672.                 ++selLast;
  673.             }
  674.             /* else NL is already in place */
  675.             col = 0;
  676.             lastCh = '\n';
  677.         } else {
  678.             if( ch == '\t' ) {
  679.                 if( (n = (col % tabWidth)) == 0 )
  680.                     n = tabWidth;
  681.                 col += n;
  682.             } else
  683.                 ++col;
  684.             ++selBegin;
  685.             lastCh = ch;
  686.         }
  687.         if( selBegin >= selLast )
  688.             break;
  689.     }
  690.     /* see how many spaces and tabs are at the end */
  691.     /* selEnd remembers the end of the string of spaces and tabs */
  692.     selEnd = --selBegin;
  693.     /* loop through the spaces and tabs */
  694.     while( 1 ) {
  695.         ch = readChar(fileId, selBegin);
  696.         if( ch != ' ' && ch != '\t' )
  697.             break;
  698.         --selBegin;
  699.     }
  700.     /* (selBegin went one too far) */
  701.     /* if we found some spaces, then delete them */
  702.     if( ++selBegin <= selEnd ) {
  703.         selLast -= (selEnd - selBegin + 1);
  704.         deleteChars(fileId, 0, 0);
  705.     }
  706.     if( lastCh != '\n' ) {
  707.         insertChar('\r');
  708.         insertChar('\n');
  709.         selLast += 2;
  710.     }
  711.     selBegin = selFirst;
  712.     selEnd = selLast - 1;
  713.     redrawBox(selWindow->row1, selWindow->col1,
  714.         selWindow->row2, selWindow->col2);
  715.     updateScreen(selWindow->row1, selWindow->row2);
  716. }
  717.