Exemplo n.º 1
0
void
GFGClass::WriteFunction
	(
	ostream& 			os,
	GFGMemberFunction*	fn,
	const JBoolean		interface
	)
{
	if (interface)
		{
		fn->GetInterface().Print(os);
		os << endl;
		return;
		}

	JString access	= "public";
	if (fn->IsProtected())
		{
		access	= "protected";
		}

	JString s	= GFGGetPrefsManager()->GetFunctionComment(fn->GetFnName(), access);
	s.Print(os);

	fn->GetReturnType().Print(os);
	os << endl;
	
	itsClassName.Print(os);
	os << "::";
	fn->GetFnName().Print(os);
	const JSize count	= fn->GetArgCount();
	if (count == 0)
		{
		os << "()" << endl;
		}
	else
		{
		os << "\n\t(\n";
		for (JIndex i = 1; i <= count; i++)
			{
			JString arg	= fn->GetArg(i);
			os << "\t";
			arg.Print(os);
			if (i != count)
				{
				os << ",";
				}
			os << endl;
			}
		os << "\t)" << endl;
		}
	if (fn->IsConst())
		{
		os << "\tconst" << endl;
		}
	os << "{\n\n}" << endl << endl;
}
void
CBPTPrinter::PrintHeader
	(
	ostream&		output,
	const JIndex	pageIndex
	)
{
	if (itsPrintHeaderFlag)
		{
		const JString dateStr = JGetTimeStamp();

		JString pageStr(pageIndex, 0);
		pageStr.Prepend("Page ");

		itsHeaderName.Print(output);
		output << '\n';

		dateStr.Print(output);
		const JInteger spaceCount =
			GetPageWidth() - dateStr.GetLength() - pageStr.GetLength();
		for (JInteger i=1; i<=spaceCount; i++)
			{
			output << ' ';
			}
		pageStr.Print(output);
		output << "\n\n\n";
		}
}
JBoolean
JConvertToStream
(
    const int		input,
    ifstream*		input2,
    JString*		tempFullName,
    const JBoolean	closeInput
)
{
    JString data;
    if (!JReadAll(input, &data, closeInput))
    {
        return kJFalse;
    }

    if (!(JCreateTempFile(tempFullName)).OK())
    {
        return kJFalse;
    }

    ofstream output(*tempFullName);
    data.Print(output);
    output.close();

    input2->open(*tempFullName);
    return JI2B(input2->good());
}
Exemplo n.º 4
0
void
JUpdateCVSIgnore
	(
	const JCharacter* ignoreFullName
	)
{
	JString path, name;
	JSplitPathAndName(ignoreFullName, &path, &name);
	const JString cvsFile = JCombinePathAndName(path, ".cvsignore");

	if (!JFileExists(cvsFile) && JGetVCSType(path) != kJCVSType)
		{
		return;
		}

	JString cvsData;
	JReadFile(cvsFile, &cvsData);
	if (!cvsData.IsEmpty() && !cvsData.EndsWith("\n"))
		{
		cvsData += "\n";
		}

	name += "\n";
	if (!cvsData.Contains(name))
		{
		JEditVCS(cvsFile);
		cvsData += name;

		ofstream output(cvsFile);
		cvsData.Print(output);
		}
}
void
GMMIMEParser::WriteAttachment
	(
	const JString&		data,
	const GMIMEHeader&	header
	)
{
	JString filename = header.GetFileName();
	if (filename.IsEmpty())
		{
		const JError err = JCreateTempFile(itsAttachDir, NULL, &filename);
		if (!err.OK())
			{
			err.ReportIfError();
			return;
			}
		}
	else
		{
		filename = JCombinePathAndName(itsAttachDir, filename);
		}

	AdjustAttachmentName(header, &filename);
	std::ofstream os(filename);
	if (header.GetEncoding() == kBase64Encoding)
		{
		std::istrstream is(data.GetCString(), data.GetLength());
		JDecodeBase64(is, os);
		}
	else
		{
		data.Print(os);
		}
}
void
GenerateCode
	(
	istream&			input,
	ostream&			output,
	const JString&		stringPath,
	const JString&		formName,
	const JString&		tagName,
	const JString&		userTopEnclVarName,
	JPtrArray<JString>*	objTypes,
	JPtrArray<JString>*	objNames
	)
{
JIndex i;

	// width

	input >> ws;
	JString line = JReadUntilws(input);
	assert( line == kFormWidthMarker );
	JSize formWidth;
	input >> formWidth;

	// height

	input >> ws;
	line = JReadUntilws(input);
	assert( line == kFormHeightMarker );
	JSize formHeight;
	input >> formHeight;

	// object count (marker contains whitespace)

	input >> ws;
	line = JReadUntil(input, ':') + ":";
	assert( line == kFormObjCountMarker );
	JSize itemCount;
	input >> itemCount;

	// create window

	const JString topEnclFrameName = tagName + "_Frame";
	const JString topEnclApName    = tagName + "_Aperture";

	JString topEnclVarName;
	if (tagName == kDefaultDelimTag)
		{
		topEnclVarName = kDefTopEnclVarName;

		output << "    JXWindow* window = new JXWindow(this, ";
		output << formWidth << ',' << formHeight;
		output << ", \"\");" << endl;
		output << "    assert( window != NULL );" << endl;
		output << endl;
		}
	else
		{
		assert( !userTopEnclVarName.IsEmpty() );
		topEnclVarName = userTopEnclVarName;

		output << "    const JRect ";
		topEnclFrameName.Print(output);
		output << "    = ";
		topEnclVarName.Print(output);
		output << "->GetFrame();" << endl;

		output << "    const JRect ";
		topEnclApName.Print(output);
		output << " = ";
		topEnclVarName.Print(output);
		output << "->GetAperture();" << endl;

		output << "    ";
		topEnclVarName.Print(output);
		output << "->AdjustSize(" << formWidth << " - ";
		topEnclApName.Print(output);
		output << ".width(), " << formHeight << " - ";
		topEnclApName.Print(output);
		output << ".height());" << endl;

		output << endl;
		}

	// We need to calculate the enclosure for each object.  Since objects
	// are drawn in the order added, an object must come after its enclosure
	// in the list in order to be visible.

	JArray<JRect>    rectList(10);
	JArray<JBoolean> isInstanceVar(10);

	// This array is used to send the options to ApplyOptions.
	// It does not own the pointers that it contains.

	JPtrArray<JString> optionValues(JPtrArrayT::kForgetAll, kOptionCount);
	for (i=1; i<=kOptionCount; i++)
		{
		optionValues.Append(NULL);
		}

	// generate code for each object

	JStringManager stringMgr;

	JIndex objCount = 1;
	for (i=1; i<=itemCount; i++)
		{
		// check for start-of-object

		input >> ws;
		line = JReadLine(input);
		assert( line == kBeginObjLine );

		// object class

		JString flClass = JReadLine(input);
		RemoveIdentifier(kObjClassMarker, &flClass);

		// object type

		JString flType = JReadLine(input);
		RemoveIdentifier(kObjTypeMarker, &flType);

		// object frame

		input >> ws;
		line = JReadUntilws(input);
		assert( line == kObjRectMarker );
		JCoordinate x,y,w,h;
		input >> x >> y >> w >> h >> ws;
		const JRect frame(y, x, y+h, x+w);
		rectList.AppendElement(frame);

		// box type

		JString boxType = JReadLine(input);
		RemoveIdentifier(kObjBoxTypeMarker, &boxType);

		// colors

		input >> ws;
		line = JReadUntilws(input);
		assert( line == kObjColorsMarker );
		JString col1 = JReadUntilws(input);
		optionValues.SetElement(kCol1Index, &col1, JPtrArrayT::kForget);
		JString col2 = JReadUntilws(input);
		optionValues.SetElement(kCol2Index, &col2, JPtrArrayT::kForget);

		// label info

		JString lAlign = JReadLine(input);
		RemoveIdentifier(kObjLAlignMarker, &lAlign);
		JString lStyle = JReadLine(input);
		RemoveIdentifier(kObjLStyleMarker, &lStyle);
		JString lSize  = JReadLine(input);
		RemoveIdentifier(kObjLSizeMarker, &lSize);
		JString lColor = JReadLine(input);
		RemoveIdentifier(kObjLColorMarker, &lColor);
		JString label  = JReadLine(input);
		RemoveIdentifier(kObjLabelMarker, &label);

		// shortcuts

		JString shortcuts = JReadLine(input);
		RemoveIdentifier(kObjShortcutMarker, &shortcuts);
		optionValues.SetElement(kShortcutsIndex, &shortcuts, JPtrArrayT::kForget);

		// resizing (ignored)

		JIgnoreLine(input);

		// gravity

		input >> ws;
		line = JReadUntilws(input);
		assert( line == kObjGravityMarker );
		const JString nwGravity = JReadUntilws(input);
		const JString seGravity = JReadUntilws(input);

		// variable name

		JBoolean isLocal = kJFalse;
		JString* varName = new JString(JReadLine(input));
		assert( varName != NULL );
		RemoveIdentifier(kObjNameMarker, varName);
		if (varName->IsEmpty())
			{
			isInstanceVar.AppendElement(kJFalse);
			GetTempVarName(tagName, varName, *objNames);
			isLocal = kJTrue;
			}
		else if (varName->GetFirstCharacter() == '(' &&
				 varName->GetLastCharacter()  == ')')
			{
			isInstanceVar.AppendElement(kJFalse);
			isLocal  = kJTrue;
			*varName = varName->GetSubstring(2, varName->GetLength()-1);
			}
		else if (varName->GetFirstCharacter() == '<' &&
				 varName->GetLastCharacter()  == '>')
			{
			isInstanceVar.AppendElement(kJFalse);
			*varName = varName->GetSubstring(2, varName->GetLength()-1);
			}
		else
			{
			isInstanceVar.AppendElement(kJTrue);
			}
		objNames->Append(varName);

		// callback (ignored)

		JIgnoreLine(input);

		// callback argument

		JString cbArg = JReadLine(input);
		RemoveIdentifier(kObjCBArgMarker, &cbArg);

		JString cbArgExtra;
		do
			{
			cbArgExtra = JReadLine(input);
			cbArgExtra.TrimWhitespace();
			}
			while (!cbArgExtra.IsEmpty());

		// don't bother to generate code for initial box
		// if it is FL_BOX, FLAT_BOX, FL_COL1

		if (i==1 && flClass == "FL_BOX" && flType == "FLAT_BOX" && col1 == "FL_COL1")
			{
			rectList.RemoveElement(objCount);
			isInstanceVar.RemoveElement(objCount);
			objNames->DeleteElement(objCount);
			continue;
			}

		// check for errors -- safe since we have read in entire object

		JString hSizing, vSizing;
		if (!ParseGravity(nwGravity, &hSizing, &vSizing))
			{
			cerr << "Illegal sizing specification ";
			cerr << nwGravity << ',' << seGravity;
			cerr << " for '" << *varName << '\'' << endl;
			rectList.RemoveElement(objCount);
			isInstanceVar.RemoveElement(objCount);
			objNames->DeleteElement(objCount);
			continue;
			}

		if (*varName == topEnclVarName)
			{
			cerr << "Cannot use reserved name '" << topEnclVarName << '\'' << endl;
			rectList.RemoveElement(objCount);
			isInstanceVar.RemoveElement(objCount);
			objNames->DeleteElement(objCount);
			continue;
			}

		// get the object's enclosure

		JIndex enclIndex;
		JString enclName;
		JRect localFrame = frame;
		if (GetEnclosure(rectList, objCount, &enclIndex))
			{
			enclName = *(objNames->NthElement(enclIndex));
			const JRect enclFrame = rectList.GetElement(enclIndex);
			localFrame.Shift(-enclFrame.topLeft());
			}
		else
			{
			enclName = topEnclVarName;
			}

		// get the class name and additional arguments

		JString* className = new JString;
		assert( className != NULL );
		objTypes->Append(className);

		JString argList;
		if (!GetConstructor(flClass, flType, &label, className, &argList))
			{
			cerr << "Unsupported class: " << flClass << ", " << flType << endl;
			rectList.RemoveElement(objCount);
			isInstanceVar.RemoveElement(objCount);
			objNames->DeleteElement(objCount);
			objTypes->DeleteElement(objCount);
			continue;
			}

		// generate the actual code

		const JBoolean needCreate = NeedsCreateFunction(*className);

		output << "    ";
		if (isLocal)
			{
			className->Print(output);
			output << "* ";
			}
		varName->Print(output);
		output << " =" << endl;
		output << "        ";
		if (!needCreate)
			{
			output << "new ";
			}
		className->Print(output);
		if (needCreate)
			{
			output << "::Create";
			}
		output << '(';
		if (!argList.IsEmpty())
			{
			argList.Print(output);
			if (argList.GetLastCharacter() != ',')
				{
				output << ',';
				}
			output << ' ';
			}

		if (!cbArg.IsEmpty())
			{
			cbArg.Print(output);
			if (cbArg.GetLastCharacter() != ',')
				{
				output << ',';
				}
			output << ' ';
			}

		if ((*className == "JXStaticText" && cbArg.IsEmpty()) ||
			NeedsStringArg(*className))
			{
			JString id = *varName;
			id += "::";
			id += formName;
			id += "::";
			id += tagName;		// last since it is almost always the same

			output << "JGetString(\"";
			id.Print(output);
			output << "\"), ";

			stringMgr.SetElement(id, label, JPtrArrayT::kDelete);
			}

		enclName.Print(output);
		output << ',' << endl;
		output << "                    JXWidget::";
		hSizing.Print(output);
		output << ", JXWidget::";
		vSizing.Print(output);
		output << ", " << localFrame.left << ',' << localFrame.top << ", ";
		output << localFrame.width() << ',' << localFrame.height() << ");" << endl;

		output << "    assert( ";
		varName->Print(output);
		output << " != NULL );" << endl;

		ApplyOptions(output, *className, formName, tagName, *varName, optionValues,
					 lSize, lStyle, lColor, &stringMgr);

		if (*className == "JXStaticText" && cbArg.IsEmpty() &&
			!lAlign.Contains("FL_ALIGN_TOP") && localFrame.height() <= 20)
			{
			output << "    ";
			varName->Print(output);
			output << "->SetToLabel();" << endl;
			}

		output << endl;

		// now we know the object is valid

		objCount++;
		}

	// write string database

	JString dbFileName = stringPath + formName;
	if (tagName != kDefaultDelimTag)
		{
		dbFileName += kCustomTagMarker + tagName;
		}
	dbFileName += "_layout";

	if (stringMgr.GetElementCount() > 0)
		{
		JEditVCS(dbFileName);
		ofstream dbOutput(dbFileName);
		stringMgr.WriteFile(dbOutput);
		}
	else
		{
		JRemoveVCS(dbFileName);
		JRemoveFile(dbFileName);
		}

	// reset enclosure size

	if (tagName != kDefaultDelimTag)
		{
		output << "    ";
		topEnclVarName.Print(output);
		output << "->SetSize(";
		topEnclFrameName.Print(output);
		output << ".width(), ";
		topEnclFrameName.Print(output);
		output << ".height());" << endl;
		output << endl;
		}

	// throw away temporary variables

	objCount--;
	assert( objCount == isInstanceVar.GetElementCount() );
	assert( objCount == objTypes->GetElementCount() );
	assert( objCount == objNames->GetElementCount() );
	for (i=objCount; i>=1; i--)
		{
		if (!isInstanceVar.GetElement(i))
			{
			objTypes->DeleteElement(i);
			objNames->DeleteElement(i);
			}
		}
}
void
ApplyOptions
	(
	ostream&					output,
	const JString&				className,
	const JString&				formName,
	const JString&				tagName,
	const JString&				varName,
	const JPtrArray<JString>&	values,
	const JString&				flSize,
	const JString&				flStyle,
	const JString&				flColor,
	JStringManager*				stringMgr
	)
{
	ifstream optionMap(optionMapFile);
	optionMap >> ws;
	while (1)
		{
		if (optionMap.peek() == '#')
			{
			JIgnoreLine(optionMap);
			}
		else
			{
			const JString aClassName = JReadUntilws(optionMap);
			if (optionMap.eof() || optionMap.fail())
				{
				break;
				}
			else if (aClassName != className)
				{
				for (JIndex i=1; i<=kOptionCount; i++)
					{
					JIgnoreLine(optionMap);
					}
				}
			else
				{
				JIndex i;
				JBoolean supported;

				// shortcuts

				optionMap >> ws >> supported;
				if (supported)
					{
					optionMap >> ws;
					const JString function = JReadUntilws(optionMap);
					const JString* value   = values.NthElement(kShortcutsIndex);
					if (!value->IsEmpty())
						{
						JString id = varName;
						id += "::";
						id += formName;
						id += "::shortcuts::";
						id += tagName;		// last since it is almost always the same

						output << "    ";
						varName.Print(output);
						output << "->";
						function.Print(output);
						output << "(JGetString(\"";
						id.Print(output);
						output << "\"));" << endl;

						JString* s = new JString(*value);
						assert( s != NULL );
						stringMgr->SetElement(id, s, JPtrArrayT::kDelete);
						}
					}
				else
					{
					JIgnoreLine(optionMap);
					}

				// colors

				for (i=2; i<=kOptionCount; i++)
					{
					optionMap >> ws >> supported;
					if (supported)
						{
						optionMap >> ws;
						const JString defValue = JReadUntilws(optionMap);
						const JString function = JReadUntilws(optionMap);
						const JString* value   = values.NthElement(i);
						if (*value != defValue)
							{
							JString jxColor;
							if (ConvertXFormsColor(*value, &jxColor))
								{
								output << "    ";
								varName.Print(output);
								output << "->";
								function.Print(output);
								output << '(';
								jxColor.Print(output);
								output << ");" << endl;
								}
							else
								{
								cerr << "Unknown color: " << *value << endl;
								}
							}
						}
					else
						{
						JIgnoreLine(optionMap);
						}
					}
				}
			}
int
main
	(
	int		argc,
	char*	argv[]
	)
{
	// parse the command line options

	JPtrArray<JString> inputFileList(JPtrArrayT::kDeleteAll);
	JString dataVarName, outputFileName, databaseFileName;
	JBoolean debug;
	GetOptions(argc, argv, &inputFileList,
			   &dataVarName, &outputFileName, &databaseFileName, &debug);

	const JSize inputCount = inputFileList.GetElementCount();

	// check mod times of input files
/*
	This doesn't work because compiling different versions of the program
	requires different sets of string files, none of which may have been
	modified in a long time.  The output file still needs to be re-built,
	however!

	time_t outputTime;
	if ((JGetModificationTime(outputFileName, &outputTime)).OK())
		{
		JBoolean changed = kJFalse;

		for (JIndex i=1; i<=inputCount; i++)
			{
			const JString* inputFileName = inputFileList.NthElement(i);
			time_t t;
			if (!(JGetModificationTime(*inputFileName, &t)).OK())
				{
				cerr << argv[0] << ":  " << *inputFileName << " does not exist" << endl;
				return 1;
				}
			else if (t >= outputTime)
				{
				changed = kJTrue;
				break;
				}
			}

		if (!changed)
			{
			return 0;
			}
		}
*/
	// process the input files

	JStringManager mgr;

	for (JIndex i=1; i<=inputCount; i++)
		{
		const JString* inputFileName = inputFileList.NthElement(i);
		if (JDirectoryExists(*inputFileName))
			{
			continue;
			}

		ifstream input(*inputFileName);
		if (!input.good())
			{
			cerr << argv[0] << ":  unable to open " << *inputFileName << endl;
			return 1;
			}
		mgr.MergeFile(input, debug);
		if (input.fail())
			{
			cerr << argv[0] << ":  error while reading " << *inputFileName << endl;
			return 1;
			}
		}

	// generate the output file

	std::ostringstream data1;
	mgr.WriteFile(data1);

	JString data1Str = data1.str();
	if (!databaseFileName.IsEmpty())
		{
		ofstream dbOutput(databaseFileName);
		data1Str.Print(dbOutput);
		}

	if (!outputFileName.IsEmpty())
		{
		JIndex i = 1;
		while (data1Str.LocateNextSubstring("\\", &i))
			{
			data1Str.ReplaceSubstring(i,i, "\\\\");
			i += 2;
			}
		i = 1;
		while (data1Str.LocateNextSubstring("\"", &i))
			{
			data1Str.ReplaceSubstring(i,i, "\\\"");
			i += 2;
			}
		i = 1;
		while (data1Str.LocateNextSubstring("\n", &i))
			{
			data1Str.ReplaceSubstring(i,i, "\\n");
			i += 2;
			}

		std::ostringstream data2;
		data2 << "#include <jTypes.h>" << endl;
		data2 << "static const JCharacter* ";
		dataVarName.Print(data2);
		data2 << "[] = {" << endl;

		// Visual C++ cannot handle file with more than 2048 characters on a line
		// and cannot compile string constant more than 2048 characters!

		const JSize l1 = data1Str.GetLength();
		for (i=0; i<l1; )
			{
			JSize l2 = JMin((JSize) 2040, l1 - i);
			while (l2 > 0 && data1Str.GetCharacter(i+l2) == '\\')
				{
				l2--;
				}
			assert( l2 > 0 );

			data2 << "\"";
			data2.write(((const char*) data1Str) + i, l2);
			data2 << "\"," << endl;

			i += l2;
			}

		data2 << "NULL };" << endl;

		// if the file won't change, don't re-write it

		const JString s2 = data2.str();
		if (JFileExists(outputFileName))
			{
			JString origData;
			JReadFile(outputFileName, &origData);
			if (origData == s2)
				{
				JUpdateCVSIgnore(outputFileName);
				return 0;
				}
			}

		// write file

		ofstream output(outputFileName);
		s2.Print(output);

		if (!output.good())
			{
			cerr << argv[0] << ":  unable to write to " << outputFileName << endl;
			return 1;
			}

		JUpdateCVSIgnore(outputFileName);
		}

	return 0;
}
void
GMMIMEParser::ParseMixed
	(
	std::istream&			input,
	const GMIMEHeader&	header
	)
{
	JString boundary	= "-" + header.GetBoundary();
	JString endBoundary = boundary + "--";

	// slurp the initial empty part
	JIndex bstart, bend;
	ReadUntilBoundary(input, boundary, &bstart, &bend);
	JIndex current = JTellg(input);
	JString data;
	data.Read(input, bend - current + 1);

	while (1)
		{
		GMIMEHeader child;
		ParseMIMEHeader(input, &child);

		// when the following function returns true, it has found the
		// last boundary
		data.Clear();
		JBoolean end;
		if (child.GetEncoding() == kBase64Encoding)
			{
			JString filename = child.GetFileName();
			if (filename.IsEmpty())
				{
				JCreateTempFile(itsAttachDir, NULL, &filename);
				}
			else
				{
				filename = JCombinePathAndName(itsAttachDir, filename);
				}
			if (!filename.IsEmpty())
				{
				AdjustAttachmentName(child, &filename);
				std::ofstream os(filename);
				JDecodeBase64(input, os);
				end = ReadUntilBoundary(input, boundary, &bstart, &bend);
	//			JBoolean found;
	//			JString line;
	//			while (line.IsEmpty() && !input.fail())
	//				{
	//				line = JReadLine(input, &found);
	//				}
	//			JString endBoundary = boundary + "--";
	//			if (line.Contains(endBoundary))
	//				{
	//				end = kJTrue;
	//				}
				}
			}
		else if ((child.GetType() != kMultipartType) &&
				 (!child.GetFileName().IsEmpty()))
			{
			JIndex startI = JTellg(input);
			JIndex findex = startI;
			if (itsData->LocateNextSubstring(boundary, &findex))
				{
				const JCharacter* c = itsData->GetCString() + startI;
				JString filename	= child.GetFileName();
				if (filename.IsEmpty())
					{
					JCreateTempFile(itsAttachDir, NULL, &filename);
					}
				else
					{
					filename = JCombinePathAndName(itsAttachDir, filename);
					}
				if (!filename.IsEmpty())
					{
					AdjustAttachmentName(child, &filename);
					std::ofstream os(filename);
					JSeekg(input, findex - 1);
					if (child.GetEncoding() == kQPEncoding)
						{
						JString data	= itsData->GetSubstring(startI, findex - 3);
						ParseQuotedPrintable(&data);
						data.Print(os);
						}
					else
						{
						os.write(c, findex - startI - 3);
						}				
					JBoolean found;
					JString line = JReadLine(input, &found);
					if (line.BeginsWith(endBoundary))
						{
						end = kJTrue;
						}
					}
				}
			}
		else
			{
			end = ReadUntilBoundary(input, boundary, &bstart, &bend);
			ParseByType(input, child, bstart - 1);

			current	= JTellg(input);
			data.Read(input, bend - current + 1);
			}
		if (end)
			{
			break;
			}
		}

	if (itsTextInfo != NULL)
		{
		itsTextInfo->ForceUpdate();
		}
	if (itsAttachInfo != NULL)
		{
		itsAttachInfo->ForceUpdate();
		}
}
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;
}