JBoolean
CBApp::FindFile
	(
	const JCharacter*	fileName,
	const JBoolean		caseSensitive,
	JString*			fullName
	)
	const
{
	JBoolean cancelled = kJFalse;

	const JBoolean relative = JIsRelativePath(fileName);
	if (!relative && JFileExists(fileName))
		{
		*fullName = fileName;
		return kJTrue;
		}
	else if (relative)
		{
		CBDirInfoList searchPaths;
		CollectSearchPaths(&searchPaths);
		const JSize dirCount = searchPaths.GetElementCount(),
					sysCount = itsSystemIncludeDirs->GetElementCount();

		JBoolean found = kJFalse;

		JLatentPG pg;
		JString msg = "Searching for \"";
		msg += fileName;
		msg += "\"...";
		pg.FixedLengthProcessBeginning(dirCount+sysCount, msg, kJTrue, kJFalse);

		JString path, newName;
		for (JIndex i=1; i<=dirCount; i++)
			{
			const CBDirInfo info = searchPaths.GetElement(i);
			if (!info.recurse)
				{
				*fullName = JCombinePathAndName(*(info.path), fileName);
				if (JFileExists(*fullName))
					{
					found = kJTrue;
					break;
					}
				}
			else if (JSearchSubdirs(*(info.path), fileName, kJTrue, caseSensitive,
									&path, &newName, NULL, &cancelled))
				{
				*fullName = JCombinePathAndName(path, newName);
				found     = kJTrue;
				break;
				}
			else if (cancelled)
				{
				break;
				}

			if (!pg.IncrementProgress())
				{
				cancelled = kJTrue;
				break;
				}
			}

		if (!found && !cancelled)
			{
			// We have to search system paths last because these are always
			// last on the compiler search path.

			for (JIndex i=1; i<=sysCount; i++)
				{
				if (JSearchSubdirs(*itsSystemIncludeDirs->GetElement(i), fileName, kJTrue, caseSensitive,
								   &path, &newName, NULL, &cancelled))
					{
					*fullName = JCombinePathAndName(path, newName);
					found     = kJTrue;
					break;
					}
				else if (cancelled)
					{
					break;
					}

				if (!pg.IncrementProgress())
					{
					cancelled = kJTrue;
					break;
					}
				}
			}

		pg.ProcessFinished();
		searchPaths.DeleteAll();
		if (found)
			{
			return kJTrue;
			}
		}

	if (!cancelled)
		{
		JString msg = "Unable to find the file \"";
		msg += fileName;
		msg += "\".";
		(JGetUserNotification())->ReportError(msg);
		}

	fullName->Clear();
	return kJFalse;
}
JBoolean
JPTPrinter::Print
	(
	const JCharacter*	text,
	ostream&			trueOutput
	)
{
	ostream* dataOutput  = &trueOutput;
	ofstream* tempOutput = NULL;
	JString tempName;
	if (itsPrintReverseOrderFlag)
		{
		if (!(JCreateTempFile(&tempName)).OK())
			{
			return kJFalse;
			}

		tempOutput = new ofstream(tempName);
		assert( tempOutput != NULL );
		if (tempOutput->bad())
			{
			delete tempOutput;
			JRemoveFile(tempName);
			return kJFalse;
			}
		dataOutput = tempOutput;
		}

	const JSize headerLineCount = GetHeaderLineCount();
	const JSize footerLineCount = GetFooterLineCount();
	assert( itsPageHeight > headerLineCount + footerLineCount );

	const JSize lineCountPerPage = itsPageHeight - headerLineCount - footerLineCount;

	JLatentPG pg;
	pg.VariableLengthProcessBeginning("Printing page...", kJTrue, kJFalse);
	JBoolean keepGoing = kJTrue;

	JUnsignedOffset i   = 0;
	JIndex pageIndex    = 0;
	JSize printCount    = 0;
	JSize textLineCount = 0;
	while (keepGoing && text[i] != '\0')
		{
		pageIndex++;
		const JBoolean shouldPrintPage =
			JI2B(itsFirstPageIndex <= pageIndex &&
				 (itsLastPageIndex == 0 || pageIndex <= itsLastPageIndex));

		std::ostringstream bitBucket;
		ostream* output = shouldPrintPage ? dataOutput : (&bitBucket);

		if (shouldPrintPage)
			{
			printCount++;
			if (printCount > 1)
				{
				*output << kPageSeparatorStr;
				}
			}

		if (headerLineCount > 0)
			{
			PrintHeader(*output, pageIndex);
			}

		JSize lineNumberWidth = 0;
		if (itsPrintLineNumberFlag)
			{
			const JString lastLineIndexStr(pageIndex * lineCountPerPage, 0);
			lineNumberWidth = lastLineIndexStr.GetLength();
			}

		JSize lineCount = 0;
		while (lineCount < lineCountPerPage && text[i] != '\0')
			{
			JSize col = 0;

			if (itsPrintLineNumberFlag)
				{
				const JString lineNumberStr(textLineCount+1, 0);
				const JSize spaceCount = lineNumberWidth - lineNumberStr.GetLength();
				for (JIndex j=1; j<=spaceCount; j++)
					{
					*output << ' ';
					}
				lineNumberStr.Print(*output);
				*output << kLineNumberMarginStr;

				col += lineNumberWidth + kLineNumberMarginWidth;
				}

			if (col >= itsPageWidth)	// insures progress, even in ludicrous boundary case
				{
				col = itsPageWidth - 1;
				}

			while (col < itsPageWidth && text[i] != '\n' && text[i] != '\0')
				{
				if (text[i] == '\t')
					{
					const JSize spaceCount = itsTabWidth - (col % itsTabWidth);
					for (JIndex j=1; j<=spaceCount; j++)
						{
						*output << ' ';
						}
					col += spaceCount;
					}
				else if (text[i] == kJFormFeedKey)
					{
					*output << ' ';
					col++;
					}
				else
					{
					*output << text[i];
					col++;
					}
				i++;
				}

			*output << '\n';
			if (text[i] == '\n')
				{
				i++;
				}

			lineCount++;
			textLineCount++;
			}

		if (footerLineCount > 0)
			{
			while (lineCount < lineCountPerPage)
				{
				*output << '\n';
				lineCount++;
				}

			PrintFooter(*output, pageIndex);
			}

		keepGoing = pg.IncrementProgress();
		}

	pg.ProcessFinished();

	if (itsPrintReverseOrderFlag)
		{
		delete tempOutput;
		if (keepGoing)
			{
			JString text;
			JReadFile(tempName, &text);
			InvertPageOrder(text, trueOutput);
			}
		JRemoveFile(tempName);
		}

	return keepGoing;
}