www.pudn.com > CENVID.rar > CMMEDIT.CMM


/************************************************************************** 
 *** CmmEdit - A simple text editor.  This is the tutorial program from *** 
 ***           chapter 3 of the CEnvi manual.                           *** 
 **************************************************************************/ 
 
main(ArgCount,ArgList) 
{ 
   FileName = GetFileName(ArgCount,ArgList); 
   ReadFile(FileName); 
   if ( Edit() )  // Edit returns TRUE if changes made to file 
      WriteFile(FileName); 
} 
 
 
GetFileName(argc,argv) 
   // return a file name from the program input arguments, or prompt user for the 
   // file name if none was supplied at the command line.  exit() program if no 
   // file name is gotten. 
{ 
   // If at least one argument was supplied to main() in addition to the source 
   // file name (which is always supplied), then that argument is the  file name. 
   if ( 1 < argc ) 
      return(argv[1]); 
 
   // File name wasn't supplied on the command line, and so prompt for name. 
   printf("Enter file name to edit: "); 
   filespec = gets(); 
   if ( filespec == NULL || filespec[0] == 0 )  // no name was entered so quit 
      exit(EXIT_FAILURE); 
   return(filespec); 
} 
 
Text[0] = "";  // Text is an array of s text strings; one for each file line 
 
ReadFile(FileSpec)   // read FileSpec file into global data.  exit() if error. 
{ 
   // Open the file, in text mode, for reading into Text. 
   fp = fopen(FileSpec,"rt"); 
   if ( fp == NULL ) { 
      // The file doesn't exist, and so ask user if they want to quit.  If they 
      // don't want to create file then simply exit this program.  If they do 
      // want to create file then we're done, as Text is already initialized. 
      printf("File \"%s\" does not exist.  Create file?  Y/N ",FileSpec); 
      do { 
         key = toupper(getch()); // make uppercase to compare to Y and N 
         if ( key == 'N' ) 
            exit(EXIT_FAILURE); 
      } while( key != 'Y' );     // wasn't Y or N, and so try again 
   } else { 
      // File opened.  Read each line of file into the next element of Text. 
      for ( LineCount = 0; NULL != (line = fgets(fp)); LineCount++ ) { 
         // line set to new string for next line in the text file.  Set the next 
         // line of Text to this line. 
         Text[LineCount] = line; 
      } 
      fclose(fp); // Should always close a file that has been opened. 
   } 
} 
 
WriteFile(FileSpec)  // write global data to back to FileSpec.  exit() if error. 
{ 
   // Open FileSpec for writing in text mode.  If the file already exists then 
   // truncate the file.  If file doesn't exist then create it. 
   fp = fopen(FileSpec,"wt"); 
   if ( fp == NULL ) { 
      printf("\aUnable to open \"%s\" for writing.\a\n"); 
      exit(EXIT_FAILURE); 
   } 
 
   // write every line of Text into fp 
   for ( i = 0; i <= GetArraySpan(Text); i++ ) 
      fputs( Text[i], fp ); 
 
   // close fp 
   fclose(fp); 
} 
 
 
// define movement keys - Give values over 0x100 to distinguish from text 
#define  MIN_CUR_MOVE   0x101 
#define  UP             0x101 
#define  DOWN           0x102 
#define  LEFT           0x103 
#define  RIGHT          0x104 
#define  PG_UP          0x105 
#define  PG_DN          0x106 
#define  HOME           0x107 
#define  END            0x108 
#define  BK_TAB         0x109 
#define  DELETE         0x110 
 
GetKeyChar()   // return key from keyboard, ascii for above #defined 
{ 
   if defined(_DOS_)  ||  defined(_OS2_) { 
      // DOS and OS/2 return 0 on first getch for extended keys 
      KeyCode = getch(); 
      if ( KeyCode == 0 ) { 
         // set value for extended key; these value found using KeyCode.cmd 
         switch( getch() ) { 
            case 0x48:     KeyCode = UP;        break; 
            case 0x50:     KeyCode = DOWN;      break; 
            case 0x4B:     KeyCode = LEFT;      break; 
            case 0x4D:     KeyCode = RIGHT;     break; 
            case 0x49:     KeyCode = PG_UP;     break; 
            case 0x51:     KeyCode = PG_DN;     break; 
            case 0x47:     KeyCode = HOME;      break; 
            case 0x4F:     KeyCode = END;       break; 
            case 0x0F;     KeyCode = BK_TAB;    break; 
            case 0x53;     KeyCode = DELETE;    break; 
            default: break;   // return 0, which will do nothing 
         } 
      } 
   } else { 
      // Windows version 
      KeyCode = getch(); 
      if ( 0x100 < KeyCode ) { 
         switch ( KeyCode ) { 
            // special values in the following table come from KeyCode.cmm 
            case 0x126:    KeyCode = UP;        break; 
            case 0x128:    KeyCode = DOWN;      break; 
            case 0x125:    KeyCode = LEFT;      break; 
            case 0x127:    KeyCode = RIGHT;     break; 
            case 0x121:    KeyCode = PG_UP;     break; 
            case 0x122:    KeyCode = PG_DN;     break; 
            case 0x124:    KeyCode = HOME;      break; 
            case 0x123:    KeyCode = END;       break; 
            case 0x109;    KeyCode = BK_TAB;    break; 
            case 0x12E;    KeyCode = DELETE;    break; 
            default:       KeyCode = 0;         break; 
         } 
      } 
   } 
   return(KeyCode); 
} 
 
 
Edit()   // Edit file.  This is were the hard work happens.  exit() if error. 
{        // Return FALSE if no editing was done, else return TRUE. 
   LineCount = 1 + GetArraySpan(Text); // how many lines in file 
 
   // Initialize screen: get its dimensions, and cursor location. 
   ScreenClear(); 
   ScreenDimension = ScreenSize(); 
   CursorCol = CursorRow = 0; // initialize cursor position 
 
   // Starting at row 0, draw all lines on screen. Initialize Start as structure 
   // for upper-left visible portion of file. Then draw the file. 
   Start.Row = Start.Col = 0; 
   DrawVisibleTextLines( Start, ScreenDimension, LineCount ); 
   DrawnStart = Start;  // remember which lines were drawn 
   CursorStatus(CursorRow,CursorCol,Start,ScreenDimension); 
 
   // FileWasEdited is boolean to say if changes made 
   FileWasEdited = FALSE; 
 
   // Stay here getting all keyboard input until escape is pressed 
   #define  ESCAPE_KEY  '\033' 
   while ( (key = GetKeyChar()) != ESCAPE_KEY ) { 
 
      // special keyboard codes are returned if getch() first 
      switch( key ) { 
         case UP:    CursorRow--;   break; 
         case DOWN:  CursorRow++;   break; 
         case LEFT:  CursorCol--;   break; 
         case RIGHT: CursorCol++;   break; 
         case HOME:  CursorCol = 0; break; 
         case END: 
            // go to end of visible line, but not including newline 
            CursorCol = strlen(Text[CursorRow]); 
            if ( 0 < CursorCol  &&  Text[CursorRow][CursorCol-1] == '\n' ) 
               CursorCol--; 
            break; 
         case PG_UP: 
            CursorRow -= (ScreenDimension.row - 1); 
            Start.Row -= (ScreenDimension.row - 1); 
            break; 
         case PG_DN: 
            CursorRow += (ScreenDimension.row - 1); 
            Start.Row += (ScreenDimension.row - 1); 
            break; 
 
         #define  TABSIZE  8 
         case '\t': 
            CursorCol += TABSIZE; 
            CursorCol -= CursorCol % TABSIZE; 
            break; 
         case BK_TAB: 
            CursorCol -= TABSIZE; 
            CursorCol -= CursorCol % TABSIZE; 
            break; 
 
         #define BACKSPACE '\010' 
         case BACKSPACE: 
            // Back space is just like deleting from one column to the left, 
            // and so check that we're not on the first column and then move 
            // left a column and let control fall to DELETE 
            if ( --CursorCol < 0 )	{ 
               // backspace from beginning of line; move to end of previous line 
               if ( CursorRow == 0 ) { 
                  // cannot backup to earlier row, so do nothing 
                  CursorCol = 0; 
                  break; 
               } 
               CursorCol = strlen(Text[--CursorRow]) - 1; 
            } 
         case DELETE: 
            if ( DeleteCharacterAtCursor(CursorRow,CursorCol,LineCount) ) { 
               FileWasEdited = TRUE; 
               DrawnStart.row = -1; // force screen redraw 
            } 
            break; 
 
         case '\r': 
            // Add a newline at the current position 
            InsertAsciiCharacter('\n',Text[CursorRow],CursorCol); 
            FileWasEdited = TRUE; 
 
            // a line must be opened up in Text, and all the data moved 
            for( i = LineCount++; CursorRow + 1 < i; i-- ) 
               strcpy( Text[i], Text[i-1] ); 
 
            // move text from after cursor to next line, and end this line 
            strcpy(Text[CursorRow+1],Text[CursorRow] + CursorCol + 1); 
            Text[CursorRow][CursorCol + 1] = 0; 
 
            // finally, move cursor to beginning of next line, and redraw screen 
            CursorRow++, CursorCol = 0; 
            DrawnStart.row = -1; // force screen redraw 
 
            break; 
 
         default: 
            if ( isprint(key) ) { 
               InsertAsciiCharacter(key,Text[CursorRow],CursorCol++); 
               FileWasEdited = TRUE; 
               // redraw this row 
               ScreenCursor(0,CursorRow - Start.row); 
               printf("%.*s",ScreenDimension.col,Text[CursorRow] + Start.col); 
            } else { 
               // the key that was pressed was not handled.  Beep at the user as a 
               // warning, but otherwise alter nothing. 
               putchar('\a'); 
            } 
            break; 
      } 
 
      // Check that cursor position has not gone out of range 
      if ( CursorRow < 0 ) CursorRow = 0; 
      if ( CursorCol < 0 ) CursorCol = 0; 
      if ( LineCount <= CursorRow ) CursorRow = LineCount - 1; 
 
      // Check that Start.Row has not gone out of range 
      MaxStartRow = LineCount - (ScreenDimension.row - 1) 
      if ( MaxStartRow < Start.Row ) 
         Start.Row = MaxStartRow; 
      if ( Start.Row < 0 ) Start.Row = 0; 
 
      // If cursor does not now fit on visible screen, then move 
      // screen so that cursor does fit on it. 
      while( CursorRow < Start.Row ) Start.Row--; 
      while( CursorCol < Start.Col ) Start.Col--; 
      while( Start.Row + ScreenDimension.Row - 1 <= CursorRow ) Start.Row++; 
      while( Start.Col + ScreenDimension.Col <= CursorCol ) Start.Col++; 
 
      // if screen must be redrawn, then do so now 
      if ( DrawnStart != Start ) { 
         ScreenClear(); 
         DrawVisibleTextLines( Start, ScreenDimension, LineCount ); 
         DrawnStart = Start; 
      } 
 
      // key was processed, so redisplay screen state 
      CursorStatus(CursorRow,CursorCol,Start,ScreenDimension); 
   } 
 
   // Return TRUE if file was edited, else false 
   ScreenClear(); 
   return(FileWasEdited); 
} 
 
 
InsertAsciiCharacter(c,str,offset)	// insert c in str at offset 
{ 
   // The newline at the end of the string can be a problem later, so for now 
   // temporarily remove the newline then we'll put it back in when we're done. 
   len = strlen(str); 
   AddNewLine = ( len != 0  &&  str[len-1] == '\n' ); 
   if ( AddNewLine ) 
      str[--len] = 0; 
 
   // If the current cursor position is longer than the line, then add spaces. 
   while( len < offset ) 
      str[len++] = ' '; 
 
   // If this character won't be at end of the string, then move all characters 
   // from here to the end of the string one space forward.  This may be done 
   // simply with a strcpy because Cmm ensures that overwriting is safe. 
   if ( offset < len ) { 
      strcpy(str + offset + 1,str + offset); 
      len++; 
   } 
 
   // At last, put the character in the string 
   str[offset] = c; 
 
   if ( AddNewLine ) // put the newline character back into the string 
      strcat(str,"\n"); 
} 
 
 
DeleteCharacterAtCursor(row,col,TotalLineCount) 
   // delete character at cursor position.  Return TRUE if a character was 
   // delete else return FALSE.  This function may alter TotalLineCount. 
{ 
   str = Text[row]; 
   len = strlen(str); 
   if ( row < (TotalLineCount - 1) ) 
      len--; 
 
   if ( col < len ) { 
      // This is the simple case.  copy string to this location from next char 
      strcpy(str + col,str + col + 1); 
   } else { 
      // deleting from the end of the string or from beyond.  Must bring in 
      // from next row. 
      if ( row == (TotalLineCount - 1) ) 
         return(FALSE); // no following text to copy to here 
 
      // fill in spaces from end of text to this location 
      for( i = len; i <= col; i++ ) 
         str[i] = ' '; 
 
      // copy from next string to the end of this string 
      strcpy( str + col, Text[row+1] ); 
      // One newline has been removed, and so there are now one fewer lines 
      // in the file.  Copy all of rows down one element in the Text array. 
      TotalLineCount--; 
      for ( i = row + 1; i < TotalLineCount; i++ ) 
         Text[i] = Text[i+1]; 
      SetArraySpan(Text,TotalLineCount - 1); 
   } 
   return(TRUE); 
} 
 
 
DrawVisibleTextLines(StartPosition,ScreenSize,TextLineCount) 
   // display visible portion of file. StartPosition is initial .row and .col 
   // that is visible.  ScreenSize show .col and .row width and height of screen. 
{ 
   // verify that the screen position is not invalid; negative would be bad. 
   assert( 0 <= StartPosition.row  &&  0 <= StartPosition.col ); 
   // Also, this function assumes that at least some lines are visible at the 
   // top of the screen, and so verify that this is true. 
   assert( StartPosition.row < TextLineCount ); 
 
   // draw all visible lines from Text; leave bottom line free for messages. 
   for ( row = 0; row < (ScreenSize.row-1); row++ ) { 
      Line = Text[StartPosition.row + row]; 
      // draw this line on the screen from StartPosition.row, remembering 
      // to clip at the right edge of screen if the line is too long 
      LineLen = strlen(Line) - StartPosition.col; 
      if ( 0 < LineLen ) { // only print if characters to print 
         ScreenCursor(0,row); 
         printf("%.*s",ScreenSize.col,Line + StartPosition.col); 
      } 
   } 
} 
 
 
CursorStatus(CRow,CCol,StartPosition,ScreenSize) 
{ 
   // show current file cursor position; based at 1 
   ScreenCursor(5,ScreenSize.row-1); 
   printf("Status: row %-3d col %-3d",CRow + 1,CCol + 1); 
 
   // put cursor at correct position on screen 
   ScreenCursor(CCol - StartPosition.Col,CRow - StartPosition.Row); 
} 
 
 
//DebugPrintf(FormatString,arg1,arg2,arg3/*etc...*/) 
//   // printf() line on bottom of string, then get key 
//{ 
//   // format message into a string 
//   va_start(VaList,FormatString); 
//   vsprintf(msg,FormatString,VaList); 
//   va_end(VaList); 
// 
//   // Save the cursor position, display this message on the bottom of the screen, 
//   // get a key, and then return.  This is very non-intrusive. 
//   SaveCursor = ScreenCursor(); 
//   ClearBottomLine(); 
//   msg[ScreenSize().Col - 1] = '\0';   // don't let line get too long 
//   while ( NULL != (nl = strchr(msg,'\n')) ) // change newlines to spaces 
//      nl[0] = '_'; 
//   ScreenCursor(0,ScreenSize().Row-1); 
//   printf("%s",msg); 
//   GetKeyChar(); 
//   ClearBottomLine(); 
//   ScreenCursor(SaveCursor.Col,SaveCursor.Row); 
//} 
// 
//ClearBottomLine() // called by DebugPrintf() to clear last lie 
//{ 
//   ScreenCursor(0,ScreenSize().Row - 1); 
//   printf("%*s",ScreenSize().Col-1,""); 
//}