1 /* CLI.C V1.0 (A MMURTL Command Line Interpreter) */
\r
2 /* Copyright (c) 1993, 1994 R.A. Burgess */
\r
3 /* ALL RIGHTS RESERVED */
\r
4 /* Use MakeIt.Bat to build in DOS, or use CM32 and DASM:
\r
10 #define U32 unsigned long
\r
12 #define U16 unsigned int
\r
14 #define U8 unsigned char
\r
24 /* Includes for OS public calls and structures */
\r
26 #include "\OSSOURCE\MKernel.h"
\r
27 #include "\OSSOURCE\MMemory.h"
\r
28 #include "\OSSOURCE\MData.h"
\r
29 #include "\OSSOURCE\MTimer.h"
\r
30 #include "\OSSOURCE\MVid.h"
\r
31 #include "\OSSOURCE\MKbd.h"
\r
32 #include "\OSSOURCE\MJob.h"
\r
33 #include "\OSSOURCE\MFiles.h"
\r
34 #include "\OSSOURCE\MStatus.h"
\r
36 /******************** BEGIN DATA ********************/
\r
38 /* Define the Foreground and Background colors */
\r
40 #define EDVID BLACK|BGWHITE
\r
41 #define NORMVID WHITE|BGBLACK
\r
42 #define STATVID BLACK|BGCYAN
\r
46 /* aStatLine is exactly 80 characters in length */
\r
49 "mm/dd/yy 00:00:00 CLI V1.0 Job Path: ";
\r
55 char sdisk, syspath[50]; /* holds system disk and system path */
\r
56 char hlppath[60]; /* Path to help file */
\r
57 char clipath[60]; /* Path to CLI */
\r
58 char cmdpath[60]; /* Path to command file */
\r
60 char aCmd[80]; /* Command line */
\r
63 unsigned char Buffer[512];
\r
64 unsigned char bigBuf[4096];
\r
69 unsigned long GPExch; /* Messaging for Main */
\r
70 unsigned long GPMsg[2];
\r
71 unsigned long GPHndl;
\r
73 unsigned long StatExch; /* Messaging for status task */
\r
74 long StatStack[256]; /* 1024 byte stack for stat task */
\r
80 /* array of internal commands for parsing */
\r
84 char paCmds[NCMDS+1][10] = {
\r
85 "", /* 0 external */
\r
86 "xxxxx", /* 1 - not used */
\r
104 #define EXTCMD 0 /* External Command */
\r
125 char *apParam[13]; /* Param 0 is cmd name */
\r
127 #define nParamsMax 13
\r
129 #define ErcBadParams 80
\r
131 /* Directory Entry Records, 16 records 32 bytes each */
\r
144 /************************* BEGIN CODE ********************/
\r
146 /*********************************************************
\r
147 This is the status task for the CLI. It updates
\r
148 the status line which has the time and path.
\r
149 It runs as a separate task and is started each
\r
150 time the CLI is executed.
\r
151 *********************************************************/
\r
153 void StatTask(void)
\r
161 FillData(&aStatLine[47], 30, ' ');
\r
162 CopyData(aPath, &aStatLine[47], cbPath);
\r
167 GetCMOSDate(&date);
\r
168 aStatLine[0] = '0' + ((date >> 20) & 0x0f); /* month */
\r
169 aStatLine[1] = '0' + ((date >> 16) & 0x0f);
\r
170 aStatLine[3] = '0' + ((date >> 12) & 0x0f); /* Day */
\r
171 aStatLine[4] = '0' + ((date >> 8) & 0x0f);
\r
172 aStatLine[6] = '0' + ((date >> 28) & 0x0f); /* year */
\r
173 aStatLine[7] = '0' + ((date >> 24) & 0x0f);
\r
175 GetCMOSTime(&time);
\r
176 aStatLine[10] = '0' + ((time >> 20) & 0x0f);
\r
177 aStatLine[11] = '0' + ((time >> 16) & 0x0f);
\r
178 aStatLine[13] = '0' + ((time >> 12) & 0x0f);
\r
179 aStatLine[14] = '0' + ((time >> 8) & 0x0f);
\r
180 aStatLine[16] = '0' + ((time >> 4) & 0x0f);
\r
181 aStatLine[17] = '0' + (time & 0x0f);
\r
183 PutVidChars(0,0, aStatLine, 80, STATVID);
\r
185 Sleep(100); /* sleep 1 second */
\r
189 /*********************************************************
\r
190 This displays error code text string if listed and
\r
192 *********************************************************/
\r
194 void CheckErc (unsigned long erc)
\r
199 FillData(st, 40, 0);
\r
203 case 1: pSt = "End of file"; break;
\r
204 case 4: pSt = "User cancelled"; break;
\r
205 case 80: pSt = "Invalid parameters"; break;
\r
206 case 101: pSt = "Out of memory (need more for this)"; break;
\r
207 case 200: pSt = "Invalid filename (not correct format)"; break;
\r
208 case 201: pSt = "No such drive"; break;
\r
209 case 202: pSt = "The name is not a file (it's a directory)"; break;
\r
210 case 203: pSt = "File doesn't exist"; break;
\r
211 case 204: pSt = "Directory doesn't exist"; break;
\r
212 case 205: pSt = "File is ReadOnly"; break;
\r
213 case 208: pSt = "File in use"; break;
\r
214 case 222: pSt = "Can't rename across drives"; break;
\r
215 case 223: pSt = "Can't rename across directories"; break;
\r
216 case 226: pSt = "File Already Exists (duplicate name)"; break;
\r
217 case 228: pSt = "Root directory is full"; break;
\r
218 case 230: pSt = "Disk is full (bummer)"; break;
\r
219 case 231: pSt = "Directory is full"; break;
\r
222 sprintf(st, "Error %05d on last command", erc);
\r
226 printf("%s\r\n", pSt);
\r
229 /*********************************************************
\r
230 This simply does a software interrupt 03 (Debugger).
\r
231 *********************************************************/
\r
242 /*********************************************************
\r
243 This is called to initialize the screen.
\r
244 If we are returning from and external command
\r
245 we do not reset the cursor position. fNew is
\r
246 used to determine is we do or not.
\r
247 *********************************************************/
\r
249 void InitScreen(int fNew)
\r
251 SetNormVid(NORMVID);
\r
252 GetXY(&iCol, &iLine);
\r
259 SetXY(iCol, iLine);
\r
260 PutVidChars(0,0, aStatLine, 80, STATVID);
\r
264 /*********************************************
\r
265 This does a hex dump to the screen of a
\r
266 memory area passed in by "pb"
\r
267 **********************************************/
\r
269 U32 Dump(unsigned char *pb, long cb, unsigned long addr)
\r
271 U32 erc, i, j, KeyCode;
\r
272 unsigned char buff[17];
\r
275 GetXY(&iCol, &iLine);
\r
279 printf("%08x ", addr);
\r
282 for (i=0; i<j; i++)
\r
284 printf("%02x ",*pb);
\r
286 if (buff[i] < 0x20)
\r
288 if (buff[i] > 0x7F)
\r
292 printf("%s\r\n", &buff[0]);
\r
297 printf("ESC to cancel, any other key to continue...");
\r
298 ReadKbd(&KeyCode, 1); /* ReadKbd, wait */
\r
299 if ((KeyCode & 0xff) == 0x1b)
\r
307 if (cb > 15) cb-=16;
\r
314 /*********************************************************
\r
315 This sets up to call the dump routine for each page.
\r
316 This expects to find the filename in param[1].
\r
317 *********************************************************/
\r
321 unsigned long j, k, l, erc, dret, fh;
\r
324 if ((apParam[1]) && (acbParam[1]))
\r
328 ScrollVid(0,1,80,23,1);
\r
331 erc = OpenFile(apParam[1], acbParam[1], ModeRead, 1, &fh);
\r
334 j=0; /* file lfa */
\r
335 GetFileSize(fh, &k);
\r
336 while ((j<k) && (!erc))
\r
338 FillData(Buffer, 512, 0);
\r
339 erc = ReadBytes (fh, Buffer, 512, &dret);
\r
344 erc = Dump(Buffer, dret, j);
\r
351 printf("Filename not given\r\n");
\r
356 /*********************************************************
\r
357 This types a text file to the screen.
\r
358 *********************************************************/
\r
360 long DoType(char *pName, long cbName)
\r
362 long i, j, lfa, erc, dret, fh, KeyCode;
\r
365 if ((pName) && (cbName))
\r
370 ScrollVid(0,1,80,23,1);
\r
373 erc = OpenFile(pName, cbName, 0, 1, &fh);
\r
376 FillData(Buffer, 512, 0);
\r
378 while ((erc<2) && (dret))
\r
380 GetFileLFA(fh, &lfa);
\r
381 erc = ReadBytes (fh, Buffer, 78, &dret);
\r
383 while ((Buffer[i-1] != 0x0A) && (i < dret))
\r
387 for (j=0; j<=i; j++)
\r
389 if ((Buffer[j] == 0x09) || (Buffer[j] == 0x0d) ||
\r
390 (Buffer[j] == 0x0a))
\r
395 PutVidChars(0, iLine, Buffer, i, NORMVID);
\r
399 SetFileLFA(fh, lfa+i);
\r
403 printf("ESC to cancel, any other key to continue...");
\r
404 ReadKbd(&KeyCode, 1); /* ReadKbd, wait */
\r
405 if ((KeyCode & 0xff) == 0x1b)
\r
412 printf("\r\nError: %d\r\n", erc);
\r
417 printf("Filename not given\r\n");
\r
422 /*******************************************************
\r
423 This reads DOS FAT date & time and converts it into
\r
424 strings with the format MM/DD/YY HH/MM/SS.
\r
425 This is used by the directory listing.
\r
426 ********************************************************/
\r
427 void CnvrtFATTime(U16 time,
\r
432 U32 yr,mo,da,hr,mi,se;
\r
435 yr = ((date & 0xFE00) >> 9) + 1980 - 1900;
\r
436 if (yr > 99) yr -=100;
\r
437 mo = (date & 0x01E0) >> 5;
\r
438 da = date & 0x001F;
\r
439 hr = (time & 0xF800) >> 11;
\r
440 mi = (time & 0x07E0) >> 5;
\r
441 se = (time & 0x001F) * 2;
\r
442 sprintf(st, "%02d:%02d:%02d",hr,mi,se);
\r
443 CopyData(st, pTimeRet, 8);
\r
444 sprintf(st, "%02d-%02d-%02d",mo,da,yr);
\r
445 CopyData(st, pDateRet, 8);
\r
449 /*******************************************************
\r
450 Copy a single file with overwrite checking.
\r
451 Use block mode for speed. This uses a conservative
\r
452 4K buffer. THis could be made a good deal faster by
\r
453 allocating a large chunk of memory and using it
\r
454 instead of the 4K static buffer.
\r
455 ********************************************************/
\r
457 U32 CopyFile(char *pFrom, U32 cbFrom, char *pTo, U32 cbTo)
\r
459 U32 fhTo, fhFrom, bytesWant, bytesGot, bytesLeft, erc,
\r
460 bytesOut, size, dLFA, KeyCode;
\r
462 erc = OpenFile(pFrom, cbFrom, ModeRead, 0, &fhFrom);
\r
465 /* Check to see if it exists already */
\r
467 erc = CreateFile(pTo, cbTo, 0);
\r
468 if ((!erc) || (erc==ErcDupName))
\r
470 if (erc == ErcDupName)
\r
472 printf("File already exists. Overwrite? (Y/N)\r\n");
\r
473 ReadKbd(&KeyCode, 1); /* ReadKbd, wait */
\r
474 if (((KeyCode & 0xff) == 'Y') || ((KeyCode & 0xff) == 'y'))
\r
482 erc = OpenFile(pTo, cbTo, ModeModify, 0, &fhTo);
\r
485 /* both files are open in block mode */
\r
487 erc = GetFileSize(fhFrom, &size);
\r
489 erc = SetFileSize(fhTo, size);
\r
493 while ((!erc) && (bytesLeft))
\r
494 { if (bytesLeft >= 4096)
\r
497 bytesWant = bytesLeft;
\r
498 if (bytesWant & 511) /* handle last sector */
\r
500 bytesWant = (bytesWant/512) * 512;
\r
501 erc = ReadBlock(fhFrom, bigBuf, bytesWant,
\r
505 erc = WriteBlock(fhTo, bigBuf, bytesGot,
\r
507 if (bytesLeft < bytesOut)
\r
510 bytesLeft-=bytesOut;
\r
523 /*********************************************************
\r
524 Does a directory listing for param[1] or current path.
\r
525 *********************************************************/
\r
530 U32 SectNum, erc, KeyCode, i;
\r
535 ScrollVid(0,1,80,23,1);
\r
542 erc = GetDirSector(apParam[1], acbParam[1], &dirent[0], 512, SectNum);
\r
545 for (i=0; i<16; i++)
\r
547 if (!dirent[i].Name[0])
\r
552 if ((dirent[i].Name[0]) && (dirent[i].Name[0] != 0xE5))
\r
554 sprintf(st, "%8s %3s %8d xx/xx/xx xx/xx/xx %2x %04x\r\n",
\r
557 dirent[i].FileSize,
\r
559 dirent[i].StartClstr);
\r
560 CnvrtFATTime(dirent[i].Time, dirent[i].Date, &st[33], &st[24]);
\r
566 printf("ESC to cancel, any other key to continue...");
\r
567 ReadKbd(&KeyCode, 1); /* ReadKbd, wait */
\r
568 if ((KeyCode & 0xff) == 0x1b)
\r
588 /*********************************************************
\r
589 This fills in the pointers to, and sizes of, each parameter
\r
590 found on the command line in the arrays apParams and
\r
592 *********************************************************/
\r
594 void ParseCmdLine(void)
\r
596 long iCmd, iPrm, i; /* iCmd is index to aCmd */
\r
598 for (iPrm=0; iPrm<nParamsMax; iPrm++)
\r
600 acbParam[iPrm] = 0; /* default the param to empty */
\r
601 apParam[iPrm] = 0; /* Null the ptr */
\r
604 if (!isspace(aCmd[iCmd]))
\r
606 apParam[iPrm] = &aCmd[iCmd++]; /* ptr to param */
\r
608 while ((iCmd < cbCmd) && (!isspace(aCmd[iCmd])))
\r
613 acbParam[iPrm] = i; /* size of param */
\r
615 while ((iCmd < cbCmd) && (isspace(aCmd[iCmd])))
\r
621 /*************************************************
\r
622 This opens and parses Commands.CLI in the system
\r
623 directory and looks for the command name in
\r
624 apParam[0]. If it finds it, it places the full
\r
625 filespec for the run file in "runfile" for the
\r
626 caller, then fixes up the aCmd command line
\r
627 just as if it had come from the user.
\r
628 The format for a line entry in Commands.CLI is:
\r
629 name fullspec param param param...
\r
630 **************************************************/
\r
632 long GetCLICommand(char *runfile)
\r
636 char rawline[90]; /* used to build a pseudo command line */
\r
637 char cmdline[90]; /* used to build a pseudo command line */
\r
642 f = fopen(cmdpath, "r");
\r
648 if (fgets(rawline, 89, f))
\r
650 if (rawline[0] == ';') /* a comment line */
\r
652 j = CompareNCS(apParam[0], rawline, acbParam[0]);
\r
654 { /* we found a match for the command */
\r
655 /* Move the command name into the command line */
\r
656 i = 0; /* Index into rawline */
\r
657 j = 0; /* Index into cmdline we are building */
\r
658 k = 0; /* Index into runfile name we return to caller */
\r
659 while (!(isspace(rawline[i])))
\r
660 cmdline[j++] = rawline[i++];
\r
661 /* follwed by a space */
\r
662 cmdline[j++] = ' ';
\r
664 /* skip whitespace in rawline */
\r
665 while (isspace(rawline[i]))
\r
668 /* now move runfile name */
\r
669 while (!(isspace(rawline[i])))
\r
670 runfile[k++] = rawline[i++];
\r
671 runfile[k] = 0; /* null terminte */
\r
673 /* skip whitespace in rawline */
\r
674 while (isspace(rawline[i]))
\r
676 /* now move arguments if any, and LF */
\r
678 cmdline[j++] = rawline[i++];
\r
681 /* Move cmd line we built to real cmd line */
\r
682 strcpy(aCmd, cmdline);
\r
683 cbCmd = strlen(aCmd);
\r
684 return(1); /* tell em we got it! */
\r
692 printf("Commands.CLI not found.\r\n");
\r
698 /**************************************************
\r
699 When a command is specified to the CLI and it
\r
700 is not an internal command, we first look for
\r
701 a .RUN file in the current path. If we don't
\r
702 find one, we go to the system directory, if
\r
703 it's not there, we then go to COMMANDS.CLI
\r
704 and search it line by line to see if a run
\r
705 file is specified there. If so, we set the
\r
706 command line with everything after the runfile
\r
707 specified and try to execute it.
\r
708 ***************************************************/
\r
710 void FindRunFile(void)
\r
715 /* Try to find in current path */
\r
717 CopyData(apParam[0], runfile, acbParam[0]);
\r
718 runfile[acbParam[0]] = 0;
\r
719 strcat(runfile, ".RUN");
\r
721 erc = OpenFile(runfile, strlen(runfile), ModeRead, 1, &fh);
\r
723 { /* found a run file in sys directory */
\r
725 SetCmdLine(aCmd, cbCmd);
\r
726 erc = Chain(runfile, strlen(runfile), 0);
\r
728 else /* Try to find in System directory */
\r
730 strcpy(runfile, syspath);
\r
731 i = strlen(runfile);
\r
732 CopyData(apParam[0], &runfile[i], acbParam[0]);
\r
733 runfile[acbParam[0]+i] = 0; /* null terminate */
\r
734 strcat(runfile, ".RUN");
\r
735 erc = OpenFile(runfile, strlen(runfile), ModeRead, 1, &fh);
\r
737 { /* found a run file */
\r
739 SetCmdLine(aCmd, cbCmd);
\r
740 erc = Chain(runfile, strlen(runfile), 0);
\r
742 /* now we call GetCLICommand as a last resort */
\r
743 else if (GetCLICommand(runfile))
\r
745 erc = OpenFile(runfile, strlen(runfile), ModeRead, 1, &fh);
\r
747 { /* found a run file */
\r
749 SetCmdLine(aCmd, cbCmd);
\r
750 erc = Chain(runfile, strlen(runfile), 0);
\r
754 printf("Command not found\r\n");
\r
757 /**********************************************
\r
758 Main function entry point.
\r
759 Note: No params to CLI.
\r
760 ***********************************************/
\r
764 unsigned long erc, i, j, fh;
\r
766 erc = AllocExch(&StatExch);
\r
767 erc = AllocExch(&GPExch);
\r
769 SpawnTask(&StatTask, 24, 0, &StatStack[255], 0);
\r
771 GetJobNum(&JobNum);
\r
772 sprintf(&aStatLine[37], "%02d", JobNum);
\r
774 SetJobName("CLI V1.0", 8);
\r
776 /* Get system disk and set up path names for
\r
777 cli, command file, and help file.
\r
780 GetSystemDisk(&sdisk);
\r
782 sdisk += 0x41; /* 0=A, 1=B, 2=C etc. */
\r
783 syspath[0] = sdisk;
\r
786 strcat(syspath, "\\MMSYS\\");
\r
788 strcpy(clipath, syspath);
\r
789 strcat(clipath,"CLI.RUN");
\r
791 strcpy(cmdpath, syspath);
\r
792 strcat(cmdpath,"COMMANDS.CLI");
\r
794 strcpy(hlppath, syspath);
\r
795 strcat(hlppath,"HELP.CLI");
\r
797 /* If a path was already set we assume that we are re-loading
\r
798 after an external command has been run. We do not
\r
799 want to reset the screen completely so the user can
\r
800 see anything the external command left on the screen.
\r
804 GetPath(JobNum, aPath, &cbPath);
\r
807 GetXY(&iCol, &iLine);
\r
808 if (iLine == 0) /* under status line */
\r
815 strcpy (aPath, syspath);
\r
816 cbPath = strlen(syspath);
\r
817 SetPath(syspath, strlen(syspath));
\r
825 FillData(aCmd, 79, ' ');
\r
829 TTYOut (">", 1, NORMVID);
\r
830 EditLine(aCmd, cbCmd, 79, &cbCmd, &ExitChar, EDVID);
\r
831 TTYOut ("\r\n", 2, NORMVID);
\r
832 GetXY(&iCol, &iLine);
\r
838 if (ExitChar == 0x0d)
\r
839 ParseCmdLine(); /* set up all params */
\r
841 if ((acbParam[0]) && (apParam[0]))
\r
842 { /* It's a command! */
\r
847 j = strlen(paCmds[i]);
\r
848 if ((acbParam[0] == j) &&
\r
849 (CompareNCS(apParam[0], paCmds[i], j) == -1))
\r
859 case EXTCMD: /* external command */
\r
860 SetExitJob(clipath, strlen(clipath));
\r
867 if ((acbParam[1]) && (acbParam[2]))
\r
868 erc = CopyFile(apParam[1], acbParam[1],
\r
869 apParam[2], acbParam[2]);
\r
870 else erc = ErcBadParams;
\r
873 erc = OpenFile(apParam[1], acbParam[1], 1, 0, &fh);
\r
875 erc = DeleteFile(fh);
\r
877 printf("Done.\r\n");
\r
893 erc = DoType(hlppath, strlen(hlppath));
\r
896 erc = CreateDir(apParam[1], acbParam[1]);
\r
899 erc = Request("KEYBOARD", 4, GPExch, &GPHndl, 0,
\r
900 0, 0, 0, 0, 1, 0, 0);
\r
901 erc = WaitMsg(GPExch, GPMsg);
\r
905 erc = SetPath(apParam[1], acbParam[1]);
\r
907 erc = GetPath(JobNum, aPath, &cbPath);
\r
911 erc = DeleteDir(apParam[1], acbParam[1]);
\r
914 if ((acbParam[1]) && (acbParam[2]))
\r
915 erc = RenameFile(apParam[1], acbParam[1],
\r
916 apParam[2], acbParam[2]);
\r
917 else erc = ErcBadParams;
\r
923 while (aCmd[i] != ' ')
\r
925 SetCmdLine(&aCmd[i], cbCmd-i);
\r
926 SetExitJob(clipath, strlen(clipath));
\r
927 erc = Chain(apParam[1], acbParam[1], 0);
\r
931 erc = DoType(apParam[1], acbParam[1]);
\r
939 GetXY(&iCol, &iLine);
\r
942 ScrollVid(0,1,80,23,1);
\r