1 /*
2  * The contents of this file are subject to the Mozilla Public License
3  * Version 1.0 (the "License"); you may not use this file except in
4  * compliance with the License. You may obtain a copy of the License at
5  * http://www.mozilla.org/MPL/
6  *
7  * Software distributed under the License is distributed on an "AS IS"
8  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
9  * License for the specific language governing rights and limitations
10  * under the License.
11  *
12  * The Initial Developer of this code is David Baum.
13  * Portions created by David Baum are Copyright (C) 1998 David Baum.
14  * All Rights Reserved.
15  *
16  * Portions created by John Hansen are Copyright (C) 2005 John Hansen.
17  * All Rights Reserved.
18  *
19  */
20 
21 #ifdef WIN32
22 #include <windows.h>
23 #endif
24 
25 #include <cstdio>
26 #include <cstring>
27 #include <cctype>
28 #include <cstdlib>
29 #include <ctime>
30 
31 #include "Program.h"
32 #include "RCX_Image.h"
33 #include "RCX_Link.h"
34 #include "Symbol.h"
35 #include "PreProc.h"
36 #include "parser.h"
37 #include "Macro.h"
38 #include "RCX_Cmd.h"
39 #include "RCX_Log.h"
40 #include "SRecord.h"
41 #include "AutoFree.h"
42 #include "DirList.h"
43 #include "Buffer.h"
44 #include "Error.h"
45 #include "Compiler.h"
46 #include "CmdLine.h"
47 #include "version.h"
48 
49 using std::fopen;
50 using std::printf;
51 using std::time_t;
52 using std::localtime;
53 using std::tm;
54 
55 
56 // use this to check for memory leaks in the compiler
57 //#define CHECK_LEAKS
58 
59 // use these to debug the LEXER
60 //#define TEST_LEXER	1	// test lexer only
61 //#define TEST_LEXER	2	// test lexer and pre-processor
62 
63 
64 
65 #ifdef CHECK_LEAKS
66 #include <DebugNew.h>
67 #endif
68 
69 // some win32 consoles are lame...use stdout instead of stderr for usage, etc.
70 #ifdef WIN32
71 #define STDERR stdout
72 #else
73 #define STDERR stderr
74 #endif
75 
76 FILE *gErrorStream = stderr;
77 
78 #define kMaxPrintedErrors	10
79 
80 
81 
82 class AutoLink : public RCX_Link
83 {
84 public:
AutoLink()85 	AutoLink() : fSerialPort(0), fOpen(false) {}
~AutoLink()86 	~AutoLink()	{ Close(); }
87 
88 	RCX_Result	Open();
89 	void		Close();
90 	RCX_Result	Send(const RCX_Cmd *cmd, bool retry=true);
91 
SetSerialPort(const char * sp)92 	void		SetSerialPort(const char *sp)	{ fSerialPort = sp; }
93 
94 	bool		DownloadProgress(int soFar, int total);
95 
96 private:
97 	const char*	fSerialPort;
98 	bool		fOpen;
99 };
100 
101 
102 
103 class MyCompiler : public Compiler, public ErrorHandler
104 {
105 public:
MyCompiler()106 	MyCompiler()	 {}
107 
108 	Buffer *CreateBuffer(const char *name);
109 
110 	void	AddError(const Error &e, const LexLocation *loc);
AddDir(const char * dirspec)111 	void	AddDir(const char *dirspec)	{ fDirs.Add(dirspec); }
112 
113 private:
114 	DirList	fDirs;
115 } gMyCompiler;
116 
117 
118 
119 #define kMaxFirmware 65536
120 #define kOldIncludePathEnv	"NQCC_INCLUDE"
121 #define kNewIncludePathEnv	"NQC_INCLUDE"
122 #define kOptionsEnv			"NQC_OPTIONS"
123 #define kLowBattery		6600
124 #define kLow45Battery	3300
125 
126 #define kRCXFileExtension ".rcx"
127 #define kNQCFileExtension ".nqc"
128 
129 
130 
131 // error codes in addition to RCX_Result codes
132 #define kUsageError	(kRCX_LastError - 1)
133 #define kQuietError	(kRCX_LastError - 2)
134 
135 // codes for the actions
136 enum
137 {
138 	kFirstActionCode = 256,
139 	kDatalogCode = kFirstActionCode,
140 	kDatalogFullCode,
141 	kClearMemoryCode,
142 	kFirmwareCode,
143 	kFirmware4xCode,
144 	kNearCode,
145 	kFarCode,
146 	kWatchCode,
147 	kSleepCode,
148 	kRunCode,
149 	kProgramCode,
150 	kMessageCode,
151 	kRawCode,
152 	kRaw1Code,
153 	kRemoteCode,
154 	kApiCode,
155 	kHelpCode,
156 	kCompileStdinCode
157 };
158 
159 // these must be in the same order as the codes for the long options
160 static const char *sActionNames[] = {
161 	"datalog", "datalog_full", "clear", "firmware", "firmfast", "near", "far", "watch",
162 	"sleep", "run", "pgm", "msg", "raw", "raw1", "remote", "api", "help", ""
163 };
164 
165 // these MUST be in the same order as the RCX_TargetType values
166 static const char *sTargetNames[] =
167 {
168 	"rcx",
169 	"cm",
170 	"scout",
171 	"rcx2",
172 	"spy",
173         "swan"
174 };
175 
176 
177 struct Request
178 {
179 	const char *fSourceFile;
180 	const char *fOutputFile;
181 	const char *fListFile;
182 	bool	fListing;
183 	bool	fSourceListing;
184 	bool	fDownload;
185 	bool	fBinary;
186         bool    fGenLASM;
187 	int	fFlags;
188 };
189 
190 static int GetActionCode(const char *arg);
191 static RCX_Result ProcessCommandLine(int argc, char **argv);
192 static void PrintError(RCX_Result error, const char *filename = 0);
193 static void PrintUsage();
194 static void DefineMacro(const char *text);
195 static RCX_Result ProcessFile(const char *sourceFile, const Request &req);
196 static char *CreateFilename(const char *source, const char *oldExt, const char *newExt);
197 static const char *LeafName(const char *filename);
198 static int CheckExtension(const char *s1, const char *ext);
199 static RCX_Image *Compile(const char *sourceFile,  int flags);
200 static bool GenerateListing(RCX_Image *image, const char *filename, bool includeSource, bool generateLASM);
201 static RCX_Result Download(RCX_Image *image);
202 static RCX_Result UploadDatalog(bool verbose);
203 static RCX_Result DownloadFirmware(const char *filename, bool fast);
204 static RCX_Result SetWatch(const char *timeSpec);
205 static RCX_Result SetErrorFile(const char *filename);
206 static RCX_Result RedirectOutput(const char *filename);
207 static RCX_Result SendRawCommand(const char *text, bool retry);
208 static RCX_Result ClearMemory();
209 static RCX_Result SetTarget(const char *name);
210 static RCX_Result SendRemote(const char *event, int repeat);
211 static void PrintToken(int t, TokenVal v);
212 static void PrintApi(bool compatMode);
213 static bool SameString(const char *s1, const char *s2);
214 static void PrintVersion();
215 
216 AutoLink gLink;
217 RCX_TargetType gTargetType = kRCX_RCXTarget;
218 bool gVerbose = false;
219 int gTimeout = 0;
220 bool gQuiet = false;
221 
222 
main(int argc,char ** argv)223 int main(int argc, char **argv)
224 {
225 	RCX_Result result;
226 
227 	// add any default include paths
228 	gMyCompiler.AddDir(getenv(kOldIncludePathEnv));
229 	gMyCompiler.AddDir(getenv(kNewIncludePathEnv));
230 
231 	result = ProcessCommandLine(argc, argv);
232 	PrintError(result);
233 
234 	gLink.Close();
235 
236 	if (gErrorStream != stderr &&
237 		gErrorStream != stdout)
238 		fclose(gErrorStream);
239 
240 #ifdef DEBUG_NEW
241 	printf("%d allocations, %d total bytes, %d max\n", gDebugNewAllocCount, gDebugNewAllocCurr, gDebugNewAllocMax);
242 #endif
243 
244 
245 	return RCX_ERROR(result) ? result : 0;
246 }
247 
248 
ProcessCommandLine(int argc,char ** argv)249 RCX_Result ProcessCommandLine(int argc, char ** argv)
250 {
251 	bool optionsOK = true;
252 	bool fileProcessed = false;
253 	Request req = { 0 };	// rest will be zero'ed
254 	CmdLine args;
255 	RCX_Result result = kRCX_OK;
256 	RCX_Cmd cmd;
257 
258 	// first add environment options
259 	args.Parse(getenv(kOptionsEnv));
260 
261 	// add all command line args after the first one
262 #ifdef WIN32
263 	// For Win32, bypass the argc/argv array and get the raw command line
264 	// This is for backwards compatability with BricxCC which doesn't like its
265 	// args to be escaped by the runtime
266 	#pragma unused(argc, argv)
267 	args.Parse(GetCommandLineA(), 1);
268 #else
269 	if (argc > 1)
270 		args.Add(argc-1, argv+1);
271 #endif
272 
273 	// process the args
274 	while(args.Remain() && !RCX_ERROR(result))
275 	{
276 		const char* a=args.Next();
277 		bool isOption = (a[0] == '-');
278 
279 #ifdef WIN32
280 		if (a[0]=='/') isOption = true;
281 #endif
282 
283 		if (isOption)
284 		{
285 			int code = GetActionCode(a+1);
286 
287 			if (code)
288 			{
289 				optionsOK = false;
290 			}
291 			else
292 			{
293 				if (!optionsOK) return kUsageError;
294 				code = a[1];
295 			}
296 
297 			switch(code)
298 			{
299 				// options
300 				case '1':
301 					req.fFlags |= Compiler::kCompat_Flag;
302 					break;
303 				case 'D':
304 					if  (*(a+2)=='\0') return kUsageError;
305 					DefineMacro(a+2);
306 					break;
307 				case 'E':
308 					result = SetErrorFile(a+2);
309 					break;
310 				case 'I':
311 					if  (*(a+2)=='\0') return kUsageError;
312 					gMyCompiler.AddDir(a+2);
313 					break;
314 				case 'L':
315 					req.fListing = true;
316 					req.fListFile = a[2] ? a+2 : 0;
317 					break;
318 				case 'R':
319 					result = RedirectOutput(a+2);
320 					break;
321 				case 's':
322 					req.fSourceListing = true;
323 					break;
324 				case 'c':
325 					req.fGenLASM = true;
326                                         break;
327 				case 'O':
328 					if  (*(a+2)=='\0') return kUsageError;
329 					req.fOutputFile = a+2;
330 					break;
331 				case 'S':
332 					if  (*(a+2)=='\0') return kUsageError;
333 					gLink.SetSerialPort(a+2);
334 					break;
335 				case 'T':
336 					if  (*(a+2)=='\0') return kUsageError;
337 					result = SetTarget(a+2);
338 					break;
339                                 case 'x':
340                                         gLink.SetOmitHeader(true);
341                                         break;
342 //                                case 'p':
343 //                                        if  (*(a+2)=='\0') return kUsageError;
344 //                                        gLink.SetRCXProgramChunkSize(atoi(a+2));
345 //                                        break;
346                                 case 'f':
347                                         if  (*(a+2)=='\0') return kUsageError;
348                                         gLink.SetRCXFirmwareChunkSize(atoi(a+2));
349                                         break;
350                                 case 'w':
351                                         if  (*(a+2)=='\0') return kUsageError;
352                                         gLink.SetDownloadWaitTime(atoi(a+2));
353                                         break;
354 				case 'U':
355 					if  (*(a+2)=='\0') return kUsageError;
356 					Compiler::Get()->Undefine(a+2);
357 					break;
358 				case 'd':
359 					req.fDownload = true;
360 					break;
361 				case 'l':
362 					req.fListing = true;
363 					break;
364 				case 'n':
365 					req.fFlags |= Compiler::kNoSysFile_Flag;
366 					break;
367 				case 'b':
368 					req.fBinary = true;
369 					break;
370 				case 't':
371 					if (!args.Remain()) return kUsageError;
372 					gTimeout = args.NextInt();
373 					break;
374 				case 'v':
375 					gVerbose = true;
376 					break;
377                                 case 'q':
378                                         gQuiet = true;
379                                         break;
380 
381 				// actions
382 				case kCompileStdinCode:
383 					result = ProcessFile(nil, req);
384 					fileProcessed = true;
385 					break;
386 				case kDatalogCode:
387 					result = UploadDatalog(false);
388 					break;
389 				case kDatalogFullCode:
390 					result = UploadDatalog(true);
391 					break;
392 				case kClearMemoryCode:
393 					result = ClearMemory();
394 					break;
395 				case kFirmwareCode:
396 					if (!args.Remain()) return kUsageError;
397 					result = DownloadFirmware(args.Next(), false);
398 					break;
399 				case kFirmware4xCode:
400 					if (!args.Remain()) return kUsageError;
401 					result = DownloadFirmware(args.Next(), true);
402 					break;
403 				case kNearCode:
404 					result = gLink.Send(cmd.Set(kRCX_IRModeOp, 0));
405 					break;
406 				case kFarCode:
407 					result = gLink.Send(cmd.Set(kRCX_IRModeOp, 1));
408 					break;
409 				case kWatchCode:
410 					if (!args.Remain()) return kUsageError;
411 					result = SetWatch(args.Next());
412 					break;
413 				case kSleepCode:
414 					if (!args.Remain()) return kUsageError;
415 					result = gLink.Send(cmd.Set(kRCX_AutoOffOp, (UByte)args.NextInt()));
416 					break;
417 				case kRunCode:
418 					result = gLink.Send(cmd.Set(kRCX_StartTaskOp, getTarget(gTargetType)->fRanges[kRCX_TaskChunk].fBase));
419 					break;
420 				case kProgramCode:
421 					if (!args.Remain()) return kUsageError;
422 					result = gLink.Send(cmd.Set(kRCX_SelectProgramOp, (UByte)(args.NextInt()-1)));
423 					break;
424 				case kMessageCode:
425 					if (!args.Remain()) return kUsageError;
426 					result = gLink.Send(cmd.Set(kRCX_Message, (UByte)(args.NextInt())), false);
427 					break;
428 				case kRawCode:
429 				case kRaw1Code:	// one-time send, no retry
430 					if (!args.Remain()) return kUsageError;
431 					result = SendRawCommand(args.Next(), code == kRawCode);
432 					break;
433 				case kRemoteCode:
434 					if (args.Remain() < 2) return kUsageError;
435 					{
436 						const char *event = args.Next();
437 						int repeat = args.NextInt();
438 						result = SendRemote(event, repeat);
439 					}
440 					break;
441 
442 				case kApiCode:
443 					PrintApi(req.fFlags & Compiler::kCompat_Flag);
444 					break;
445 
446 				case kHelpCode:
447 					PrintUsage();
448 					break;
449 				default:
450 					return kUsageError;
451 			}
452 		}
453 		else if (!fileProcessed)
454 		{
455 			result = ProcessFile(a, req);
456 
457 #ifdef CHECK_LEAKS
458 			int firstAllocCount = gDebugNewAllocCount;
459 			Compiler::Get()->Reset();
460 			result = ProcessFile(a, req);
461 			printf("%d leaked allocations\n", gDebugNewAllocCount - firstAllocCount);
462 #endif
463 			optionsOK = false;
464 			fileProcessed = true;
465 		}
466 		else
467 			return kUsageError;
468 	}
469 
470 	// check if we did anything (compile and/or actions)
471 	if (optionsOK) return kUsageError;
472 
473 	return result;
474 }
475 
476 
PrintApi(bool compatMode)477 void PrintApi(bool compatMode)
478 {
479 	Buffer *b = Compiler::CreateApiBuffer(compatMode);
480 	fwrite(b->GetData(), b->GetLength(), 1, stdout);
481 	delete b;
482 }
483 
484 
SetTarget(const char * name)485 RCX_Result SetTarget(const char *name)
486 {
487 	for(unsigned int i=0; i<sizeof(sTargetNames) / sizeof(const char *); ++i)
488 	{
489 		if (SameString(name, sTargetNames[i]))
490 		{
491 			gTargetType = (RCX_TargetType)i;
492 			return kRCX_OK;
493 		}
494 	}
495 
496 	fprintf(STDERR, "Warning: use of abbreviated target names has been deprecated\n");
497 
498 	switch(name[0])
499 	{
500 		case 'r':
501 		case 'R':
502 			gTargetType = kRCX_RCXTarget;
503 			break;
504 		case 'c':
505 		case 'C':
506 			gTargetType = kRCX_CMTarget;
507 			break;
508 		case 's':
509 		case 'S':
510 			gTargetType = kRCX_ScoutTarget;
511 			break;
512 		default:
513 			return kUsageError;
514 	}
515 
516 	return kRCX_OK;
517 }
518 
519 
SetWatch(const char * timeSpec)520 RCX_Result SetWatch(const char *timeSpec)
521 {
522 	int hour;
523 	int minute;
524 	RCX_Cmd cmd;
525 
526 	if (strcmp(timeSpec, "now")==0)
527 	{
528 		time_t t;
529 		struct tm *tmp;
530 
531 		time(&t);
532 		tmp = localtime(&t);
533 		hour = tmp->tm_hour;
534 		minute = tmp->tm_min;
535 	}
536 	else
537 	{
538 		int t = atoi(timeSpec);
539 		hour = t / 100;
540 		minute = t % 100;
541 	}
542 
543 	return gLink.Send(cmd.Set(kRCX_SetWatchOp, (UByte)hour, (UByte)minute));
544 }
545 
546 
ProcessFile(const char * sourceFile,const Request & req)547 RCX_Result ProcessFile(const char *sourceFile, const Request &req)
548 {
549 	RCX_Image *image;
550 	RCX_Result result = kRCX_OK;
551 	bool ok = true;
552 	bool compiled = false;
553 
554 	if (sourceFile && (req.fBinary || CheckExtension(sourceFile, kRCXFileExtension)))
555 	{
556 		// load RCX image file
557 		image = new RCX_Image();
558 		result = image->Read(sourceFile);
559 		if (RCX_ERROR(result))
560 		{
561 			PrintError(result, sourceFile);
562 			delete image;
563 			return kQuietError;
564 		}
565 	}
566 	else
567 	{
568 		// compile file
569 		compiled = true;
570 		image = Compile(sourceFile, req.fFlags);
571 
572 		if (!image)
573 		{
574 			int errors = ErrorHandler::Get()->GetErrorCount();
575 
576 			if (errors)
577 				fprintf(gErrorStream, "# %d error%s during compilation\n", errors, errors==1 ? "" : "s");
578 			return kQuietError;
579 		}
580 
581 		const char *outputFile = req.fOutputFile;
582 		char *newFilename = 0;
583 
584 		if (!req.fDownload && !req.fListing && !req.fOutputFile && sourceFile)
585 			outputFile = newFilename = CreateFilename(LeafName(sourceFile), kNQCFileExtension, kRCXFileExtension);
586 
587 		if (outputFile)
588 		{
589 			if (!image->Write(outputFile))
590 			{
591 				fprintf(gErrorStream, "Error: could not create output file \"%s\"\n", outputFile);
592 				ok = false;
593 			}
594 		}
595 
596 		if (newFilename)
597 			delete [] newFilename;
598 	}
599 
600 	// generate the listing
601 	if (req.fListing)
602 	{
603 		if (!GenerateListing(image, req.fListFile, compiled && req.fSourceListing, req.fGenLASM))
604 			ok = false;
605 	}
606 
607 	// reset the compiler after generating the listing so that the Compiler's
608 	// buffers will be available for inserting source code into the listing
609 	if (compiled)
610 		Compiler::Get()->Reset();
611 
612 	if (req.fDownload)
613 	{
614 		result = Download(image);
615 	}
616 
617 	// Check for the case of a listing or output file failure but
618 	// download ok.  In this case an error code must still be returned
619 	// so that command line processing stops and main() indicates
620 	// the failure
621 	if (result==kRCX_OK && !ok)
622 		result = kQuietError;
623 
624 	delete image;
625 	return result;
626 }
627 
628 
GenerateListing(RCX_Image * image,const char * filename,bool includeSource,bool generateLASM)629 bool GenerateListing(RCX_Image *image, const char *filename, bool includeSource, bool generateLASM)
630 {
631 	FILE *fp;
632 
633 	if (filename)
634 	{
635 		fp = fopen(filename, "w");
636 		if (!fp)
637 		{
638 			fprintf(STDERR, "Error: could not generate listing to file %s\n", filename);
639 			return false;
640 		}
641 	}
642 	else
643 		fp = stdout;
644 
645 	RCX_StdioPrinter dst(fp);
646 	image->Print(&dst, includeSource ? Compiler::Get() : 0, generateLASM);
647 
648 	if (fp != stdout)
649 		fclose(fp);
650 
651 	return true;
652 }
653 
654 
Download(RCX_Image * image)655 RCX_Result Download(RCX_Image *image)
656 {
657 	RCX_Result result;
658 	RCX_Cmd cmd;
659 
660 	fprintf(STDERR, "Downloading Program:");
661 	result = gLink.Open();
662 	if (result != kRCX_OK) goto ErrorReturn;
663 
664 	result = image->Download(&gLink);
665 	if (result != kRCX_OK) goto ErrorReturn;
666 
667 	fprintf(STDERR, "complete\n");
668 
669 	result = gLink.GetBatteryLevel();
670 	if (!RCX_ERROR(result))
671 	{
672 		fprintf(STDERR, "Battery Level = %3.1f V\n", (double)result / 1000);
673 		int lowBatt = (gTargetType==kRCX_SpyboticsTarget) ? kLow45Battery : kLowBattery;
674 		if (result < lowBatt)
675 			fprintf(STDERR, "*** Warning: batteries are low ***\n");
676 	}
677 
678 	return kRCX_OK;
679 
680 ErrorReturn:
681 	fprintf(STDERR, "error\n");
682 	return result;
683 }
684 
685 
Compile(const char * sourceFile,int flags)686 RCX_Image *Compile(const char *sourceFile, int flags)
687 {
688 	Buffer *mainBuf;
689 
690 	if (sourceFile)
691 	{
692 		FILE *fp  = fopen(sourceFile, "rb");
693 		if (!fp)
694 		{
695 			fprintf(gErrorStream, "Error: could not open file \"%s\"\n", sourceFile);
696 			return nil;
697 		}
698 
699 		mainBuf = new Buffer();
700 		mainBuf->Create(sourceFile, fp);
701 		fclose(fp);
702 	}
703 	else
704 	{
705 		mainBuf = new Buffer();
706 		mainBuf->Create("<stdin>", stdin);
707 	}
708 
709 #ifdef TEST_LEXER
710 	LexPush(mainBuf);
711 
712 	int t;
713 	TokenVal v;
714 	LexLocation loc;
715 
716 #if TEST_LEXER > 1
717 	while((t=gPreProc->Get(v)) != 0)
718 #else
719 	while((t=yylex(v)) != 0)
720 #endif
721 	{
722 		LexCurrentLocation(loc);
723 		printf("%3d (%2d) : ", loc.fOffset, loc.fLength);
724 		PrintToken(t, v);
725 	}
726 
727 	LexCurrentLocation(loc);
728 	printf("%3d (%2d) : EOF\n", loc.fOffset, loc.fLength);
729 
730 	return 0;
731 #else
732 	Buffer *b = new Buffer();
733 	b->Create(mainBuf->GetName(), mainBuf->GetData(), mainBuf->GetLength());
734 
735 	return Compiler::Get()->Compile(mainBuf, getTarget(gTargetType), flags);
736 #endif
737 }
738 
739 
UploadDatalog(bool verbose)740 RCX_Result UploadDatalog(bool verbose)
741 {
742 	RCX_Log log;
743 	RCX_Result result;
744 	int i;
745 
746 	fprintf(STDERR, "Uploading Datalog");
747 
748 	result = gLink.Open();
749 	if (RCX_ERROR(result)) return result;
750 
751 	result = log.Upload(&gLink);
752 	if (RCX_ERROR(result)) return result;
753 
754 	fprintf(STDERR, "\n");
755 
756 	for(i=0; i<log.GetLength(); i++)
757 	{
758 		char line[256];
759 		log.SPrintEntry(line, i, verbose);
760 		printf("%s\n", line);
761 	}
762 
763 	return kRCX_OK;
764 }
765 
766 
ClearMemory()767 RCX_Result ClearMemory()
768 {
769 	RCX_Result result;
770 	RCX_Cmd cmd;
771 
772 	// halt all tasks
773 	result = gLink.Send(cmd.Set(kRCX_StopAllOp));
774 	if (RCX_ERROR(result)) return result;
775 
776 	for(UByte p=0; p<5; p++)
777 	{
778 		// select and delete program
779 		result = gLink.Send(cmd.Set(kRCX_SelectProgramOp, p));
780 		if (RCX_ERROR(result)) return result;
781 
782 		result = gLink.Send(cmd.MakeDeleteSubs());
783 		if (RCX_ERROR(result)) return result;
784 
785 		result = gLink.Send(cmd.MakeDeleteTasks());
786 		if (RCX_ERROR(result)) return result;
787 	}
788 
789 	result = gLink.Send(cmd.Set(kRCX_SetDatalogOp, 0, 0));
790 	return result;
791 }
792 
793 
DownloadFirmware(const char * filename,bool fast)794 RCX_Result DownloadFirmware(const char *filename, bool fast)
795 {
796 	FILE *fp;
797 	SRecord srec;
798 	bool ok;
799 	RCX_Result result;
800 	ULong rom, ram;
801 
802 	fp = fopen(filename, "rb");
803 	if (!fp)
804 	{
805 		fprintf(STDERR, "Error: could not open file \'%s\'\n", filename);
806 		return kQuietError;
807 	}
808 
809 	ok = srec.Read(fp, kMaxFirmware);
810 	fclose(fp);
811 
812 	if (!ok)
813 	{
814 		fprintf(STDERR, "Error: \'%s\' is not a valid S-Record file\n", filename);
815 		return kQuietError;
816 	}
817 
818 	result = gLink.Open();
819 	if (RCX_ERROR(result)) return result;
820 
821 	fprintf(STDERR, "Downloading firmware:");
822 	fflush(STDERR);
823 	result = gLink.DownloadFirmware(srec.GetData(), srec.GetLength(), srec.GetStart(), fast);
824 	fputc('\n', STDERR);
825 	if (RCX_ERROR(result)) return result;
826 
827 	result = gLink.GetVersion(rom, ram);
828 	if (RCX_ERROR(result)) return result;
829 
830 	fprintf(STDERR, "Current Version: %08lx/%08lx\n", rom, ram);
831 
832 	return result;
833 }
834 
835 
SendRawCommand(const char * text,bool retry)836 RCX_Result SendRawCommand(const char *text, bool retry)
837 {
838 	int length = (int)strlen(text);
839 	RCX_Cmd cmd;
840 	RCX_Result result;
841 
842 	// we need an even number of chars in text
843 	if (length & 1) return kUsageError;
844 
845 	// determine actual length of command
846 	length /= 2;
847 	cmd.SetLength(length);
848 
849 	for(int i=0; i<length; i++)
850 	{
851 		int byte = SRecord::ReadHexByte(text);
852 		if (byte == -1) return kUsageError;
853 		cmd[i] = (UByte) byte;
854 		text+=2;
855 	}
856 
857 	result = gLink.Send(&cmd, retry);
858 
859 	if (result > 0)
860 	{
861 		for(int i=0; i<result; i++)
862 			printf("%02x ", gLink.GetReplyByte(i));
863 		printf("\n");
864 	}
865 
866 	return result;
867 }
868 
869 
SendRemote(const char * event,int repeat)870 RCX_Result SendRemote(const char *event, int repeat)
871 {
872 	RCX_Cmd cmd;
873 	int low, high;
874 
875 	if (repeat == 0) return kUsageError;
876 
877 	if (strlen(event) != 4)
878 		return kUsageError;
879 
880 	high = SRecord::ReadHexByte(event);
881 	low = SRecord::ReadHexByte(event+2);
882 	if (high==-1 || low==-1) return kUsageError;
883 
884 
885 	cmd.Set(kRCX_Remote, low, high);
886 
887 	while(repeat--)
888 	{
889 		gLink.Send(&cmd, false);
890 	}
891 
892 	return kRCX_OK;
893 }
894 
895 
CreateFilename(const char * source,const char * oldExt,const char * newExt)896 char *CreateFilename(const char *source, const char *oldExt, const char *newExt)
897 {
898 	char *filename;
899 	size_t n = strlen(source);
900 	size_t newExtLen = strlen(newExt);
901 
902 	if (CheckExtension(source, oldExt))
903 		n -= strlen(oldExt);
904 
905 	filename = new char[n + newExtLen + 1];
906 	memcpy(filename, source, n);
907 	strcpy(filename + n, newExt);
908 
909 	return filename;
910 }
911 
912 
CheckExtension(const char * s1,const char * ext)913 int CheckExtension(const char *s1, const char *ext)
914 {
915 	size_t slen = strlen(s1);
916 	size_t plen = strlen(ext);
917 	unsigned i;
918 
919 	if (slen < plen) return 0;
920 
921 	for(i=0; i<plen; i++)
922 		if (tolower(s1[slen-plen+i]) != tolower(ext[i])) return 0;
923 
924 	return 1;
925 }
926 
927 
LeafName(const char * filename)928 const char *LeafName(const char *filename)
929 {
930 	const char *ptr;
931 
932 	// start at end and work towards beginning
933 	ptr = filename + strlen(filename) - 1;
934 
935 	// test each character (including first one)
936 	while(ptr >= filename)
937 	{
938 		// check for delimiter
939 		if (*ptr == DIR_DELIMITER) break;
940 
941 #ifdef WIN32
942 		// extra check for ':' in paths for Windows
943 		if (*ptr == ':') break;
944 #endif
945 
946 		--ptr;
947 	}
948 
949 	// we either stopped at the char before the first, or at
950 	// the delimiter - either way return pointer to next char
951 	return ptr+1;
952 }
953 
954 
SetErrorFile(const char * filename)955 RCX_Result SetErrorFile(const char *filename)
956 {
957 	FILE *fp;
958 
959 	if (*filename==0)
960 	{
961 		gErrorStream = stdout;
962 		return kRCX_OK;
963 	}
964 
965 	fp = fopen(filename, "w");
966 	if (!fp)
967 	{
968 		fprintf(STDERR, "Error: could not open error file \'%s\'\n", filename);
969 		return kQuietError;
970 	}
971 
972 	gErrorStream = fp;
973 	return kRCX_OK;
974 }
975 
976 
RedirectOutput(const char * filename)977 RCX_Result RedirectOutput(const char *filename)
978 {
979 	if (*filename==0)
980 	{
981 		fprintf(STDERR, "Error: -R requires a filename\n");
982 		return kQuietError;
983 	}
984 
985 	if (!freopen(filename, "w", stdout))
986 	{
987 		fprintf(STDERR, "Error: could not open output file \'%s\'\n", filename);
988 		return kQuietError;
989 	}
990 
991 	return kRCX_OK;
992 }
993 
994 
DefineMacro(const char * text)995 void DefineMacro(const char *text)
996 {
997 	const char *body;
998 	body = strchr(text, '=');
999 	if (body)
1000 	{
1001 		// create a copy of the symbol name
1002 		int length = body - text;
1003 		char *name = new char[length+1];
1004 		memcpy(name, text, (size_t)length);
1005 		name[length] = 0;
1006 
1007 		Compiler::Get()->Define(name, body+1);
1008 
1009 		delete [] name;
1010 	}
1011 	else
1012 	{
1013 		Compiler::Get()->Define(text);
1014 	}
1015 }
1016 
1017 
GetActionCode(const char * arg)1018 int GetActionCode(const char *arg)
1019 {
1020 	for(int i=0; i< (int)(sizeof(sActionNames)/sizeof(const char *)); ++i)
1021 		if (strcmp(sActionNames[i], arg)==0) return i+kFirstActionCode;
1022 
1023 	return 0;
1024 }
1025 
1026 
PrintError(RCX_Result error,const char * filename)1027 void PrintError(RCX_Result error, const char *filename)
1028 {
1029 	const char *targetName = getTarget(gTargetType)->fName;
1030 	if (!filename) filename = "?";
1031 
1032 	if (error >= 0) return;
1033 
1034 	switch(error)
1035 	{
1036                 case kRCX_RequestError:
1037                         fprintf(STDERR, "Request error\n");
1038                         break;
1039 		case kRCX_OpenSerialError:
1040 			fprintf(STDERR, "Could not open serial port or USB device\n");
1041 			break;
1042 		case kRCX_IREchoError:
1043 			fprintf(STDERR, "Problem talking to IR device\n");
1044 			break;
1045 		case kRCX_ReplyError:
1046 			if (gLink.WasErrorFromMissingFirmware()) {
1047 				fprintf(STDERR, "No firmware installed on %s\n", targetName);
1048 			}
1049 			else {
1050 				fprintf(STDERR, "No reply from %s\n", targetName);
1051 			}
1052 			break;
1053 		case kRCX_MemFullError:
1054 			fprintf(STDERR, "Not enough free memory in %s to download program\n", targetName);
1055 			break;
1056 		case kRCX_FileError:
1057 			fprintf(STDERR, "Could not access file \'%s\'\n", filename);
1058 			break;
1059 		case kRCX_FormatError:
1060 			fprintf(STDERR, "File \'%s\' is not a valid RCX image\n", filename);
1061 			break;
1062 		case kUsageError:
1063 			PrintVersion();
1064 			fprintf(STDERR,"Usage error: try \'nqc -help\' to display options\n");
1065 			break;
1066 		case kQuietError:
1067 			break;
1068 
1069 		case kRCX_PipeModeError:
1070 			fprintf(STDERR,"USB driver does not support -firmfast, CyberMaster, or Spybotics\n");
1071 			break;
1072 
1073 		case kRCX_USBUnsupportedError:
1074 			fprintf(STDERR,"USB Tower not supported\n");
1075 			break;
1076 
1077 		case kRCX_GhostNotFoundError:
1078 			fprintf(STDERR,"Ghost libraries are not installed properly\n");
1079 			break;
1080 		default:
1081 			fprintf(STDERR, "Error #%d\n", -error);
1082 			break;
1083 	}
1084 }
1085 
PrintVersion()1086 void PrintVersion()
1087 {
1088 	fprintf(STDERR,"nqc version %s\n", VERSION_STRING);
1089 	fprintf(STDERR,"     Copyright (C) 2005 John Hansen.  All Rights Reserved.\n");
1090 }
1091 
PrintUsage()1092 void PrintUsage()
1093 {
1094 	PrintVersion();
1095 	fprintf(stdout,"Usage: nqc [options] [actions] [ - | filename ] [actions]\n");
1096 	fprintf(stdout,"   - : read from stdin instead of a source_file\n");
1097 	fprintf(stdout,"Options:\n");
1098 	fprintf(stdout,"   -T<target>: target can be RCX, CM, Scout, RCX2, Spy, or Swan\n");
1099 	fprintf(stdout,"   -d: download program\n");
1100 	fprintf(stdout,"   -n: prevent the system file (rcx.nqh) from being included\n");
1101 	fprintf(stdout,"   -b: treat input file as a binary file (don't compile it)\n");
1102 	fprintf(stdout,"   -D<sym>[=<value>] : define macro <sym>\n");
1103 	fprintf(stdout,"   -E[<filename>] : write compiler errors to <filename> (or stdout)\n");
1104 	fprintf(stdout,"   -R<filename> : redirect text output (datalog, etc) to <filename>\n");
1105 	fprintf(stdout,"   -I<path>: search <path> for include files\n");
1106 	fprintf(stdout,"   -L[<filename>] : generate code listing to <filename> (or stdout)\n");
1107 	fprintf(stdout,"   -s: include source code in listings if possible\n");
1108 	fprintf(stdout,"   -c: generate LASM compatible listings\n");
1109 	fprintf(stdout,"   -v: verbose\n");
1110 	fprintf(stdout,"   -q: quiet\n");
1111 	fprintf(stdout,"   -x: omit packet header (RCX only)\n");
1112 	fprintf(stdout,"   -f<size>: set firmware chunk size\n");
1113 	fprintf(stdout,"   -w<ms>: set the download wait timeout\n");
1114 //	fprintf(stdout,"   -p<size>: set program chunk size\n");
1115 //	fprintf(stdout,"   -X<factor>: set USB tower speed factor (<100=faster, >100=slower)\n");
1116 	fprintf(stdout,"   -O<outfile>: specify output file\n");
1117 	fprintf(stdout,"   -S<portname>: specify serial port\n");
1118 	fprintf(stdout,"   -U<sym>: undefine macro <sym>\n");
1119 	fprintf(stdout,"Actions:\n");
1120 	fprintf(stdout,"   -run: run current program\n");
1121 	fprintf(stdout,"   -pgm <number>: select program number\n");
1122 	fprintf(stdout,"   -datalog | -datalog_full: upload datalog\n");
1123 	fprintf(stdout,"   -near: set IR to near mode\n");
1124 	fprintf(stdout,"   -far: set IR to far mode\n");
1125 	fprintf(stdout,"   -watch <time> | now: set RCX time\n");
1126 	fprintf(stdout,"   -firmware <filename>: download firmware\n");
1127 	fprintf(stdout,"   -firmfast <filename>: download firmware at quad speed\n");
1128 	fprintf(stdout,"   -sleep <timeout>: set RCX sleep timeout\n");
1129 	fprintf(stdout,"   -msg <number>: send IR message to RCX\n");
1130 	fprintf(stdout,"   -raw <data>: format data as a packet and send to RCX\n");
1131 	fprintf(stdout,"   -remote <value> <repeat>: send a remote command to the RCX\n");
1132 	fprintf(stdout,"   -clear: erase all programs and datalog in RCX\n");
1133 	fprintf(stdout,"   -api: dump the standard api file (e.g. rcx.nqh) to stdout\n");
1134 	fprintf(stdout,"   -help: display command line options\n");
1135 }
1136 
1137 
Open()1138 RCX_Result AutoLink::Open()
1139 {
1140 	RCX_Result result;
1141 
1142 	if (!fOpen)
1143 	{
1144 		ULong options = gTimeout & RCX_Link::kRxTimeoutMask;
1145 		if (gVerbose) options |= RCX_Link::kVerboseMode;
1146 
1147 		result = RCX_Link::Open(gTargetType, fSerialPort, options);
1148 		if (RCX_ERROR(result)) return result;
1149 
1150 		fOpen = true;
1151 	}
1152 	return kRCX_OK;
1153 }
1154 
1155 
Close()1156 void AutoLink::Close()
1157 {
1158 	if (fOpen)
1159 	{
1160 		RCX_Link::Close();
1161 		fOpen = false;
1162 	}
1163 }
1164 
1165 
Send(const RCX_Cmd * cmd,bool retry)1166 RCX_Result AutoLink::Send(const RCX_Cmd *cmd, bool retry)
1167 {
1168 	RCX_Result result;
1169 
1170 	result = Open();
1171 	if (RCX_ERROR(result)) return result;
1172 
1173 	if (retry)
1174 	{
1175 		result = Sync();
1176 		if (RCX_ERROR(result)) return result;
1177 	}
1178 
1179 	result = RCX_Link::Send(cmd, retry);
1180 
1181 	return retry ? result : kRCX_OK;
1182 }
1183 
1184 
DownloadProgress(int,int)1185 bool AutoLink::DownloadProgress(int /* soFar */, int /* total */)
1186 {
1187 	fputc('.', STDERR);
1188 	fflush(STDERR);
1189 	return true;
1190 }
1191 
1192 
1193 
AddError(const Error & e,const LexLocation * loc)1194 void MyCompiler::AddError(const Error &e, const LexLocation *loc)
1195 {
1196 	// check the error count
1197 	if (!e.IsWarning())
1198 	{
1199 		int errorCount = ErrorHandler::Get()->GetErrorCount();
1200 		// only print the first few errors
1201 		if (errorCount > kMaxPrintedErrors)
1202 		{
1203 			if (errorCount == kMaxPrintedErrors+1)
1204 				fprintf(gErrorStream, "Too many errors - only first %d reported\n", kMaxPrintedErrors);
1205 			return;
1206 		}
1207 	}
1208 
1209 	char msg[Error::kMaxErrorMsg];
1210 	e.SPrint(msg);
1211 
1212 	const char *errorType = e.IsWarning() ? "Warning" : "Error";
1213 	fprintf(gErrorStream, "# %s: %s\n", errorType, msg);
1214 
1215 	if (loc && loc->fIndex!=kIllegalSrcIndex)
1216 	{
1217 		Buffer *b = Compiler::Get()->GetBuffer(loc->fIndex);
1218 		int lineStart = loc->fOffset;
1219 		int line = b->FindLine(lineStart);
1220 		const char *ptr;
1221 		int i;
1222 
1223 		// print file/line info
1224 		fprintf(gErrorStream, "File \"%s\" ; line %d\n", b->GetName(), line);
1225 
1226 		// print the code line
1227 		fprintf(gErrorStream, "# ");
1228 		for(ptr=b->GetData() + lineStart; *ptr != '\n'; ++ptr)
1229 		{
1230 			putc((*ptr == '\t') ? ' ' : *ptr, gErrorStream);
1231 		}
1232 
1233 		// mark the token
1234 		fprintf(gErrorStream,"\n# ");
1235 		for(i=0; i<(loc->fOffset-lineStart); i++)
1236 			putc(' ', gErrorStream);
1237 		for(i=0; i<loc->fLength; i++)
1238 			putc('^', gErrorStream);
1239 		fprintf(gErrorStream,"\n");
1240 	}
1241 
1242 	fprintf(gErrorStream, "#----------------------------------------------------------\n");
1243 
1244 	// this is a hack for Windows!
1245 	fflush(gErrorStream);
1246 }
1247 
1248 
CreateBuffer(const char * name)1249 Buffer *MyCompiler::CreateBuffer(const char *name)
1250 {
1251 	static char pathname[DirList::kMaxPathname];
1252 
1253 	if (!fDirs.Find(name, pathname))
1254 		return nil;
1255 
1256 	Buffer *buf = new Buffer();
1257 
1258 	if (buf->Create(name, pathname))
1259 		return buf;
1260 	else
1261 	{
1262 		delete buf;
1263 		return 0;
1264 	}
1265 }
1266 
1267 
1268 #ifdef TEST_LEXER
PrintToken(int t,TokenVal v)1269 void PrintToken(int t, TokenVal v)
1270 {
1271 	printf("%3d / ", t);
1272 	if (t < 256)
1273 		printf("%c", t);
1274 	else switch(t)
1275 	{
1276 		case ID:
1277 			printf("%s",v.fSymbol->GetKey());
1278 			break;
1279 		case NUMBER:
1280 			printf("%d", v.fInt);
1281 			break;
1282 		case IF:
1283 			printf("if");
1284 			break;
1285 		case STRING:
1286 			printf("%s", v.fString);
1287 			break;
1288 		default:
1289 			printf("0x%08x", v.fInt);
1290 			break;
1291 	}
1292 
1293 	printf("\n");
1294 }
1295 #endif
1296 
1297 
SameString(const char * s1,const char * s2)1298 bool SameString(const char *s1, const char *s2)
1299 {
1300 	char c1, c2;
1301 
1302 	while(true)
1303 	{
1304 		c1 = *s1++;
1305 		c2 = *s2++;
1306 
1307 		if (c1==0 && c2==0) return true;
1308 
1309 		if (tolower(c1) != tolower(c2)) return false;
1310 	}
1311 }
1312