I
266
fc
Part III M MMURTL-Specific Details
While working with MMURTL, most of my efforts have been directed at the operating sys-
tem itself. I wrote literally hundreds of little programs to test detailed aspects of MMURTL.
Only in the last year or so have I made any real effort to produce even the simplest of applica-
tions or externally executable programs.
One of the first programs was a simple command-line interpreter. It has since been expanded,
and its real purpose is to illustrate certain job-management aspects of MMURTL. However, it
does work and it's useful in its own right. It's still missing some important pieces (at least, some
that are important to me), but it's a good start.
Other test programs that may be useful to you have been included on the CD-ROM that ac-
companies this book. Some of these programs have their source code listed and their operation
discussed in this chapter. To see what has been included on the CD-ROM, see “What's On
the CD-ROM.”
Command-Line Interpreter (CLI)
The MMURTL command-line interpreter (called CLI) is a program that accepts commands
and executes them for you.
The CLI screen is divided into two sections. The top line is the status line. It provides informa-
tion such as date, time, CLI version, job number for this CLI, and your current path.
The rest of the screen is an interactive display area that will contain your command prompt.
The command prompt is a greater-than symbol (>) followed by a reverse video line. This line
is where you enter commands to be executed by the CLI.
The CLI contains some good examples of using a wide variety of the job-management func-
tions of the MMURTL operating system. The source code to the CLI is listed in this chapter
and on the CD-ROM.
Internal Commands
The CLI has several built-in commands and can also execute external commands (load and
execute .RUN files).
The internal commands are as follows:
Cls
Clear screen
Copy
Copy a file
Dir
Directory listing
Debug
Enter debugger
Del
Delete a file
Dump
Hex dump of a file
Chapter 16 ■ MMURTL Sample Software I 2^7 Exit Exit and terminate CLI (return to monitor) Help Display list of internal commands Monitor Return to monitor (leave this CLI running) MD Make directory Path Set file access path (new path, for example, D:\DIR) RD Remove directory Rename Rename a file Run Execute a RUN file (replacing this CLI) Type Type the contents of text file to the screen Each of these commands, along with an example of its use, is described in the following sec- tions. CIs (Clear Screen) The CIs command removes all the text from the CLI screen and places the command prompt at the top of the screen. Copy (Copy a File) The Copy command makes a copy of a file with a different name in the same directory, or with the same name in a different directory. If a file exists with the same name, you are prompted to confirm that you want to overwrite it. The command format is as follows:
Copy THISFILE.TXT THATFILE.TXT Wildcards (pattern matching) and default names (leaving one parameter blank) are not sup- ported in the first version of the CLI. Both names must be specified. The current path will be properly used if a full file specification is not used. Dir (Directory Listing) The Dir command lists all the files for your current path or a path that you specify, as follows: Dir C:\SOURCE\ The listing fills a page and prompts you to press Enter for the next screen full. Pressing Esc will stop the listing. The listing is in multiple columns, as follows: FILENAME SIZE DATE TIME TYPE FIRST_CLUSTER The first-cluster entry is a hexadecimal number used for troubleshooting the file system. The Type is the attribute value taken directly from the MS-DOS directory structure. The values are as follows:
^g| Part III ■ MMURTL-Specific Details 00 or 20 is a normal file. 01 or 2 1 is a read-only file. 02 or 22 is a hidden file. 04 or 24 is a system file. 10 is a subdirectory entry. 08 is a volume name entry (found only in the root). File values greater than 20 indicate that the archive attribute is set. It means the file has been modified or newly created since the archive bit was last reset. Debug (Enter Debugger) The Debug command enters MMURTL's built-in Debugger. To exit the Debugger, press the Esc key. For more information on the Debugger, see Chapter 12, “The Debugger.” Del (Delete a File) The Del command deletes a single file from a disk device. Here's an example:
Del C: \TEST\FILE.TXT Dump (Hex Dump of a File) The Dump command dumps the contents of a file to the screen in hexadecimal and ASCII for- mat, as follows (16 bytes per line): ADDRESS 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 xxxxxxxxxxxxxxx The address is the offset in the file. A new line is displayed for each 1 6 bytes. The text from the line is displayed following the hexadecimal values. If the character is not ASCII text, a period is displayed instead. Dump pauses after each screen full of text. Exit (Exit the CLI) The Exit command exits this CLI and terminates this job. The keyboard and video are as- signed to the monitor if this was the active job. Help (List Internal Commands) The Help command opens the text file called Help.CLI (if it exists) and displays it on the screen. The file should be located in the MMURTL system directory. This is an ASCII text file, and you may edit it as needed. Monitor (Return to Monitor) If this is the active CLI (the one displayed), the Monitor command will assign the keyboard and video back to the monitor. This CLI will still be executing.
Chapter 16 ■ MMURTL Sample Software A4D (Make Directory) The MD command creates an empty directory in the current path or the path specified on the command line, for example:
MDOLDSTUFF MD C:\SAMPLES\OLDSTUFF The directory tree up to the point of the new directory must already exist. In other words, the tree will be extended only one level. Paf/i (Set File-Access Path) The Path command sets the file-access path for this job. Do not confuse the term path with the MS-DOS version of the Path command. In MMURTL, a job's path is simply a prefix used by the file system to make complete file specifications from a filename. Unlike a single-tasking operating system, no system-wide concept of a “current drive” or “current directory” can be assumed. Each job control block maintains a context for its par- ticular job. With any file-system operation (opening, renaming, listing, and so on), if you don't specify a full file specification (including the drive), the file system appends your specification to the current path for this job. The path you set in the CLI will remain the path for any job that is run or any external com- mand that is executed for this CLI. The path may also be set programmatically (by a program or utility). Don't be surprised if you return from a program you have executed to the CLI with a different path. The current path is displayed on the status line in the CLI. Each CLI (job) has its own path, which is maintained by the operating system in the job control block. The path must end with the backslash. Here are some examples: Path D:\DIR\ PATHC:\ C:\MMSYS\ H RD (Remove Directory) The RD command removes an empty directory in the current path or in the path specified on the command line: RD OLDSTUFF RD C:\MMURTL\OLDSTUFF The directory must be empty. The only entries allowed are the two default entries ( . and . . )
270 Part III M MMURTL-Specific Details Rename (Rename a File) The Rename command changes the name of a file. If the new filename exists, you are prompted to either overwrite existing file or cancel the command. The file must not be open under any other job. The command format is as follows:
Rename OLDNAME.TXT NEWNAME.TXT Run (Execute a .RUN File) The Run command executes the named RUN file in place of this CLI (same job number). The path remains the same as was set in the CLI. Here are some examples: Run C:\NewEdit\NewEdit.run New.Txt Run D:\MMDEV\Test.run Any parameters specified after the RUN-file name are passed to the RUN file. Type (View a Text File on the Screen) The Type command displays the contents of a text file to the screen one page at a time, pausing after each page. Here's an example: Type C:\MMSYS\Commands.CLI External Commands External commands are those not built into the CLI. These are RUN files (MMURTL execut- able files with the file suffix .RUN). MMURTL looks for external commands in three places (in this order):
Chapter 16 ■ MMURTL Sample Software ;Any line beginning with semicolon is a comment. EDIT C:\MSAMPLES\EDITOR\Edit.run Print C: \MSamples\Print\Print . run CM32 C:\CM32M\CM32.run DASM D:\DASMM\DASM.run ;End of Command. cli At least one space or tab must be between the command name and the RUN file. There must also be at least one space or tab between the RUN-file name and the arguments (if they exist). Additional spaces and tabs are ignored. Any parameters you specify on the command line in the CLI are passed to the RUN file that is executed. You may also add parameters to the line in COMMANDS. CLI. In this case, the parameters you specified on the command line are appended to those you have in the COMMANDS.CLI file. F\ Global “Hot Keys” The system recognizes certain keys that are not passed to applications to perform system-wide functions. Global hot keys are those that are pressed while the Ctrl and Alt keys are held down. The CLI doesn't recognize any hot keys, but other applications might. Ctrl- Alt-Page Down Switches video and keyboard to next job or the monitor. This key se- quence is received and acted upon by the Monitor program. Ctrl-Alt-Del Terminates the currently displayed job. The monitor and the Debugger will not terminate using this command. This key sequence is received and acted upon by the Monitor program. CLI Source Listing The following is the source code for version 1.0 of the Command Line Interpreter. The In- clude files from the OS Source directory contain the ANSI C prototype for the operating sys- tem functions. Listing 16.1. The source code for the Command Line Interpreter Version 1.0. #define U32 unsigned long #define S32 long #define U16 unsigned int #define S16 int #define U8 unsigned char #define S8 char #define TRUE 1 #define FALSE 0 #include <stdio.h>
I
272
Part III ■ MMURTL-Specific Details
Listing 16.1. continued
#include <string.h>
#include <ctype.h>
/* Includes for OS public calls and structures */
#include “\OSSOURCE\MKernel.h”
#include “\OSSOURCE\MMemory.h”
#include “\OSSOURCE\MData.h”
#include “\OSSOURCE\MTimer.h”
#include “\OSSOURCE\MVid.h”
#include “\OSSOURCE\MKbd.h”
#include “\OSSOURCE\MJob.h”
#include " \OSSOURCE\MFiles.h”
#include “\OSSOURCE\Status.h”
/******************** BEGIN DATA ********************/
/* Define the Foreground and Background colors */
#define EDVID BLACK jBGWHITE
#define NORMVID WHITE | BGBLACK
#define STATVID BLACK | BGCYAN
long iCol, iLine;
/* aStatLine is exactly 80 characters in length */
char aStatLine[ ] =
“mm/dd/yy 00:00:00 CLI V1.0 Job Path:
char aPath[70] ;
long cbPath;
char fUpdatePath;
char sdisk, syspath[50]; /* holds system disk and system path */
char hlppath[60]; /* Path to help file */
char clipath[60]; /* Path to CLI */
char cmdpath[60]; /* Path to command file */
char aCmd[80]; /* Command line */
long cbCmd = 0;
unsigned char Buffer[512];
unsigned char bigBuf [4096] ;
char text[70] ;
char ExitChar;
unsigned long GPExch; /* Messaging for Main */
unsigned long GPMsg[2];
unsigned long GPHndl;
unsigned long StatExch; /* Messaging for status task */
long StatStack[256] ; /* 1024 byte stack for stat task */
long time, date;
Chapter 16 ■ MMURTL Sample Software s long JobNum; /* array of internal commands for parsing */ #define NCMDS 16 char paCmds[NCMDS+1][10] = { "” . /* 0 external */ “xxxxx” , /* 1 - not used */ “CIS”, /* 2 */ “Copy”, /* 3 */ “Del”, /* 4 */ “Dir” , /* 5 */ “Debug” , /* 6 */ " Dump " , /* 7 */ “Exit”, /* 8 */ “Help”, /* 9 */ “MD”, /* 10 */ “Monitor” , /* 11 */ “Path”, /* 12 */ “RD”, /* 13 */ “Ren” , /* 14 */ “Run” , /* 15 */ “Type”, }; /* 16 */ #define EXTCMD 0 /* External Comman #define RESVDCMD 1 #define CLSCMD 2 #define COPYCMD 3 #define DELCMD 4 #define DIRCMD 5 #define DBGCMD 6 #define DUMPCMD 7 #define EXITCMD 8 #define HELPCMD 9 #define MAKEDIR 10 #define MONCMD 11 #define PATHCMD 12 #define REMDIR 13 #define RENCMD 14 #define RUNCMD 15 #define TYPECMD 16 long CmdNum = 0; char *apParam[13] ; long acbParam[13] ; #define nParamsMax 13 /* Param 0 is cmd name */ #define ErcBadParams 80 /* Directory Entry Records, 16 records 32 bytes each */ struct dirstruct { U8 Name[8]; U8 Ext[3];
274 Part III ■ MMURTL-Specific Details Listing 16.1. continued U8 Attr; U8 Rsvd[10]; U16 Time; U16 Date; U16 StartClstr; U32 FileSize; } dirent[16]; BEGIN CODE ‘**/ This is the status task for the CLI. It updates the status line, which has the time and path. It runs as a separate task and is started each time the CLI is executed. *****************: void StatTask(void) { for(;;) { if (fUpdatePath) if (cbPath > 30) cbPath = 30; FillData(&aStatLine[47], 30, ' ‘); CopyData(aPath, &aStatLine[47] , cbPath) fUpdatePath = 0; GetCMOSDate(&date) ; aStatLine[0] aStatLine[1] aStatLine[3] aStatLine[4] aStatLine[6] aStatLine[7] ((date » 20) & 0x0f); / month / ((date » 16) & 0x0f); ((date » 12) & 0x0f); / Day / ((date » 8) & 0x0f); ((date » 28) & 0x0f); / year / ((date » 24) & 0x0f); GetCMOSTime(&time) ; aStatLine[l0] = ‘0’ aStatLine[H] = ‘0’ aStatLine[13] = ‘0’ aStatLine[14] = ‘0’ aStatLine[16] = ‘0’ aStatLine[l7] = ‘0’ ((time » 20) & 0x0f) ((time » 16) & 0x0f) ((time » 12) & 0x0f) ((time » 8) & 0x0f); ((time » 4) & 0x0f); (time & 0x0f); PutVidChars(0,0, aStatLine, 80, STATVID) Sleep(100); / sleep 1 second / } / forEVER */ This displays error code text string if listed and NON zero.
Chapter 16 ■ MMURTL Sample Software void CheckErc (unsigned long ere) { char pSt; char st[40] ; FillData(st, 40, 0) switch (ere) case 0: return; case 1 : pSt = case 4: pSt = case 80: pSt = case 101 : pSt = case 200: pSt = case 201: pSt = case 202: pSt = case 203: pSt = case 204: pSt = case 205: pSt = case 208: pSt = case 222: pSt = case 223: pSt = case 226: pSt = case 228: pSt = case 230: pSt = case 231 : pSt = default: jprintf (st, pSt = st; break ; ‘End of file” ; break; ‘User cancelled”; break; ‘Invalid parameters”; break; ‘Out of memory (need more for this)"; break; ‘Invalid filename (not correct format)"; break; ‘No such drive”; break; ‘The name is not a file (it's a directory)"; break: ‘File doesn't exist”; break; ‘Directory doesn't exist”; break; ‘File is Readonly”; break; ‘File in use” ; break; ‘Can't rename across drives”; break; ‘Can't rename across directories”; break; ‘File Already Exists (duplicate name)"; break; ‘Root directory is full”; break; ‘Disk is full (bummer)"; break; ‘Directory is full”; break; “Error %05d on last command”, ere); printf ("%s\r\n” , pSt; This simply does a software interrupt 03 (Debugger) void GoDebug(void) { #asm INT 03 #endasm return; } This is called to initialize the screen. If we are returning from an external command, we do not reset the cursor position. fNew is used to determine if we do or not. ‘/ ***/ void InitScreen(int fNew)
276 Part III ■ MMURTL-Specific Details Listing 16.1. continued SetNormVid(NORMVID); GetXY(&iCol, &iLine); if (fNew) { iCol = 0; iLine = 2; ClrScr(); } SetXY(iCol, iLine); PutVidChars(0,0, aStatLine, 80, STATVID) ; return; } This does a hex dump to the screen of a memory area passed in by “pb” **********************************************/ U32 Dump(unsigned char *pb, long cb, unsigned long addr) { U32 ere, i, j , KeyCode; unsigned char buff [17]; ere = 0; GetXY(&iCol, &iLine); while (cb) { printf("%08x “, addr); if (cb > 15) j=16; else j = cb; for (i=0; i<j ; i++) { printf("%02x “,*pb); buff[i] = pb++; if (buff[i] < 0x20) buff[i] = 0x2E; if (buff[i] > 0x7F) buff[i] = 0x2E; } buff(i+1] = 0; printf("%s\r\nn , &buff[0]); ++iLine; if (iLine >= 22) { SetXY(0,23); printf(“ESC to cancel, any other key to continue. ReadKbd(&KeyCode, 1); / ReadKbd, wait */ if ((KeyCode & 0xff) == 0x1b) { return 4; } InitScreen(TRUE); SetXY(0,1); iLine =1;
Chapter 16 ■ MMURTL Sample Software if (cb > 15) cb-=16; else cb=0; addr+=j ; } return ere; } This sets up to call the dump routine for each page. This expects to find the filename in param[1]. long DoDump(void) { unsigned long j, k, 1, ere, dret, fh; ere = 0; if ((apParam[1 ] ) && (acbParam[1 ] ) ) { if (iLine >= 23) { ScrollVid(0, 1,80, 23,1); SetXY(0,23); } ere = 0penFile(apParam[1 ] , acbParam[1], ModeRead, 1, &fh] if (!erc) { j=0; /* file lfa */ GetFileSize(fh, &k); while ((j<k) && (!erc)) { FillData(Buffer, 512, 0); ere = ReadBytes (fh, Buffer, 512, &dret); if (k-j > 512) 1 = 512; else l=k- j ; if (ere < 2) ere = Dump(Buffer, dret, j); j+=512; } CloseFile (fh); } } else printf (“Filename not given\r\n”) ; return ere; } This types a text file to the screen. long DoType(char *pName, long cbName) { long i, j, lfa, ere, dret, fh, KeyCode; ere = 0;
Part III ■ MMURTL-Specific Details Listing 16.1. continued if ((pName) && (cbName)) { if (iLine >= 23) { ScrollVid (0,1,80,23,1); SetXY(0,23); } ere = OpenFile(pName, cbName, 0, 1, &fh); if (!erc) { FillData(Buffer, 512, 0); dret = 1; while ((erc<2) && (dret)) { GetFileLFA(fh, &lfa); ere = ReadBytes (fh, Buffer, 78, &dret); i ■ 1; while ((Buffer[i-1] != 0x0A) && (i < dret)) { i++; } for (j=0; j<=i; j++) { if ((Buffer[j] == 0x09) || (Buffer[j] == 0x0d) \ (Buffer[ j] == 0x0a)) Buffer[ j] = 0x20; } if (dret) { PutVidChars(0, iLine, Buffer, i, NORMVID); iLine++; } SetXY(0, iLine); SetFileLFA(fh, lfa+i); if (iLine >= 22) { SetXY(0,23); printf(“ESC to cancel, any other key to continue...") ReadKbd(&KeyCode, 1); /* ReadKbd, wait */ if ((KeyCode & 0xff) == 0x1b) return(4) ; InitScreen(TRUE); SetXY(0,1); iLine =1 ; } } printf ( “\r\nError: %d\r\n”, ere); CloseFile (fh); } } else printf ( “Filename not given\r\n”); return ere; }
Chapter 16 ■ MMURTL Sample Software This reads DOS FAT date & time and converts it into strings with the format MM/DD/YY HH/MM/SS. This is used by the directory listing. void CnvrtFATTime(U16 time, U16 date, char pTimeRet, char pDateRet) { U32 yr,mo,da,hr,mi,se; char st[15] ; yr = ((date & 0xFE00) » 9) + 1980 - 1900; if (yr > 99) yr -=100; mo = (date & 0X01E0) » 5; da = date & 0X001F; hr = (time & 0xF800) » 11; mi = (time & 0X07E0) » 5; se = (time & 0X001F) * 2; sprintf (st, “%02d:%02d:%02d” ,hr,mi,se) ; CopyData(st, pTimeRet, 8); sprintf ( st , “%02d -%02d -%02d " , mo , da , yr) ; CopyData(st, pDateRet, 8); } Copy a single file with overwrite checking. Use block mode for speed. This uses a conservative 4KB buffer. This could be made a good deal faster by allocating a large chunk of memory and using it instead of the 4KB static buffer. I******************************************************/ U32 CopyFile(char *pFrom, U32 cbFrom, char *pTo, U32 cbTo) { U32 fhTo, fhFrom, bytesWant, bytesGot, bytesLeft, ere, bytesOut, size, dLFA, KeyCode; ere = OpenFile(pFrom, cbFrom, ModeRead, 0, &fhFrom); if (!erc) { /* Check to see if it exists already */ ere = CreateFile(pTo, cbTo, 0); if ((!erc) !! (erc==ErcDupName) ) { if (ere == ErcDupName) { printf(“File already exists. Overwrite? (Y/N)\r\n”); ReadKbd(&KeyCode, 1); /* ReadKbd, wait */ if (((KeyCode & 0xff) == ‘Y’) ;; ((KeyCode & 0xff) == ‘y’)) { ere = 0; }
280 Part III ■ MMURTL-Specific Details Listing 1 6.1 . continued if (!erc) { ere = OpenFile(pTo, cbTo, ModeModif y, 0, &fhTo); if (!erc) { /* both files are open in block mode */ ere = GetFileSize(fhFrom, &size); if (!erc) ere = SetFileSize(fhTo, size); dLFA = 0; bytesLeft = size; while ((!erc) && (bytesLeft)) { if (bytesLeft >= 4096) bytesWant = 4096; else bytesWant = bytesLeft; if (bytesWant & 511) /* handle last sector */ bytesWant += 512; bytesWant = (bytesWant/512) * 512; ere = ReadBlock(fhFrom, bigBuf, bytesWant, dLFA, &bytesGot); if (bytesGot) { ere = WriteBlock(fhTo, bigBuf, bytesGot, dLFA, &bytesOut); if (bytesLeft < bytesOut) bytesLeft = 0; else bytesLeft -=bytesOut ; } dLFA += bytesGot; } CloseFile(fhTo); } } } CloseFile(fhFrom) ; } return(erc) ; } /I********************************************************* Does a directory listing for param [1] or current path. U32 DoDir(void) { U8 fDone; U32 SectNum, ere, KeyCode, i; char st[78] ; if (iLine >= 23) {
Chapter 16 ■ MM URTL Sample Software 281 SerollVid(0, 1,80, 23,1); SetXY(0,23); } fDone = 0; SectNum = 0; while (IfDone) { ere = GetDirSector(apParam[1 ] , acbParam[1], &dirent[0], 512, SectNum); if (!erc) { for (i=0; i<16; i++) { if (!dirent[i].Name[0]) { ere = 1 ; fDone = 1 ; } if ((dirent[i] .Name[0]) && (dirent[i] .Name[0] !=0xE5)) { sprintf (st, “%8s %3s %8d xx/xx/xx xx/xx/xx %2x %04x\r\n” dirent[i] .Name, dirent[i] .Ext, dirent[i] .FileSize, dirent[i] .Attr, direntfi] .StartClstr) ; CnvrtFATTime(dirent[i] .Time, direntfi] .Date, &st[33], &st[24] printf("%s”, st); iLine++; if (iLine >= 22) { SetXY(0,23); printf(“ESC to cancel, any other key to continue..."); ReadKbd(&KeyCode, 1); /* ReadKbd, wait */ if ((KeyCode & 0xff) == 0x1b) { ere = 4; fDone = TRUE; } InitScreen(TRUE) ; SetXY(0,1); iLine =1 ; } } } } else fDone = TRUE; SectNum++; return(erc) ; } /♦♦a****************************************************** This fills in the pointers to and sizes of each parameter found on the command line in the arrays apParams and acbParams.
I
282
Part III ■ MMURTL-Specific Details
Listing 16.1. continued
void ParseCmdLine(void)
{
long iCmd, iPrm, i; /* iCmd is index to aCmd */
iCmd = 0;
for (iPrm=0; iPrm<nParamsMax; iPrm++)
{
acbParam[iPrm] = 0; /* default the param to empty */
apParam[iPrm] = 0; /* Null the ptr */
if (iCmd < cbCmd)
{
if ( !isspace(aCmd[iCmd]))
{
apParam[iPrm] = &aCmd[iCmd++] ; /* ptr to param */
i = 1;
while ((iCmd < cbCmd) && ( !isspace(aCmd[iCmd] ) ) )
{
i++;
iCmd++;
}
acbParam[iPrm] = i; /* size of param */
}
while ((iCmd < cbCmd) && (isspace(aCmd[iCmd] ) ) )
iCmd++;
}
}
}
This opens and parses Commands. CLI in the system
directory and looks for the command name in
apParam[0]. If it finds it, it places the full
filespec for the run file in “runfile” for the
caller, then fixes up the aCmd command line
just as if it had come from the user.
The format for a line entry in Commands. CLI is:
name fullspec param param param...
♦ it************************************************/
long GetCLICommand(char *runfile)
{
long i, j , k;
FILE *f;
char rawline[90]; /* used to build a pseudo command line */
char cmdline[90]; /* used to build a pseudo command line */
char fdone;
cmdline[0] = 0;
f = fopen(cmdpath, “r”);
if (f)
{
fdone = 0;
while (Ifdone)
{
if (fgets(rawline, 89, f))
{
Chapter 16 ■ MMURTL Sample Software if (rawline[0] ==’;') /* a comment line */ continue; j = CompareNCS(apParam[0] , rawline, acbParam[0] ) ; if (j == -1) { /* we found a match for the command */ /* Move the command name into the command line */ i = 0; /* Index into rawline */ j = 0; /* Index into cmdline we are building */ k = 0; /* Index into runfile name we return to caller */ while ( ! (isspace(rawline[i] ) ) ) cmdline[j++] = rawline[i++] ; /* follwed by a space */ cmdline[ j++] = ' ' ; /* skip whitespace in rawline */ while (isspace(rawline[i] ) ) i++; /* now move runfile name */ while ( ! (isspace(rawline[i] ) ) ) runfile[k++] = rawline[i++] ; runfile[k] = 0; /* null terminte */ /* skip whitespace in rawline */ while (isspace(rawline[i] ) ) i++; /* now move arguments if any, and LF */ while (rawline[i]) cmdline[ j++] = rawline[i++] ; cmdline [j ] = 0; /* Move cmd line we built to real cmd line */ strcpy(aCmd, cmdline); cbCmd = strlen(aCmd) ; return(1); /* tell em we got it! */ } } else f done = 1 ; } } else printf( “Commands. CLI not found. \r\n” ) ; return(0) ; When a command is specified to the CLI and it is not an internal command, we first look for a .RUN file in the current path. If we don't find one, we go to the system directory, if it's not there, we then go to COMMANDS. CLI and search it line by line to see if a run file is specified there. If so, we set the command line with everything after the run file
284 | Part III ■ MMURTL-Specific Details Listing 16.1. continued specified and try to execute it. void FindRunFile(void) { char runfile[80] ; long i, ere, fh; /* Try to find in current path */ CopyData(apParam[0] , runfile, acbParam[0] ) ; runf ile[acbParam[0] ] = 0; strcat(runfile, “.RUN”); ere = OpenFile(runfile, strlen(runfile) , ModeRead, 1, &fh); if (!erc) { /* found a run file */ CloseFile(fh); SetCmdLine(aCmd, cbCmd); SetExitJob(clipath, strlen(clipath) ) ; ere = Chain(runfile, strlen(runf ile) , 0); } else /* Try to find in System directory */ { strcpy(runfile, syspath); i = strlen(runfile) ; CopyData(apParam[0] , &runfile[i], acbParam[0] ) ; runfile[aebParam[0]+i] = 0; /* null terminate */ strcat(runfile, “.RUN”); ere = OpenFile(runfile, strlen(runf ile) , ModeRead, 1, &fh); if (!erc) { /* found a run file */ CloseFile(fh); SetCmdl_ine(aCmd, cbCmd); SetExitJob(clipath, strlen(clipath) ) ; ere = Chain(runfile, strlen(runf ile) , 0); } /* now we call GetCLICommand as a last resort */ else if (GetCLICommand (runfile)) { ere = OpenFile(runfile, strlen(runf ile) , ModeRead, 1, &fh) if (!erc) { /* found a run file */ CloseFile(fh); SetCmdLine ( aCmd , cbCmd ) ; SetExitJobjclipath, strlen(clipath) ) ; ere = Chain(runf ile, strlen(runf ile) , 0); } } } printf( “Command not found\r\n”); } Main function entry point. Note: No params to CLI.
Chapter 16 ■ MMURTL Sample Software H void main(void) { unsigned long ere, i, j, fh; ere = AllocExch(&StatExch) ; ere = AllocExch(&GPExch) ; SpawnTask(&StatTask, 24, 0, &StatStack[255] , 0); GetJobNum(&JobNum) ; sprintf (&aStatLine[37], “%02d”, JobNum) ; SetJobName(“CLI V1.0”, 8); /* Get system disk and set up path names for cli, command file, and help file. */ GetSystemDisk(&sdisk) ; sdisk &= 0x7F; sdisk += 0x41; /* 0=A, 1=B, 2=C etc. */ syspath[0] = sdisk; syspath[1 ] = ' : ' ; syspath[2] = 0; strcat(syspath, “WMMSYSW”) ; strcpy(clipath, syspath); strcat (clipath , “CLI . RUN” ) ; strcpy(cmdpath, syspath); strcat ( cmdpath , “COMMANDS . CLI " ) ; strcpy(hlppath, syspath); strcat (hlppath, “HELP. CLI” ) ; /* If a path was already set we assume that we are reloading after an external command has been run. We do not want to reset the screen completely so the user can see anything the external command left on the screen. */ cbPath = 0; GetPath(JobNum, aPath, &cbPath); if (cbPath) { GetXY(&iCol, &iLine); if (iLine == 0) /* under status line */ iLine = 1 ; InitScreen(FALSE); SetXY(0, iLine); } else { strcpy (aPath, syspath); cbPath = strlen(syspath) ; SetPath(syspath, strlen(syspath) ) ;
286 | Part III ■ MMURTL-Specific Details Listing 16.1. continued InitScreen(TRUE) ; } fUpdatePath = 1 ; SetExitJob(clipath, strlen(clipath) ) ; while (1) { FillData(aCmd, 79, ' ‘); aCmd[79] = 0; cbCmd = 0; SetXY(0, iLine); TTYOut (“>”, 1, NORMVID); EditLine(aCmd, cbCmd, 79, &cbCmd, &ExitChar, EDVID) ; TTYOut ("\r\n”, 2, NORMVID); GetXY(&iCol, &iLine); acbParam[0] = 0; apParam[0] = 0; CmdNum = 0; if (ExitChar == 0x0d) ParseCmdLine() ; /* set up all params */ if ( (acbParam[0] ) && (apParam[0] ) ) { /* It's a command! */ i = 1; while (i <= NCMDS) { j = strlen(paCmds[i] ) ; if ((acbParam[0] == j) && (CompareNCS(apParam[0] , paCmds[i], j) == -1)) { CmdNum = i; break; } i++; } ere = 0; switch (CmdNum) { case EXTCMD: /* external command */ FindRunFile( ) ; break; case CLSCMD: InitScreen(TRUE) ; break; case COPYCMD: if ( (acbParam[1 ] ) && (acbParam[2] ) ) ere = CopyFile(apParam[1 ] , acbParam[l], apParam[2), acbParam(2]) else ere = ErcBadParams; break; case DELCMD:
Chapter 16 ■ MMURTL Sample Software ere = OpenFile(apParam[l ] , acbParam[ 1 ] , 1, 0, &fh) if (!erc) ere = DeleteFile(fh) ; if (lerc) printf (“Done.\r\n”) ; break; case DIRCMD: ere = DoDir() ; break; case DBGCMD: GoDebugO ; break; case DUMPCMD: ere = DoDump( ) ; break; case EXITCMD: SetExitJob("", 0); ExitJob(0); break; case HELPCMD: ere = DoType(hlppath, strlen(hlppath) ) ; break; case MAKEDIR: ere = CreateDir(apParam[1 ] , acbParam[1 ] ) ; break; case MONCMD: ere = Request (“KEYBOARD”, 4, GPExch, &GPHndl, 0, 0, 0, 0, 0, 1, 0, 0); ere = WaitMsg (GPExch, GPMsg); SetVid0wner(1 ) ; break; case PATHCMD: ere = SetPath(apParam[1 ] , acbParam[1 ] ) ; if (!erc) ere = GetPath(JobNum, aPath, &cbPath); fUpdatePath = 1; break; case REMDIR: ere = DeleteDir(apParam[1 ] , acbParam[1 ] ) ; break; case RENCMD: if ((acbParam[1]) && (acbParam[2] ) ) ere = RenameFile(apParam[1 ] , acbParam[1 ] , apParam[2] , acbParam[2] ) ; else ere = ErcBadParams; break; case RUNCMD: if (acbParam[1 ] ) { i = 2; while (aCmd[i] != ' ‘) i++; SetCmdLine(&aCmd[i] , cbCmd-i); SetExitJob(clipath, strlen(clipath) ) ; ere = Chain (apParam[1 ] , acbParam[1 ] , 0); } break; case TYPECMD:
288 Part III ■ MMURTL-Specific Details Listing 16.1. continued ere = DoType(apParam[l ] , acbParam[1]) break; default: break; } CheckErc(erc) ; } GetXY(&iCol, &iLine); if (iLine >= 23) { ScrollVid(0,1,80,23,1); SetXY(0,23); } % A Simple Editor The editor included with MMURTL is a very simple in-memory editor. The editor always word-wraps lines that extend beyond 79 columns. The largest file that can be edited is 128K. The source code is listed forthwith and is also included on the accompanying CD-ROM. The editor contains a good example of extensive use of the keyboard service (translating keycodes). Editor Screen The editor screen is divided into three sections. The top line provides continuous status infor- mation, such as the position of the cursor in the file (column and line), current filename, and typing mode (Insert or Overtype). The next 23 lines are the text editing area. The last line is used for data entry, status, and error display. Editor Commands The following four tables describe all the editor commands. Table 16.1. File management and general commands. Keystroke Action Alt-S Saves changes to current file Alt-C Closes and prompts to save current file Alt-O Open a new file Alt-Q Quits (exits) the editor Alt-X Same as Alt-Q (exit)
Chapter 16 ■ MMURTL Sample Software Keystroke Action Insert Esc Alt-V Toggles between insert and overtype mode Exits filename entry mode for open-file command Make nontext chars visible/invisible (toggle) Table 16.2. Cursor and screen-management commands. Keystroke Action PageDown Move one screen down in the text file PageUp Move one screen up in the text file Up Cursor up one line (scroll down if needed) Down Cursor down one line (scroll up if needed) Left Cursor left one column (no wrap) Right Cursor right one column (no wrap) Alt-B Go to beginning of text Alt-E Go to end of text Alt-Up Arrow Cursor to top of screen Alt-Down Arrow Cursor to bottom of screen Alt- Left Arrow Cursor to beginning of line Alt Right Arrow Cursor to end of line Home Cursor to beginning of line End Cursor to end of line Shift Right Arrow Move cursor right five spaces Shift Left Arrow Move cursor left five spaces Table 16.3. Block-selection and editing commands. Keystroke Action F3 F4 F2 F9 F10 Alt-Del Begin block (mark) End block (bound) Unmark block (no block defined or highlighted) Move marked block to current cursor position Copy marked block to current cursor position Delete marked block
I
290
Part III ■ MMURTL-Specific Details
Table 16.4. Miscellaneous editing keys.
Keystroke Action
Delete Delete character at current cursor position
Backspace Destructive in insert mode (INS)
Non-destructive in overtype mode (OVR)
Tab Pseudo tab every four columns (space filled)
Editor Source Listing
The editor source is a good example of a program that uses a lot of functionality from the key-
board service. File system calls are also used in place of equivalent C library functions, which
provides good examples of checking for file system errors. Listing 16.2 is the editor source code.
Listing 16.2. Editor source code.
/* Edit.c A simple editor using MMURTL file system and
keyboard services */
#define U32 unsigned long
#define S32 long
#define U16 unsigned int
#define S16 int
#define U8 unsigned char
#define S8 char
#define TRUE 1
#define FALSE 0
#include <stdio.h>
#include <string.h>
//include <ctype.h>
/* Includes for OS public calls and structures */
#include “\0SS0uRCE\MKernel.h”
#include " \0SS0URCE\MMemory .h”
#include “\0SS0URCE\MData.h”
//include " \OSSOURCE\MTimer.h”
//include “\0SS0URCE\MVid.h”
//include “\0SS0URCE\MKbd.h”
//include " \OSSOURCE\MJob.h”
//include " \0SS0URCE\MFiles.h”
//define EDVID BRITEWHITEIBGBLUE
//define N0RMVID WHITE ! BGBLACK
//define MARKVID WHITE | BGRED
//define STATVID BLACK | BGCYAN
//define EMPTY 99999
Chapter 16 ■ MMURTL Sample Software H #define NLINESMAX 26 struct EditRecType { U8 pBuf ; U8 pBufWork; / For copy and move U32 Line [ NLINESMAX ; | ; / Offset in buf for 1st char in line / U32 iBufMax; / sBuf - 1 / U32 iColMin; / Screen coords / U32 iRowMin; U32 iColMax; U32 iRowMax ; U32 sLine; U8 bSpace; U8 fVisible; U32 iAttrMark; U32 iAttrNorm; U32 iTabNorm; U32 oBuf LineO / oBufLineO / U32 iCol; / cursor, 0. .sLine-1 / U32 iLine; / cursor, 0..cLines-1 / U32 oBuf Insert; / offset of next char in / U32 oBufLast; / offset+1 of last char */ U32 oBufMark; U32 }; oBuf Bound; struct EditRecType EdRec; struct EditRecType *pEdit; char *pBuf1 , pBuf2; unsigned char b, bl ; long ere, fh; char fModified; char fOvertype; char aStat[80] ; char aStat1[80]; char aCmd[80] ; / long cbCmd = 0; char *apParam[l3] ; long acbParam[13] ; #define nParamsMax 13 char Filename[60] ; long cbFilename; unsigned char filler[100] Get our command line / / Param 0 is cmd name / void clearbuf (void) ; / prototype for forward usage */ Displays errors if they occur for certain file operations. long CheckErc(long call, long ere) { char st[40] ; long i; if (ere) {
I
292
Part III M MMURTL-Specific Details
Listing 16.2. continued
FillData(st, 40, 0);
Beep();
switch (call) {
case 1 :
sprintf(st, “Error %05d occurred on OpenFile”, ere);
break;
case 2:
sprintf(st, “Error %05d occurred on ReadBytes”, ere);
break;
case 3:
sprintf(st, “Error %05d occurred on WriteBytes” , ere);
break;
case 4:
sprintf(st, “Error %05d occurred on CreateFile”, ere);
break;
case 5:
sprintf(st, “Error %05d occurred on SetFileSize” , ere);
break;
case 6:
sprintf(st, “Error %05d occurred on SetFileLFA”, ere);
break;
case 7:
sprintf(st, “Error %05d occurred on ReadKbd”, ere);
break;
default:
sprintf(st, “Error %05d occurred on last command”, ere);
break;
}
for (i=0; i<40; i++)
if (lst[il)
st[i] = ' ‘;
PutVidChars (40, 24, st, 39, STATVID) ;
}
return (ere);
}
Clears the status line with 80 blank chars.
void ClearStatus(void)
{
char st[80] ;
FillData(st, 80, 0);
PutVidChars (0, 24, st, 80, NORMVID) ;
}
2
Saves a file you are editing. If fPrompt is true, this
will prompt you to save. If fClose is true, the file will be closed
and the buffer will be closed.
void SaveFile(int f Prompt, int fClose)
{
Chapter 16 ■ MMURTL Sample Software
|
293
U32 i, keycode, fYes;
unsigned char pBuff;
pBuff = pEdit->pBuf;
if ((fh) && (f Modified))
{
if (pEdit->fVisible)
{ / fix visible characters */
for (i=0; i <=pEdit->iBufMax; i++)
if (pBuff[i] == 0x07)
pBuff[i] = 0x20;
pEdit->f Visible = FALSE;
}
fYes = 1;
if (f Prompt)
{
ClearStatus() ;
SetXY(0, 24);
TTYOut(“This file has been modified. SAVE IT? (Y/N)", 43,
black;bgcyan);
ReadKbd(&keycode, 1);
if (((keycode & 0xff) == ‘N’) j| ((keycode & 0xff) == ‘n’))
{
fYes = 0;
ClearStatus() ;
}
}
if (fYes)
{
ere = CheckErc(6, SetFileLFA(fh, 0));
if (!erc)
ere = CheckErc(5, SetFileSize(fh,
pEdit->oBufLast));
if (!erc)
ere = CheckErc(3, WriteBytes (fh, pBuf 1 ,
pEdit->oBufLast, &i));
fModified = 0;
ClearStatus();
PutVidChars (0, 24, “DONE... “, 10, STATVID) ;
Sleep(150);
ClearStatus() ;
}
if (fh && fClose)
{
CloseFile(fh)
fh = 0;
cbFilename = i
clearbuf ( ) ;
}
This prompts for a filename to open and opens it if it
294 Part III ■ MMURTL-Specific Details Listing 1 6.2. continued exists. If not, it will prompt to create. void OpenAFile(char name) { U32 filesize, dret, keycode; ere = 0; cbFilename = 0; if (!name) { SetXY(0,24); PutVidChars (0,24, “Filename: “, 10, BLACK | BGWHITE) ; SetXY(10,24); EditLine(Filename, 0, 60, &cbFilename, &b1 , BLACK | BGCYAN ) ; SetXY(0,0); } else { b1=0x0d; strncpy(Filename, name, 13); cbFilename = strlen(Filename) ; } if ((bl==0x0d) && (cbFilename)) { ere = OpenFile( Filename, cbFilename, ModeModify, 1, &fh); if (!erc) { GetFileSize(fh, &filesize); if (filesize < 131000) / Buf is 131071 / { ere = ReadBytes (fh, pBuf 1 , filesize, &dret); if (ere > 1 ) ere = CheckErc(2, ere) ; else ere = 0; pEdit->oBufLast = dret; / offset+1 of last char / pBufl[pEdit->oBufLast] = 0x0F; / the SUN / } else { CloseFile(fh) ; fh = 0; Beep(); SetXY(50, 24); TTYOut(“File is too large to edit.", 26, BLACK \ BGCYAN ) ; ReadKbd(&keycode, 1); } } else if (ere == 203) { / no such file */ Beep(); SetXY(50, 24); TTYOut( “Doesn't exist. Create?? (Y/N)", 29, BLACK | BGCYAN ) ; ReadKbd(&keycode, 1); if (((keycode & 0xff) == ‘Y’) \ ((keycode & 0xff) == ‘y’)) { ere = CheckErc(4, CreateFile(Filename, cbFilename, 0)); if (!erc) ere = CheckErc(l, OpenFile(Filename, cbFilename,
Chapter 16 ■ MMURTL Sample Software H ModeModify, 1, &fh)) if (ere) { fh = 0; cbFilename = 0; } } else { cbFilename = 0; ClearStatus() ; } } else CheckErc(1, ere) if (!erc) ClearStatus( } This counts ABSOLUTE LINES from the begining of the buffer up to point of oBufLine0 (which is the first char displayed in the window). ABSOLUTE means LFs were found, even though we word wrap always. unsigned long CountEols (void) { unsigned long nEols, i; unsigned char pBuff; pBuff = pEdit->pBuf; nEols = 0; while (i < pEdit->oBufLine0) / count LFs if (pBuff[i++] == 0X0A) nEols++; return(nEols) ; This returns the index to the the last character in a line up to a maximum of sLine-1. iBuf points to the beginning point in the buffer for which to find the end of line. ****************; unsigned long findEol (unsigned long iBuf) { unsigned long iEol, iEolMax; unsigned char *pBuff;
296 Part III M MMURTL-Specific Details Listing 16.2. continued pBuff = pEdit->pBuf; /* Calculate the most it could be */ iEolMax = iBuf + pEdit->sLine-1 ; /* Fix it if EOL is past end of data */ if (iEolMax > pEdit->oBufLast) iEolMax = pEdit->oBufLast; iEol = iBuf; while ((pBuff[iEol] != 0x0A) && (iEol < iEolMax)) /* Find CR */ iEol++; if ((iEol == iEolMax) && (pBuff[iEol] != 0X0A) ) { /* if no CR... */ iEol = iEolMax; if (iEolMax < pEdit->oBuf Last) { /* now work back to last space */ while ((pBuff [iEol] != pEdit ->bSpace) && (iEol > iBuf)) iEol-; /* now find first non-space - allows */ if ((iEol > iBuf) && (pBuff[iEol] == pEdit->bSpace) && /* wrap-around w/ double space */ (iEol == iEolMax)) { if ((pBuff[iEol-1] == pEdit->bSpace) || ((iEol == iEolMax) && (pBuff [iEol+1 ] == pEdit ->bSpace) ) ) { while ((pBuff [iEol] == pEdit->bSpace) && (iEol > iBuf)) iEol-; while ((pBuff [iEol] != pEdit->bSpace) && (iEol > iBuf)) iEol-; } } if ((iEol == iBuf) && (pBuff[iBuf] > 0) && (pBuff [iEolMax] > 0)) /* handles “all-char” of full line */ iEol = iEolMax; } } return(iEol) ; } This walks back through the buffer looking for the logical end of a line. unsigned long findPrevLine (unsigned long oBuf Start) { unsigned long i, j ; char *pBuff;
Chapter 16 ■ MMURTL Sample Software H pBuff = pEdit->pBuf; i = 0; if (oBuf Start) i = oBuf Start - 1 ; while ((i) && (pBuff[i] != 0x0A)) i— ; if (i > 0) i-; while ((i > 0) && (pBuff[i] != 0x0A)) i-; if (i) i++; /* Get to known start of line */ do { ] ■ i; i = (findEol (j)) + 1; } while (i < oBuf Start); return( j ) ; } This executes the BEGIN BLOCK (Mark) command. void doMark (unsigned long iLn) { unsigned long iColStart, iColFinish, iMarkLoc, iBoundLoc; if (pEdit->oBufMark < EMPTY) { if (pEdit->oBufMark <= pEdit->oBuf Bound) { iMarkLoc = pEdit->oBufMark; iBoundLoc = pEdit->oBuf Bound; } else { iMarkLoc = pEdit->oBufBound; iBoundLoc = pEdit->oBufMark; } if ( ((iMarkLoc >= pEdit->Line[iLn] ) && (iMarkLoc < pEdit->Line[iLn+1 ] ) ) || ((iBoundLoc >= pEdit->Line[iLn] ) && (iBoundLoc < pEdit->Line[iLn+1 ] ) ) \ ((iMarkLoc < pEdit->Line[iLn] ) && (iBoundLoc >= pEdit->Line[iLn+1 ] ) ) ) { if (iMarkLoc >= pEdit->Line[iLn] ) iColStart = pEdit->iColMin + iMarkLoc - pEdit->Line[iLn] ; else iColStart = pEdit->iColMin; if (iBoundLoc < pEdit->Line[iLn+1 ] ) iColFinish = pEdit->iColMin + iBoundLoc - pEdit->Line[iLn] ; else iColFinish = pEdit->iColMin + pEdit->Line[iLn+1 ] - pEdit->Line[iLn] - 1; if (iColStart > pEdit->iColMin) PutVidAttrs (pEdit->iColMin, iLn, iColStart - pEdit ->iColMin ,
298 Part III ■ MMURTL-Specific Details Listing 1 6.2. continued pEdit->iAttrNorm) ; PutVidAttrs (iColStart, iLn, iColFinish - iColStart +1 , pEdit->iAttrMark] if (iColFinish < pEdit->iColMax) PutVidAttrs (iColFinish+1, iLn, pEdit->iColMax - iColFinish, pEdit->iAttrNorm) ; } else /buf col/ PutVidAttrs (pEdit->iColMin, iLn, pEdit->sLine, pEdit->iAttrNorm) ; } } This inserts data into the main editing buffer. ♦A************************************************ char putInBuf( unsigned char bPutln, char f Overtype, char fSpecInsert) { unsigned long cb; char fOK, *pBuff; fModified = 1; pBuff = pEdit->pBuf; if ((pEdit->oBuf Insert < pEdit->iBufMax) && ( (pEdit->oBufLast < pEdit ->iBufMax) j| ((fOvertype) && ( lfSpednsert) ) ) ) { f OK = 1 ; if ((fOvertype) && ( lfSpednsert) ) { pBuff [pEdit->oBuf Insert] = bPutln; if (pEdit->oBufLast == pEdit->oBuf Insert) pEdit->oBufLast++; pEdit ->oBuf Insert++; } else { cb = pEdit->oBufLast - pEdit->oBuf Insert + 1; CopyData (&pBuff[pEdit->oBuf Insert] , pEdit->pBufWork, cb); pBuff [pEdit->oBufInsert] = bPutln; CopyData (pEdit ->pBufWork, &pBuff [pEdit ->oBuf Insert+1 ] , cb) pEdit->oBufLast++; pEdit->oBuf Insert++; if (pEdit->oBufMark < EMPTY) { if (pEdit->oBuf Insert -1 < pEdit ->oBufMark) pEdit->oBufMark++; if (pEdit->oBuf Insert -1 <= pEdit ->oBuf Bound) pEdit>oBufBound++;
Chapter 16 ■ MM URTL Sample Sofit
|
299 |
}
}
}
else {
fOK = 0;
Beep();
ere = 40400;
}
return (fOK);
}
This executes the MOVE command, which moves a marked
block to the cursor's current location in the file.
void moveData (void)
{
unsigned long i, iMk, iBd;
char *pBuff, pBuffWork;
pBuff = pEdit->pBuf;
pBuffWork = pEdit->pBufWork;
if (pEdit->oBufMark < EMPTY) {
fModified = 1;
if (pEdit->oBufMark <= pEdit->oBuf Bound) {
iMk = pEdit->oBufMark;
iBd ■ pEdit->oBuf Bound;
}
else {
iBd = pEdit->oBufMark;
iMk = pEdit->oBuf Bound;
}
if ((pEdit->oBuf Insert < iMk) \ (pEdit->oBuf Insert > iBd)) {
for (i=0; i <= pEdit->oBuf Last; i++)
pBuffWork[i] = pBuff[i];
if (pEdit->oBuf Insert < iMk) {
for (i=0; i<=iBd-iMk; i++) / Move mk/bd /
pBuff[pEdit->oBufInsert+i] = pBuffWork[iMk+i] ;
for (i=0; i<=iMk - pEdit->oBuf Insert - 1; i++) / Shift overwritten ahead
*/
pBuf f [ pEdit ->oBuf Insert+iBd - iMk+1 +i] =
pBuffWork[pEdit->oBufInsert+i] ;
2 }
if (pEdit->oBuf Insert > iBd) {
for (i=0; pEdit->oBuf Insert - iBd - 1; i++)
pBuff[iMk+i] = pBuffWork[iBd+1+i];
pEdit->oBuf Insert = pEdit->oBuf Insert - iBd + iMk - 1;
for (i=0; i <=iBd-iMk; i++)
pBuff [pEdit->oBufInsert+i] = pBuffWork[iMk+i] ;
}
iBd = pEdit->oBuf Insert + iBd - iMk;
iMk = pEdit->oBuf Insert;
if (pEdit->oBuf Bound > pEdit ->oBufMark) {
pEdit->oBuf Bound = iBd;
pEdit->oBufMark = iMk;
300 I Part III ■ MMURTI-Specific Details Listing 16.2. continued } else { pEdit->oBufMark = iBd; pEdit->oBuf Bound = iMk; } } } else Beep() ; } This sets the characters on the screen to normal attributes. void Copylt (void) { unsigned long iMk, iBd; char *pBuff, *pBuffWork; pBuff = pEdit->pBuf; pBuffWork = pEdit->pBufWork; if (pEdit->oBufMark < EMPTY) { fModified = 1; if (pEdit->oBufMark <= pEdit->oBuf Bound) { iMk = pEdit->oBufMark; iBd = pEdit->oBuf Bound; } else { iBd = pEdit->oBufMark; iMk = pEdit->oBuf Bound; } if (pEdit->oBufLast+iBd-iMk+1 < pEdit->iBufMax) { CopyData(pBuff , pBuffWork, pEdit->oBuf Last+1 ) ; CopyData(&pBuffWork[iMk], &pBuff[pEdit->oBuf Insert] , iBd-iMk+1) if (pEdit->oBufLast >= pEdit ->oBuf Insert) CopyDat a (&pBuff Work [pEdit->oBuf Insert] , &pBuff [pEdit->oBuf Insert+iBd-iMk+1 ] , pEdit->oBufLast - pEdit->oBuf Insert+1 ) ; iBd = pEdit->oBuf Insert + iBd - iMk; iMk ■ pEdit->oBuf Insert; pEdit->oBuf Insert = pEdit ->oBuf Insert + iBd - iMk + 1; pEdit->oBufLast = pEdit->oBuf Last + iBd - iMk + 1 ; if (pEdit->oBufBound > pEdit ->oBufMark) { pEdit->oBuf Bound = iBd; pEdit->oBufMark = iMk; } else { pEdit->oBufMark = iBd; pEdit->oBuf Bound = iMk; } } } else Beep( ) ; }
Chapter 16 ■ MMURTL Sample Software H This executes the COPY command, which copies a marked block to the cursor's current location in the file. void normAttr (void) { unsigned long i; for (i = pEdit->iRowMin; i <= pEdit->iRowMax; i++) PutVidAttrs (pEdit->iColMin, i, pEdit ->sLine, pEdit->iAttrNorm) ; } This unmarks a selected block, (hides it). void nullMarkBound (void) {2 pEdit->oBufMark = EMPTY; pEdit->oBuf Bound = EMPTY; normAttr (); } This DELETES a selected block. void deleteData (void) { unsigned long i, iMk, iBd; char fProb; char *pBuff, *pBuffWork; pBuff = pEdit->pBuf; pBuffWork = pEdit->pBufWork; if (pEdit->oBufMark < EMPTY) { fModified = 1; if (pEdit->oBufMark <= pEdit->oBuf Bound) { iMk = pEdit->oBufMark; iBd = pEdit->oBuf Bound; } else { iBd = pEdit->oBufMark; iMk = pEdit->oBuf Bound; } if ((pEdit->oBufLine0 >= iMk) && (pEdit->oBufLine0 <= iBd)) fProb = TRUE; else fProb = FALSE; CopyData(&pBuff [iBd+1 ] , &pBuff[iMk], pEdit ->oBuf Last -iBd) ; pEdit->oBufLast = pEdit ->oBuf Last - iBd + iMk - 1; if (pEdit->oBuf Insert > iBd) pEdit->oBuf Insert = pEdit->oBuf Insert - iBd + iMk; else if ((pEdit->oBuf Insert > iMk) && (pEdit->oBuf Insert <= iBd); pEdit->oBuf Insert = iMk; if (pEdit->oBuf Insert > pEdit->oBuf Last) pEdit->oBuf Insert = pEdit->oBuf Last ;
Part III ■ MMURTL-Specific Details Listing 16.2. continued if (fProb) { i = findPrevLine (pEdit->oBuf Insert) ; pEdit->oBufLine0 = i; } nullMarkBound (); } } After screen movement (such as scrolling), this finds the proper location of the cursor in relationship to the portion of the file currently displayed. void findCursor (void) { /* locates cursor based on oBuflnsert
Chapter 16 ■ MMURTL Sample Software if (pEdit->Line[i] == EMPTY) pEdit->iCol = pEdit->iColMax; /* if bogus line, find last good line */ while ((pEdit->Line[i] == EMPTY) && (i > pEdit->iRowMin) ) { pEdit->iLine— ; i = pEdit->iLine; } i = pEdit->iLine; pEdit->oBuf Insert = pEdit->Line[i] + pEdit->iCol - pEdit->iColMin; if (pEdit->oBufInsert > pEdit->oBufLast) pEdit->oBuf Insert = pEdit->oBufLast; oBuf = pEdit->Line[i+1 ] ; if (pEdit->oBuf Insert > oBuf) pEdit->oBuf Insert = oBuf; /* get to potential insert - is, if prev char <> CR, is not if prev = CR */ if (pEdit->oBuf Insert == oBuf) /* if at EOL */ if (pEdit->pBuf [oBuf-1] == 0x0A) pEdit->oBuf Insert— ; pEdit->iCol = pEdit->oBuf Insert + pEdit->iColMin - pEdit->Line[i] ; } } Adjusts oBufLineO to make sure that oBuf Insert is on the screen. This also sets all of the Line array values (Line[n]) void makeOnScreen (void) { unsigned long i, j , k; /* If oBuflnsert is not on screen (above current display) then find the previous line beginning and make that the new first line 1 until it is. */ while (pEdit->oBuf Insert < pEdit->oBuf Line0) pEdit->oBufLine0 = findPrevLine (pEdit->oBufLine0) ; /* Set Line[iRowMin] to match oBufLineO */ k = pEdit->iRowMin; pEdit->Line[k] = pEdit->oBufLine0; /* Set all subsequent Line[s] by calling findEol for each. */ for (i = k; i <= pEdit->iRowMax; i++) { if (pEdit->Line[i] < EMPTY) { j = findEol (pEdit->Line[i] ) ; if (j < pEdit->oBufLast) /* j = offset of last char of line */ H
304] Part III M MMURTL-Specific Details Listing 16.2. continued pEdit->Line[i+l ] = j + 1 ; else for (j=i+1; j<NLINESMAX; j++) pEdit->Line[j] ■ EMPTY; } } /* If the InsertPoint (your cursor position) is past the last line, then do this junk to fix it */ j = pEdit->iRowMin; k = pEdit->iRowMax; while (pEdit->oBuf Insert >= pEdit->Line[k+l ] ) { for (i=j; i<=k; i++) pEdit->Line[i] = pEdit->Line[i+1 ] ; pEdit->oBufLine0 = pEdit->Line[ j ] ; i = findEol (pEdit->Line[k]); /* EOL of iRowMax */ if (i < pEdit->oBufLast) /* i = offset of last char of line */ pEdit->Line[k+1] = i + 1; else pEdit->Line[k+1] = EMPTY; } } Redisplay all data on the screen. void showScreen (char *pFiller) { unsigned long i, il_n, cb, oBuf; char *pBuff; pBuff = pEdit->pBuf; makeOnScreen (); /* oBuf Insert on screen - Line correct */ for (iLn = pEdit->iRowMin; iLn <= pEdit ->iRowMax; iLn++) { /* i = offset in buf of last char on line */ /* cb = nchars in line */ cb = 0; oBuf = pEdit->Line[iLn] ; if (oBuf < EMPTY) { if (pEdit->Line[iLn+1] < EMPTY) i = pEdit->Line[iLn+1] ■ 1; else i = pEdit->oBufLast; cb = i - oBuf +1; /* Make size, not offset */ if ((IpEdit >fVisible) && (pBuff(i) == 0x0A) && (cb)) cb-;
Chapter 16 ■ MMURTL Sample Software H if ((Cb) && (oBuf < EMPTY)) PutVidChars (pEdit->iColMin, iLn, pBuff+oBuf, cb, pEdit->iAttrNorm) ; if (cb < pEdit->sLine) PutVidChars (pEdit->iColMin+cb, iLn, pFiller, pEdit->sLine-cb, pEdit->iAttrNorm) ; doMark (iLn); } } Resets all variables for the editor and clears the buffers. Called before use and after closure. void clearbuf (void) { unsigned long i; char pBuff; pBuff = pEdit->pBuf; FillData(filler, 80, 0x20); for (i=0; KNLINESMAX; i++) pEdit->Line[i] = EMPTY; i = pEdit->iRowMin; pEdit->Line[i] = 0; pEdit->iCol = pEdit->iColMin; pEdit->iLine = pEdit->iRowMin; pEdit->bSpace = 0x20; pEdit->fVisible = FALSE; fModified = 0; f Overtype = FALSE; pEdit->oBufLast = 0; pEdit->oBuf Insert = 0; pEdit->oBufLine0 = 0; pBuff [pEdit->oBufLast] = 0x0F; / the SUN */ nullMarkBound() ; normAttr (); } This is the main editing function. It is a huge WHILE loop that reads keystrokes and processes them. void Editor(char pbExitRet) { unsigned long i, j, k, key; char fSpecInsert; / TRUE = insert no matter what fOvertype is / char f Screen; / TRUE = display entire screen */ char fDone; unsigned char b;
306 Part III ■ MMURTL-Specific Details Listing 16.2. continued char *pBuff, pBuffWork; long exch; pBuff = pEdit->pBuf ; pBuffWork = pEdit->pBufWork; fDone = FALSE; if (pEdit->f Visible) { pEdit->bSpace = 0x07; for (i=0; i <=pEdit->oBufLast; i++) if (pBuff[i] — 0x20) pBuff[i] = pEdit->bSpace; } else pEdit->bSpace = 0x20; normAttr (); fScreen = TRUE; pBuff[pEdit->oBufLast] = 0X0F; / the SUN / ere = AllocExch(&exch) ; while (IfDone) { if (fScreen) { showScreen (filler); / we know oBuf Insert on screen / findCursor (); fScreen = FALSE; } SetXY (pEdit->iCol, pEdit->iLine) ; FillData(aStat, 80, 0x20); i= CountEols() + pEdit->iLine; sprintf (aStat, “C: %02d L: %05d nChars: %05d”, pEdit->iCol, i, pEdit ->oBuf Last) ; if (cbFilename) CopyData( Filename, &aStat[40], cbFilename); if (fOvertype) CopyData(“OVR”, &aStat[77], 3); else CopyData(“INS”, &aStat[77], 3); PutVidChars (0, 0, aStat, 80, STATVID) ; fSpecInsert = FALSE; / True if char should be inserted even if fOver / CheckErc(7, ReadKbd (&key, 1)); / Wait for char / b = key & 0xff; if (key & 0x3000) { / ALT key is down / switch (b) { case 0x42: / ALT-B - Beginning of Text */
Chapter 16 ■ MMURTL Sample Software H case 0x62: /* ALT-b */ pEdit->oBufLine0 = 0; pEdit->oBuf Insert = 0; pEdit->iCol = pEdit->iColMin; pEdit->iLine = pEdit ->iRowMin; f Screen = TRUE; break; case 0x43: /* ALT-C — CloseFile */ case 0x63: /* ALT-c */ SaveFile(TRUE, TRUE); f Screen = TRUE; break; case 0x45: /* ALT-E - End of Text */ case 0x65: /* ALT-e */ pEdit->oBuf Insert = pEdit ->oBuf Last; coordCursor_oBuf (); fScreen = TRUE; break; case 0x01: /* ALT-UP Arrow - Cursor to top of screen */ pEdit->iLine = pEdit->iRowMin; pEdit->iCol = pEdit->iColMin; break; case 0x02: /* ALT Down Arrow - Cursor to bottom of screen */ pEdit->iLine = pEdit->iRowMin; i = pEdit->iLine; while ((pEdit->Line[i+1] < EMPTY) && (i < pEdit->iRowMax) ) { pEdit->iLine++; i = pEdit->iLine; } pEdit->iCol = pEdit->iColMax; coordCursor_oBuf ( ) ; break; case 0x03: /* ALT-Left Arrow - Cursor to BOL */ pEdit->iCol = pEdit->iColMin; break; case 0x04: /* ALT-Right Arrow - Cursor to EOL */ i = pEdit->iLine; if (pEdit->Line[i+1] < EMPTY) { pEdit->iCol = pEdit->iColMin + pEdit->Line[i+1 ] • pEdit->Line[i] ; i = pEdit->iLine+1 ; if ((pBuff[pEdit->Line[i]-1] == 0x0A) && (pEdit->iCol > pEdit->iColMin)) pEdit->iCol— ; } else pEdit->iCol = pEdit->iColMin + pEdit->oBufLast - pEdit->Line[i] ; break; case 0x56: /* ALT-V Make nontext chars Visible/Invisible */ case 0x76: /* ALT-v */ if (pEdit->f Visible) { for (i=0; i <= pEdit ->oBuf Last; i++)
308 Part III ■ MMURTL-Specific Details Listing 16.2. continued if (pBuff[i] == 0x07) pBuff[i] = 0x20; pEdit->f Visible = FALSE; pEdit->bSpace = 0x20; } else { for (i=0; i<=pEdit->oBufLast; i++) if (pBuff[i] — 0x20) pBuff[i] = 0x07; pEdit->f Visible = TRUE; pEdit->bSpace = 0x07; } f Screen = TRUE; break; case 0x4F: /* ALT-0 OpenFile */ case 0x6F: /* ALT-o */ if (!fh) { clearbuf ( ) ; OpenAFile(0) ; fScreen = TRUE; } break; case 0x51: /* ALT Q - Quit */ case 0x71 : SaveFile(TRUE, TRUE); fDone = TRUE; break; case 0x53: /* ALT S - SaveFile */ case 0x73: /* ALT s */ SaveFile (FALSE, FALSE); f Screen = TRUE; break; case 0x7F: /* ALT Delete (Delete Marked Block) */ if (pEdit->oBufMark < EMPTY) { deleteData (); f Screen = TRUE; } break; default: break; else if (key & 0x0300) { /* CTRL key is down */ } /* Standard editing keys including LF */ else if (((b >= 0x20) && (b <= 0x7E)) |j (b == 0x0D)) { coordCursor_oBuf (); if (b == 0x20) b = pEdit->bSpace; if (b == 0x0D) b = 0x0A; /* Don't overwrite CR */
Chapter 16 ■ MMURTL Sample Software if (pBuff [pEdit->oBuf Insert] == 0x0A) fSpednsert = TRUE; if (!(putInBuf (b, fOvertype, fSpednsert) ) ) Beep(); findCursor (); f Screen = TRUE; } else if (key & 0X0C00) { /* SHIFT key is down & NOT letter keys switch (b) { case 0x04: /* SHIFT Right */ if (pEdit->iCol < pEdit->iColMax - 5) pEdit->iCol += 5; else if (pEdit->iCol < pEdit->iColMax) pEdit->iCol++; break; case 0x03: /* SHIFT Left */ if (pEdit->iCol > pEdit->iColMin + 5) pEdit->iCol -= 5; else if (pEdit->iCol > pEdit->iColMin) pEdit->iCol— ; break; default: break; } } else { /* Unshifted editing keys */ switch (b) { case 0x08: /* Backspace */ if (pEdit->oBufLast) { coordCursor_oBuf (); if (pEdit->oBufInsert) { pEdit->oBuf Insert = pEdit->oBuf Insert - 1; if (IfOvertype) { CopyData(pBuff , pBuffWork, pEdit->oBufLast+1 ) ; CopyData(&pBuffWork[pEdit->oBuf Insert+1 ] , &pBuff[pEdit->oBuf Insert] , pEdit - >oBuf Last -pEdit->oBuf Insert) ; pBuff [pEdit->oBufLast] = 0; pEdit->oBufLast = pEdit->oBuf Last - 1; if ( (pEdit->oBufMark == pEdit->oBuf Bound) && (pEdit->oBufMark == pEdit->oBuf Insert) ) nullMarkBound (); if (pEdit->oBufMark < EMPTY) { if (pEdit->oBuf Insert <= pEdit ->oBufMark) pEdit->oBufMark— ; if (pEdit->oBuf Insert <= pEdit->oBuf Bound) pEdit->oBufBound— ; } }
I
310
Part III ■ MMURTL-Specific Details
Listing 16.2. continued
if (pEdit->oBuf Insert < pEdit->oBufLine0)
pEdit->oBufLine0 = findPrevLine (pEdit->oBufLine0) ;
f Screen = TRUE;
f Modified = TRUE;
}
break;
case 0x06: /* Home - Cursor to BOL */
pEdit->iCol = pEdit->iColMin;
break;
case 0x09: /* Tab */
if (pEdit->oBufLast + pEdit->iTabNorm < pEdit->iBufMax) {
coordCursor_oBuf ();
j = pEdit->iTabNorm - (pEdit->iCol % pEdit->iTabNorm) ;
for (i=1 ; i <=j ; i++)
putlnBuf (pEdit->bSpace, FALSE, FALSE);
fScreen = TRUE;
}
break;
case 0x10: /* F2 - UNMARK BLOCK */
nullMarkBound ();
break;
case 0x11: /* F3 — Begin Block */
if (pEdit->oBufLast > 0) {
coordCursor_oBuf ();
pEdit->oBufMark = pEdit->oBuf Insert;
i = pEdit->iLine;
if (pEdit->oBufMark >= pEdit->Line[i+1 ] )
pEdit->oBufMark = pEdit->oBufMark - 1;
if (pEdit->oBufMark == pEdit->oBuf Last)
pEdit->oBufMark = pEdit->oBuf Last - 1;
pEdit->oBuf Bound = pEdit->oBufMark;
f Screen = TRUE;
}
break;
case 0x12: /* F4 — End Block */
if (pEdit->oBufMark < EMPTY) {
coordCursor_oBuf ();
pEdit->oBuf Bound = pEdit->oBuf Insert;
i = pEdit->iLine;
if (pEdit->oBuf Bound >= pEdit->Line[i+1 ] )
pEdit->oBufBound— ;
if (pEdit->oBuf Bound == pEdit->oBufLast)
pEdit->oBuf Bound = pEdit->oBuf Last - 1;
f Screen = TRUE;
}
break;
case 0x17: /* F9 - MOVE */
coordCursor_oBuf ();
moveData ();
if (pEdit->oBuf Insert < pEdit ->oBuf Line0)
pEdit->oBufLine0 = pEdit ->oBuf Insert ;
f Screen = TRUE;
break;
case 0x18: /* F10 - COPY •/
coordCursor_oBuf ();
Copylt ();
Chapter 16 ■ MMURTL Sample Software H coordCursor_oBuf (); f Screen = TRUE; break; case 0x0C: /* Page Down */ coordCursoroBuf (); i = pEdit->iRowMax; while ((pEdit->Line[i] == EMPTY) && (i > pEdit ->iRowMin) ) i— ; pEdit->oBufLine0 = pEdit->Line[i] ; /♦always keep onscreen*/ if (pEdit->oBuf Insert < pEdit->oBuf LineO) pEdit->oBuf Insert = pEdit->oBuf LineO; pEdit->iLine = pEdit ->iRowMin; pEdit->iCol = pEdit->iColMin; f Screen = TRUE; break; case 0x05: /* Page Up */ if (pEdit->oBufLine0) { coordCursor_oBuf (); j = pEdit->iRowMax - pEdit->iRowMin; i = pEdit->oBuf LineO; k = pEdit->iLine; /*fix for scrolling when iLine=iRowMax */ do { i = findPrevLine (i); j-; k-; } while ((j > 0) && (i > 0)); pEdit->oBufLine0 = i; /*fix for scroll when iLine=iRowMax*/ if (pEdit->iLine == pEdit->iRowMax) pEdit->oBuf Insert = pEdit->Line[k] ; /*keep on screen*/ i = pEdit->iRowMax; if (pEdit->oBuf Insert >= pEdit->Line[i+1 ] ) pEdit->oBuf Insert = pEdit->Line[i] ; f Screen = TRUE; } break; case 0x01 : /* Up */ if (pEdit->iLine > pEdit->iRowMin) { pEdit->iLine— ; } else { /* scroll screen down if we can */ i = pEdit->oBufLine0; if (i > 0) { i = findPrevLine (i) ; pEdit->oBufLine0 = i; pEdit->oBuf Insert = i; fScreen = TRUE; } } break; case 0x02: /* Down */ i = pEdit->iLine; if ((pEdit->Line[i+1] < EMPTY) && /*Down Arrow*/ (i < pEdit->iRowMax) ) { pEdit->iLine++;
I
312
Part III ■ MMURTL-Specific Details
Listing 16.2. continued
}
else {
/* ELSE scroll screen UP if we can */
i =
pEdit->iRowMax;
if (pEdit->Line[i+1] < EMPTY) {
pEdit->oBuf Insert = pEdit->Line[i+1 ] ;
i = pEdit->iCol;
j = pEdit->iLine;
coordCursor_oBuf ();
pEdit->iCol = i;
pEdit->iLine = j ;
}
fScreen = TRUE;
}
break;
case 0x03:
/* Left */
if (pEdit->iCol > pEdit->iColMin) { /*Left Arrow*/
pEdit->iCol— ;
v break;
case 0x04:
/* Right */
if (pEdit->iCol < pEdit->iColMax) {
pEdit->iCol++;
}
break;
case 0x0E: /* Insert */
if (fOvertype)
f Overtype = FALSE;
else fOvertype = TRUE;
break;
case 0x7F: /* Delete */
coordCursor_oBuf ();
if ((pEdit->oBufLast) &&
(pEdit->oBufLast > pEdit->oBuf Insert) ) {
CopyData(pBuff ,
pBuffWork,
pEdit->oBufLast+1 ) ;
CopyData(&pBuffWork[pEdit->oBuf Insert + 1 ] ,
&pBuff[pEdit->oBuf Insert] ,
pEdit ->oBuf Last - pEdit - >oBuf Insert ) ;
pBuff [pEdit->oBufLast] = 0;
pEdit->oBufLast— ;
if ( (pEdit->oBuf Insert == pEdit ->oBufMark) &&
(pEdit->oBufMark == pEdit ->oBuf Bound) )
nullMarkBound ();
if (pEdit->oBufMark < EMPTY) {
if (pEdit->oBuf Insert < pEdit ->oBufMark)
pEdit ->oBufMark— ;
if (pEdit->oBuf Insert < pEdit ->oBufBound)
pEdit->oBufBound — ;
if (pEdit->oBufMark == pEdit ->oBuf Last )
pEdit->oBufMark — ;
if (pEdit->oBuf Bound == pEdit->oBuf Last)
pEdit->oBuf Bound— ;
}
f Screen = TRUE;
f Modified = TRUE;
Chapter 16 ■ MMURTL Sample Software s } break: default: break; } } } /* Not fDone */ for (i=pEdit->iRowMin; i <=pEdit->iRowMax; i++) PutVidAttrs (pEdit->iColMin, i, pEdit->sLine+1 , 0); /* REM buffer column */ if (fh) { if (pEdit->f Visible) /* fix visible characters */ for (i=0; i <=pEdit->iBufMax; i++) if (pBuff[i] == 0x07) pBuff[i] = 0x20; pBuff [pEdit->oBufLast] = 0; if (fModified) { ere = CheckErc(6, SetFileLFA(f h, 0)); if (!erc) ere = CheckErc(5, SetFileSize(f h,pEdit->oBuf Last) ) ; if (!erc) ere = CheckErc(3, WriteBytes (fh, pBufl, pEdit->oBufLast, &i)); fModified = 0; } CloseFile(fh); cbFilename = 0; } *pbExitRet = b; return; } This is the main entry point for the editor. It allocates two buffers of equal size (a main and a working buffer), and then checks for a single parameter which should be the name of the file to edit. void main(U32 argc, U8 { long i; *argv[]) ClrScr(); Set JobName ( " Editor " fh = 0; , 6); pEdit = &EdRec; ere = AllocPage(32, ere = AllocPage(32, &pBuf1); &pBuf2); /* 32 pages = 128K */ pEdit->pBuf pEdit->pBufWork pEdit->iBufMax pEdit->iColMin = pBufl; = pBuf2; = 131071; = 0; /*sBuf - 1 */ /♦Screen coordinates*/
|314 Part III ■ MMURTL-Specific Details Listing 1 6.2. continued pEdit->iColMax = 79; pEdit->iRowMin = 1 ; pEdit->iRowMax = 23; pEdit->sLine = 80; / pEdit->bSpace = 0x20; pEdit->f Visible = FALSE; pEdit->iAttrMark = MARKVID; / pEdit->iAttrNorm = EDVID; / pEdit->iTabNorm ■ 4; / pEdit->oBufLine0 ■ 0; / pEdit->iCol = 0; / pEdit->il_ine ■ 0; / pEdit->oBuf Insert = 0; / pEdit->oBufLast = 0; / pEdit->oBufMark = EMPTY; pEdit->oBuf Bound = EMPTY; iColMax-iColMin+1 / Rev Vid/ Rev Vid Half Bright */ Tabs every 4th column */ oBufLine0 */ cursor, 0..sLine-1 */ cursor, 0..cLines-l */ offset of next char in */ offset+1 of last char / SetNormVid(NORMVID); FillData(filler, 80, 0x20); for (i=0; KNLINESMAX; i++) pEdit->Line[i] = EMPTY; i = pEdit->iRowMin; pEdit->Line[i] = 0; fModified = 0; f Overtype = FALSE; if (argc > 1 ) { / Set Overtype OFF */ 0penAFile(argv[1 ] ) ; Editor(&b): ExitJob(0): DumbTerm DumbTerm is possibly the “dumbest” communications terminal program in existence. It is included here to demonstrate the interface to the RS-232 communications device driver. Listing 1 6.3. DumbTerm source code. #include <stdio.h> #include <ctype.h> ^include <string.h> ^include “\OSSOURCE\MDevDrv.h’ ^include " \OSSOURCE\MJob.h”
Chapter 16 ■ MMURTL Sample Software H //include “\OSSOURCE\MKbd.h” #include " \OSSOURCE\MTimer.h” //include “\OSSOURCE\MVid.h” //include “\0SS0URCE\RS232.h” #define NORMVID BRITEWHITEJBGBLUE #define CLIVID WHITE |BGBLACK unsigned long key; struct statRecC com; void main(void) { int ere, i; unsigned char b, lastb; char fOK; SetNormVid( NORMVID); ClrScr(); printf(” Terminally DUMB, Dumb Terminal Program\r\n” ) ; printf(” (MMURTL Comms Device Driver demo) \r\n”); /* Get the 64-byte device status block that is specific to the RS-232 device driver. The structure is defined in commdrv.h */ ere = DeviceStat(6, &com, 64, &i); if (ere) { SetNormVid (CLIVID); ClrScr(); printf (“Error on Device Stat: %d\r\n”, ere); ExitJob(erc) ; } /* set the params in the block */ com.Baudrate = 9600; com. parity = NO_PAR; com.databits = 8; com.stopbits = 1 ; /* View other params, which we could set but should already be defaulted with standard values when driver was initialized. */ printf (“IRQNum: %d\r\n”, com.IRQNum); printf (“IOBase: %d\r\n”, com.IOBase); printf (“sXBuf: %d\r\n”, com. XBuf Size ) ;
316 I Part III ■ MMURTL-Specific Details Listing 16.3. continued printf ( “sRBuf : %d\r\n”, com. RBuf Size ) ; printf ( “RTimeO: %d\r\n”, com.RTimeOut) ; printf (“XTimeO: %d\r\n”, com.XTimeOut) ; /* Set the params we changed with a Devicelnit */ ere = Devicelnit(6, &com, 64); if (ere) { SetNormVid(CLIVID); ClrScr(); printf (“Error on Device Init: %d\r\n”, ere); ExitJob(erc) ; } /* If device init went OK, we open the comms port */ /* device, dOpNum, dLBA, dnBlocks, pData */ ere = Device0p(6, CmdOpenC, 0, 0, &i); if (ere) { SetNormVid(CLIVID); ClrScr(); printf ( “OpenCommC ERROR: %d \r\n”, ere); ExitJob(erc) ; } printf ( “Communications Port Initialized. \r\n” ) ; f OK = 1 ; /* This is it. . . */ while (fOK) { if (!ReadKbd(&key, 0)) /* no wait */ { b = key & 0x7f; if (key & 0x3000) { /* ALT key is down */ switch (toupper(b)) { case ‘Q’ : /* device, dOpNum, dLBA, dnBlocks, pData */ ere = Device0p(6, CmdCloseC, 0, 0, &i) SetNormVid(CLIVID); ClrScr(); ExitJob(erc) ; break; default: break; } } else
Chapter 16 ■ MMURTL Sample Software I* device, dOpNum, dLBA, dnBlocks, pData */ ere = Device0p(6, CmdWriteB, 0, 0, &b) ; if (ere) printf (“WriteByteCError: %d \r\n”, ere); else { if (b == 0x0D) { b = 0x0A; ere = Device0p(6, CmdWriteB, 0, 0, &b) } } } } /* device, dOpNum, dLBA, dnBlocks, pData */ ere = Device0p(6, CmdReadB, 0, 0, &b); if (!erc) { TTYOut (&b, 1, NORMVID); /* add a LF if it's not there after a CR... */ if ((lastb == 0x0D) && (b != 0x0A)) TTYOut ("\n”, 1, NORMVID); lastb = b; } Print The Print program is an example of using the device-driver interface for the parallel LPT de- vice driver. The Print program formats a text file and sends it to the LPT device. It recognizes the following command-line switches: \n Expand tabs to n space columns, (n = 1, 2, 4, or 8) \D Display the file while printing. \ F Suppress the form feed sent by the Print command. \B Binary print. Send with no translations. Print also converts single-line feeds to CR/LF. Print Source Listing In Listing 16.4, notice that the device-driver interface is almost identical for each device. (Compare DumbTerm and this program.) That's the intention of the design. The differences in handling different devices will be the values in the status record and also the commands that are specific to a driver.
Part III M MMURTL-Specific Details Listing 16.4. Print source code. /* A simple program that prints a single file directly using the Parallel Device Driver in MMURTL (Device No. 3 “LPT”) . */ #include <stdio.h> #include <ctype.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include " \OSSource\MDevDrv.h” #include “\OSSource\MJob.h” #include " \OSSource\MKbd.h” #include " \OSSource\MTimer.h” #include " \OSSource\MVid.h” #include " \OSSource\Parallel.h” #define FF 0x0C #define LF 0x0A #define CR 0x0D #define TAB 0x09 unsigned long key; long tabstops = 4; long NoFF = 0; long fDisplay = 0; long fBinary = 0; long col = 0; char name[80] ; FILE *f; struct statRecL lpt; void main(long argc, unsigned char *argv[]) { long ere, erck, i, cl; unsigned char b, lastb; char fdone, *ptr; SetJobName( “Printing” , 8); name[0] = 0; for(i=1; i < argc; ++i) /* start at arg 1 */ { ptr = argv[i] ; if (*ptr — ‘/') { ptr++; switch(*ptr) { case ‘1’ : /“Tab Translation Width */
Chapter 16 ■ MMURTL Sample Software H case ‘2’ case ‘4’ case ‘8’ tabstops = *ptr break; case ‘F’ case ‘f NoFF break case ‘D’ case ‘d’ fDisplay = 1 break case ‘B’ case ‘b’ f Binary = 1 ; break; default: printf (“Invalid switch”); exit(1); break;
I
320
Part III M MMURTL-Specific Details
Listing 16.4. continued
ere = Device0p(3, CmdOpenL, 0, 0, &i) ;
if (ere)
{
printf (“OpenLPT ERROR: %d \r\n\ ere);
ExitJob(erc) ;
}
printf ( “Printing %s ...\r\n”, name);
/* This is it. . . */
f = fopen(name, “r”) ;
if (If)
{
/* device, dOpNum, dLBA, dnBlocks, pData */
ere = Device0p(3, CmdCloseLU, 0, 0, &i);
printf (“Can't open: %s\r\n”, name);
ExitJob(erc) ;
}
col = 0;
i = 0;
b = 0;
fdone = 0;
while ((Ifdone) && (!erc))
{
i++;
cl = fgetc(f);
lastb = b;
b = (cl & 0xff);
if (cl == EOF)
fdone = 1 ;
else if (fBinary)
ere = Device0p(3, CmdWriteB, 0, 1, &lastb) ;
else
switch (b)
{ /* print/translate the char */
case CR:
ere = Device0p(3, CmdWriteB, 0, 1, &b);
break;
case LF:
if (lastb != CR)
{
lastb = CR;
ere = Device0p(3, CmdWriteB, 0, 1, &lastb)
Chapter 16 ■ MMURTL Sample Software H ere = DeviceOp(3, CmdWriteB, 0, 1, &b); if (fDisplay) printf ("\r\n” , lastb); col = 0; /* reset */ break; case TAB: do { ere = Device0p(3, CmdWriteB, 0, 1, " “); col++; if (fDisplay) printf (” “); } while (col % tabstops); break; default: if (fDisplay) printf ("%c”, b); col++; ere = Device0p(3, CmdWriteB, 0, 1, &b); if (ere) printf ( “Error Writing Byte: %d\r\n”, ere) break; if (i%l00==0) /* every 100 chars see if they want to abort */ { erck = ReadKbd(&key, 0); /* no wait */ if (!erck) { if (key & 0xff == 0x1b) { f done = 1 ; ere = 4; } } if ((If Binary) && (INoFF)) { ere = Device0p(3, CmdWriteB, 0, 1, “\f”); } fclose(f ) ; /* device, dOpNum, dLBA, dnBlocks, pData ere = Device0p(3, CmdCloseL, 0, 0, &i) ; if (ere) printf ( “Can't close LPT. Error: %d\r\n”, ere); printf ( “Done\r\n” ) ; ExitJob(erc) ;
322 I Part III m MMURTL-Specific Details System Service Example Installable system services are the easiest way to coordinate resource usage and provide addi- tional functionality in MMURTL. The following two listings show a simple system service and a client of that service. The pro- gram is similar to the example shown in Chapter 10, “Systems Programming.” It has been expanded some, and it also allows deinstallation with proper termination and recovery of op- erating-system resources. Service.C Listing Execute this from a command-line interpreter. It becomes a system service while keeping its virtual video screen. Pressing any key will terminate the program properly. While this program is running, run the TestSvc.RUN program to exercise the service. Listing 16.5. Simple service source code. /* Super Simple System Service. This is expanded from the sample in the System Programmer chapter to show how to properly deinstall a system service. The steps to deinstall are:
Chapter 16 ■ MMURTL Sample Software 323 I OSError = RegisterSvc( “NUMBERS “, MainExch); if (OSError) /* look for a system error */ ExitJob(OSError) ; SetNormVid (WHITE | BGBLACK) ; ClrScr(); printf (“NUMBERS Service Installed. \r\n” ) ; printf(“ANY valid keystroke will terminate the service. \r\n” ) ; OSError = Request ( “KEYBOARD” , 1, MainExch, &rqHndl, 0, &keycode, 4, 0, 0, 1, 0, 0); /* 1 in dData0 = WAIT for key */ if (OSError) printf (“Error on Keyboard Request: \r\n” , OSError); while (1) /* WHILE forever (almost...) */ { /* Now we wait for a client or for a keystroke to come back */ OSError = WaitMsg (MainExch, Message); /* Exch & pointer */ if (lOSError) { if (Message[0] == rqHndl) /* it was a keystroke and NOT a client */ { UnRegisterSvc( “NUMBERS “); while ( !CheckMsg(MainExch, Message)) { pRqBlk = Message[0]; Respond(pRqBlk, ErcNoSuchSvc) ; } DeAllocExch (MainExch) ; ExitJob(ErcOpCancel) ; } pRqBlk = Message[0]; /* First DWORD contains ptr to RqBlk */ if (pRqBlk->ServiceCode == 0) /* Abort request from OS */ ErrorToUser = ErcOK; else if (pRqBlk->ServiceCode ==1) /* User Asking for Number */ { pDataRet = pRqBlk->pData1 ; *pDataRet = NextNumber++; /* Give them a number */ ErrorToUser = ErcOK; /* Respond with No error */ printf (“NUMBERS Service gave out number: %d.\r\n”, NextNumber-1 ) ; } else ErrorToUser = ErcBadSvcCode; /* Unknown Service code! */ OSError = Respond(pRqBlk, ErrorToUser); /* Respond to Request */ } } /* Loop while(1) */ }
H Part III ■ MMURTL-Specific Details TestSvc.C Listing The program in Listing 16.6 exercises the sample system service listed previously. Execute this from a CLI while the service is running. (Also try it when the service is not running. You should receive a proper error code indicating that the service is not available.) Listing 16.6. TestSvc.C. /* Test client for the NUMBERS System Service. Run this program from another CLI when Service is running */ #include <stdio.h> #include “\OSSOurce\MKernel.h” unsigned long Number; /* The number to return */ unsigned long Exch; /* Where the service will respond */ unsigned long Message[2]; /* The Message from the service */ void main(void) { unsigned long Error, rqhndl; Error = AllocExch(&Exch) ; /* get an exchange */ if (Error) printf (“Error %d allocating Exchange. \r\n” , Error); Error = Request ( “NUMBERS “, 1, Exch, &rqhndl, 0, /* No Send ptrs */ &Number, 4, 0, 0, 0,0,0); if (Error) printf ( “Error %d from Request . \r\n” , Error); if (!Error) { Error = WaitMsg(Exch, Message); if (Error) printf (“Error %d from WaitMsg. \r\n” , Error); else { if (MessageM]) printf ( “Error %d from NUMBERS Service. \r\n* , Messagefl]); else printf (“NUMBERS Service gave out number: %d.\r\n”, Number); } } DeAllocExch(Exch) ; /* it's nice to do this for the OS */
Powered by TurnKey Linux.