void
GMMIMEParser::ParseContentParameters
	(
	const JString&		text,
	JPtrArray<JString>*	strings
	)
{
	JString tmp = text;
	JString* str;
	JIndex findex;
	while (tmp.LocateSubstring("=", &findex) && findex > 1)
		{
		str = new JString(tmp.GetSubstring(1, findex - 1));
		assert(str != NULL);
		str->TrimWhitespace();
		str->ToLower();
		strings->Append(str);

		tmp.RemoveSubstring(1, findex);
		tmp.TrimWhitespace();

		// now we need to get the corresponding value.
		// we need to check for quotes first.
		JIndex index = 1;
		if (tmp.GetLength() > 1 && tmp.GetCharacter(1) == '\"')
			{
			FindStringEnd(tmp, &index);
			str = new JString(tmp.GetSubstring(2, index-1));
			assert(str != NULL);
			if (tmp.LocateSubstring(";", &findex))
				{
				tmp.RemoveSubstring(1, findex);
				}
			}
		else if (tmp.LocateSubstring(";", &index))
			{
			str = new JString();
			assert(str != NULL);
			if (index > 1)
				{
				*str = tmp.GetSubstring(1, index-1);
				}
			tmp.RemoveSubstring(1, index);
			}
		else
			{
			str = new JString(tmp);
			assert(str != NULL);
			}

		str->TrimWhitespace();
		strings->Append(str);

		tmp.TrimWhitespace();
		}
}
JString
JVMVarNode::GetFullName
	(
	JBoolean* isPointer
	)
	const
{
	JString str;
	if (IsRoot())
		{
		return str;
		}

	const JVMVarNode* parent = dynamic_cast<const JVMVarNode*>(GetVarParent());
	const JString& name      = GetName();
	if (parent->IsRoot())
		{
		str = "(" + name + ")";
		}
	else if (name.IsEmpty())
		{
		JIndex i;
		const JBoolean found = parent->FindChild(this, &i);
		assert( found );
		str = parent->GetFullName(isPointer);
		if (!str.BeginsWith("(") || !str.EndsWith(")"))
			{
			str.PrependCharacter('(');
			str.AppendCharacter(')');
			}
		str += "[" + JString(i-1, JString::kBase10) + "]";
		}
	else if (name.BeginsWith("<"))
		{
		if (isPointer != NULL)
			{
			*isPointer = parent->IsPointer();
			}
		str = parent->GetFullName(isPointer);
		}
	else if (name.BeginsWith("["))
		{
		str = parent->GetFullName(isPointer) + name;
		}
	else if (name.BeginsWith("*"))
		{
		str = parent->GetPath() + "(" + name + ")";
		}
	else
		{
		str = name;
		if (str.BeginsWith("static "))
			{
			str.RemoveSubstring(1,7);
			}
		str.Prepend(parent->GetPath());
		}

	return str;
}
void
SyGViewManPageDialog::SetFunction
	(
	const JCharacter* historyStr
	)
{
	JString fnName = historyStr;

	JCharacter manIndex[] = { '\0', '\0' };
	if (fnName.GetLastCharacter() == ')')
		{
		manIndex[0] = fnName.GetCharacter(fnName.GetLength()-1);
		fnName.RemoveSubstring(fnName.GetLength()-3, fnName.GetLength());
		}

	if (manIndex[0] == '*')
		{
		manIndex[0] = '\0';
		itsAproposCheckbox->SetState(kJTrue);
		}
	else
		{
		itsAproposCheckbox->SetState(kJFalse);
		}

	itsFnName->SetText(fnName);
	itsManIndex->SetText(manIndex);
}
JBoolean
JDirInfo::BuildRegexFromWildcardFilter
	(
	const JCharacter*	origFilterStr,
	JString*			regexStr
	)
{
	regexStr->Clear();

	JString filterStr = origFilterStr;
	filterStr.TrimWhitespace();

	if (filterStr.IsEmpty())
		{
		return kJFalse;
		}

	JIndex index;
	while (filterStr.LocateSubstring(" ", &index))
		{
		assert( index > 1 );
		const JString str = filterStr.GetSubstring(1, index-1);

		AppendRegex(str, regexStr);

		filterStr.RemoveSubstring(1, index);
		filterStr.TrimWhitespace();
		}

	assert( !filterStr.IsEmpty() );
	AppendRegex(filterStr, regexStr);
	return kJTrue;
}
void
CBExecOutputDocument::ReceiveRecord()
{
	assert( itsRecordLink != NULL );

	JString text;
	const JBoolean ok = itsRecordLink->GetNextMessage(&text);
	assert( ok );

	// remove text that has already been printed

	if (!itsLastPrompt.IsEmpty() && text.BeginsWith(itsLastPrompt))
		{
		text.RemoveSubstring(1, itsLastPrompt.GetLength());
		}
	itsLastPrompt.Clear();

	const JXTEBase::DisplayState state = GetTextEditor()->SaveDisplayState();

	AppendText(text);
	GetTextEditor()->ClearUndo();

	if (!itsReceivedDataFlag)
		{
		itsReceivedDataFlag = kJTrue;
		if (!IsActive())
			{
			Activate();
			}
		}

	GetTextEditor()->RestoreDisplayState(state);
}
Esempio n. 6
0
void
XDLink::SwitchToFrame
	(
	const JUInt64 id
	)
{
	if (id != itsStackFrameIndex)
		{
		itsStackFrameIndex = id;
		Broadcast(FrameChanged());
		}

	const CMStackFrameNode* frame;
	JString fileName;
	JIndex lineIndex;
	if (CMGetCommandDirector()->GetStackDir()->GetStackWidget()->GetStackFrame(id, &frame) &&
		frame->GetFile(&fileName, &lineIndex))
		{
		if (fileName.BeginsWith("file://"))
			{
			fileName.RemoveSubstring(1, 7);
			}
		Broadcast(ProgramStopped(CMLocation(fileName, lineIndex)));
		}
}
void
GMMIMEParser::ParseContentDisposition
	(
	const JString&	val,
	GMIMEHeader*	header
	)
{
	// we first need to strip the type. this will be found before the
	// first ';' which will be followed by the parameters.
	JString tVal	= val;
	tVal.TrimWhitespace();
	JSize length	= tVal.GetLength();

	JIndex findex;
	if (tVal.LocateSubstring(";", &findex) && (findex > 1))
		{
		tVal.RemoveSubstring(1, findex);
		tVal.TrimWhitespace();
		}
	else
		{
		return;
		}

	JPtrArray<JString> strings(JPtrArrayT::kDeleteAll);
	ParseContentParameters(tVal, &strings);
	JSize count = strings.GetElementCount();
	for (JIndex i = 1; i <= count; i += 2)
		{
		JString* str = strings.NthElement(i);
		if ((*str == "filename") &&
			(strings.IndexValid(i+1)))
			{
			str	= strings.NthElement(i+1);
			if (str->LocateLastSubstring("/", &findex))
				{
				str->RemoveSubstring(1, findex);
				}
			if (str->LocateLastSubstring("\\", &findex))
				{
				str->RemoveSubstring(1, findex);
				}
			header->SetFileName(*str);
			}
		}
}
JString
JConvertToTimeStamp
	(
	const time_t t
	)
{
	JString timeStamp  = ctime(&t);
	const JSize length = timeStamp.GetLength();
	timeStamp.RemoveSubstring(length, length);
	return timeStamp;
}
JBoolean
GAddressBookMgr::GetNextRecord
	(
	JString& line,
	JString& record,
	std::istream& is
	)
{
	if (line.IsEmpty())
		{
		return kJFalse;
		}
	JIndex index;
	if (line.LocateSubstring("\t", &index))
		{
		if (index > 1)
			{
			record = line.GetSubstring(1, index - 1);
			line.RemoveSubstring(1, index);
			return kJTrue;
			}
		line.RemoveSubstring(1, 1);
		return kJFalse;
		}
	record = line;
	if (record.Contains("(") && !record.Contains(")"))
		{
		JString temp = JReadUntil(is, ')');
		record += temp + ")";
		line = JReadLine(is);
		if (!line.IsEmpty() && (line.GetFirstCharacter() == '\t'))
			{
			line.RemoveSubstring(1, 1);
			}
		}
	else
		{
		line.Clear();
		}
	return kJTrue;
}
JString
CMLineAddressTable::GetLineTextFromAddress
	(
	const JString& addr
	)
	const
{
	JString s = addr;
	if (s.BeginsWith("0x"))
		{
		s.RemoveSubstring(1,2);
		}

	while (s.GetFirstCharacter() == '0' &&
		   s.GetLength() > itsLineTextList->LastElement()->GetLength())
		{
		s.RemoveSubstring(1,1);
		}

	return s;
}
JBoolean
CBStringCompleter::Complete
	(
	JTextEditor*			te,
	const JBoolean			includeNS,
	JXStringCompletionMenu*	menu
	)
{
	assert( menu != NULL );

	JIndex caretIndex;
	if (!te->GetCaretLocation(&caretIndex))
		{
		return kJFalse;
		}

	const JString& text = te->GetText();
	JIndex i            = caretIndex;
	while (i > 1 && IsWordCharacter(text, i-1, includeNS))
		{
		i--;
		}

	if (i == caretIndex)
		{
		return kJFalse;
		}

	JString s;
	const JString prefix   = text.GetSubstring(i, caretIndex-1);
	const JSize matchCount = Complete(prefix, &s, menu);
	if (matchCount > 0 &&
		s.GetLength() > prefix.GetLength())
		{
		s.RemoveSubstring(1, prefix.GetLength());
		te->Paste(s);
		menu->ClearRequestCount();
		return kJTrue;
		}
	else if (matchCount > 1)
		{
		menu->CompletionRequested(prefix.GetLength());
		return kJTrue;
		}
	else if (matchCount == 0 && includeNS)
		{
		// try once more without namespace
		return Complete(te, kJFalse, menu);
		}

	return kJFalse;
}
void
JWebBrowser::ShowURL
	(
	const JCharacter* url
	)
{
	JString s = url;
	if (s.BeginsWith(kMailURLPrefix, kJFalse))
		{
		s.RemoveSubstring(1, kMailURLPrefixLength);
		ComposeMail(s);
		}
	else if (s.BeginsWith(kFileURLPrefix, kJFalse))
		{
		s.RemoveSubstring(1, kFileURLPrefixLength);
		ShowFileContent(s);
		}
	else
		{
		Exec(itsShowURLCmd, kURLVarName, url);
		}
}
JString
CMLineAddressTable::BuildAddress
	(
	const JString& addr
	)
{
	JString s = addr;
	JIndex i;
	if (s.LocateSubstring(" ", &i))
		{
		s.RemoveSubstring(i, s.GetLength());
		}

	s.Prepend("0x");
	return s;
}
Esempio n. 14
0
JString
CBCClass::RemoveNamespace
	(
	const JCharacter* fullName
	)
{
	JString name = fullName;

	JIndex colonIndex;
	if (name.LocateLastSubstring("::", &colonIndex))
		{
		name.RemoveSubstring(1, colonIndex+1);
		}

	return name;
}
void
JXStringCompletionMenu::HandleSelection
	(
	const JIndex index
	)
{
	if (itsAllowTabChar && index == kInsertTabCmd)
		{
		// tab may insert spaces
		itsTE->JXTEBase::HandleKeyPress('\t', JXKeyModifiers(GetDisplay()));
		}
	else if (index - (itsAllowTabChar ? kSpecialCmdCount : 0) <= kMaxItemCount)
		{
		JString s = GetItemText(index);
		s.RemoveSubstring(1, kItemPrefixLength + itsPrefixLength);
		itsTE->Paste(s);
		}
}
Esempio n. 16
0
void
GFGLink::ParseClass
	(
	GFGClass* 		  list,
	const JCharacter* filename, 
	const JCharacter* classname
	)
{
	JBoolean ok	= kJTrue;
	if (itsCTagsProcess == NULL)
		{
		ok = StartCTags();
		}

	if (ok)
		{
		itsClassList	= list;
		itsCurrentClass	= classname;
		itsCurrentFile	= filename;

		JConvertToAbsolutePath(filename, "", &itsCurrentFile);

		itsCurrentFile.Print(*itsOutputLink);
		*itsOutputLink << std::endl;
		itsOutputLink->flush();

		JBoolean found = kJFalse;
		JString result = JReadUntil(itsInputFD, kDelimiter, &found);

		if (found)
			{
			JIndex findex;
			while (	result.LocateSubstring("\n", &findex) &&
					findex > 1)
				{
				JString line	= result.GetSubstring(1, findex - 1);
				result.RemoveSubstring(1, findex);
				ParseLine(line);
				}
			Broadcast(FileParsed());
			}
		}
}
JBoolean
GMMIMEParser::GetTextSegment
	(
	const JIndex	index,
	JString*		text,
	TextFormat*		format,
	JString*		charset
	)
{
	assert(itsTextInfo != NULL);
	assert(index <= itsTextInfo->GetEntryCount());
	JString name = itsTextInfo->GetEntry(index).GetFullName();
	std::ifstream is(name);
	if (is.good())
		{
		JReadAll(is, text);
		if (name.EndsWith(kPlainType))
			{
			*format = kPlain;
			}
		else if (name.EndsWith(kHTMLType))
			{
			*format = kHTML;
			}
		JIndex findex;
		JBoolean ok = name.LocateLastSubstring(".", &findex);
		assert(ok);
		name.RemoveSubstring(findex, name.GetLength());
		ok = name.LocateLastSubstring(".", &findex);
		if (findex < name.GetLength())
			{
			*charset = name.GetSubstring(findex + 1, name.GetLength());
			}
		}
	else
		{
		itsIsSuccessful = kJFalse;
		}

	return itsIsSuccessful;
}
void
JFunctionWithVar::DrawString
	(
	const JExprRenderer&	renderer,
	const JCoordinate		left,
	const JCoordinate		midline,
	const JSize				fontSize,
	const JString&			str
	)
	const
{
	JCoordinate x = left;

	const JCharacter* greekPrefix = JPGetGreekCharPrefixString();
	const JSize greekPrefixLength = JPGetGreekCharPrefixLength();

	JString s = str;
	JIndex greekIndex;
	while (s.LocateSubstring(greekPrefix, &greekIndex) &&
		   greekIndex < s.GetLength() - greekPrefixLength + 1)
		{
		if (greekIndex > 1)
			{
			const JString s1 = s.GetSubstring(1, greekIndex-1);
			renderer.DrawString(x, midline, fontSize, s1);
			x += renderer.GetStringWidth(fontSize, s1);
			}

		const JCharacter c = s.GetCharacter(greekIndex + greekPrefixLength);
		renderer.DrawGreekCharacter(x, midline, fontSize, c);
		x += renderer.GetGreekCharWidth(fontSize, c);

		s.RemoveSubstring(1, greekIndex + greekPrefixLength);
		}

	if (!s.IsEmpty())
		{
		renderer.DrawString(x, midline, fontSize, s);
		}
}
void
GDBVarCommand::HandleSuccess
	(
	const JString& data
	)
{
	JString s = data;
	s.TrimWhitespace();

	JBoolean success = kJFalse;

	JIndexRange r;
	prefixPattern.SetSingleLine();
	if (prefixPattern.Match(data, &r))
		{
		s.RemoveSubstring(r);
		SetData(s);

		GDBVarTreeParser parser(s);
		if (parser.yyparse() == 0)
			{
			parser.ReportRecoverableError();
			success = kJTrue;
			Broadcast(ValueMessage(kValueUpdated, parser.GetRootNode()));
			}
		}
	else
		{
		(CMGetLink())->Log("GDBVarCommand failed to match");
		}

	if (!success)
		{
		Broadcast(ValueMessage(kValueFailed, NULL));
		}

	s.Clear();
	SetData(s);
}
Esempio n. 20
0
void
CBApp::GetSystemIncludeDirectories()
{
	int pid, fd, inFD;
	const JError err = JExecute("gcc -Wp,-v -x c++ -fsyntax-only -", &pid,
								kJCreatePipe, &inFD,
								kJCreatePipe, &fd,
								kJAttachToFromFD);
	if (!err.OK())
		{
		for (const JCharacter* s : kSysIncludeDir)
			{
			itsSystemIncludeDirs->Append(s);
			}
		return;
		}

	close(inFD);

	JString s;
	while (1)
		{
		s = JReadUntil(fd, '\n');
		if (s.IsEmpty())
			{
			break;
			}

		if (s.GetFirstCharacter() == ' ')
			{
			s.RemoveSubstring(1,1);
			if (!s.Contains(" "))
				{
				itsSystemIncludeDirs->Append(s);
				}
			}
		}
}
JSize
JFunctionWithVar::GetStringWidth
	(
	const JExprRenderer&	renderer,
	const JSize				fontSize,
	const JString&			str
	)
	const
{
	JSize w = 0;

	const JCharacter* greekPrefix = JPGetGreekCharPrefixString();
	const JSize greekPrefixLength = JPGetGreekCharPrefixLength();

	JString s = str;
	JIndex greekIndex;
	while (s.LocateSubstring(greekPrefix, &greekIndex) &&
		   greekIndex < s.GetLength() - greekPrefixLength + 1)
		{
		if (greekIndex > 1)
			{
			const JString s1 = s.GetSubstring(1, greekIndex-1);
			w += renderer.GetStringWidth(fontSize, s1);
			}

		const JCharacter c = s.GetCharacter(greekIndex + greekPrefixLength);
		w += renderer.GetGreekCharWidth(fontSize, c);

		s.RemoveSubstring(1, greekIndex + greekPrefixLength);
		}

	if (!s.IsEmpty())
		{
		w += renderer.GetStringWidth(fontSize, s);
		}

	return w;
}
void
GetTempVarName
	(
	const JCharacter*			tagName,
	JString*					varName,
	const JPtrArray<JString>&	objNames
	)
{
	JString suffix = tagName;
	JIndexRange r;
	while (illegalCChar.Match(suffix, &r))
		{
		suffix.RemoveSubstring(r);
		}
	suffix.PrependCharacter('_');

	const JString prefix = "obj";
	const JSize count    = objNames.GetElementCount();
	for (JIndex i=1; i<=INT_MAX; i++)
		{
		*varName = prefix + JString(i) + suffix;
		JBoolean unique = kJTrue;
		for (JIndex j=1; j<=count; j++)
			{
			const JString* usedName = objNames.NthElement(j);
			if (*varName == *usedName)
				{
				unique = kJFalse;
				break;
				}
			}
		if (unique)
			{
			break;
			}
		}
}
Esempio n. 23
0
void
XDLink::SetProgram
	(
	const JCharacter* fileName
	)
{
	Send("detach");

//	StopDebugger();		// avoid broadcasting DebuggerRestarted
//	StartDebugger();

	itsProgramConfigFileName.Clear();
	itsSourcePathList->DeleteAll();

	JString fullName;
	if (!JConvertToAbsolutePath(fileName, NULL, &fullName) ||
		!JFileReadable(fullName))
		{
		const JString error = JGetString("ConfigFileUnreadable::XDLink");
		Broadcast(UserOutput(error, kJTrue));
		return;
		}
	else if (CMMDIServer::IsBinary(fullName))
		{
		const JString error = JGetString("ConfigFileIsBinary::XDLink");
		Broadcast(UserOutput(error, kJTrue));
		return;
		}

	JString line;
	if (!CMMDIServer::GetLanguage(fullName, &line) ||
		JStringCompare(line, "php", kJFalse) != 0)
		{
		const JString error = JGetString("ConfigFileWrongLanguage::XDLink");
		Broadcast(UserOutput(error, kJTrue));
		return;
		}

	JString path, name, suffix;
	JSplitPathAndName(fullName, &path, &name);
	JSplitRootAndSuffix(name, &itsProgramName, &suffix);

	itsProgramConfigFileName = fullName;

	ifstream input(fullName);
	while (1)
		{
		line = JReadLine(input);
		line.TrimWhitespace();

		if (line.BeginsWith("source-path:"))
			{
			line.RemoveSubstring(1, 12);
			line.TrimWhitespace();

			name = JCombinePathAndName(path, line);
			itsSourcePathList->Append(name);
			}
		else if (!line.IsEmpty() && !line.BeginsWith("code-medic:"))
			{
			line.Prepend("Unknown option: ");
			line.AppendCharacter('\n');
			Broadcast(UserOutput(line, kJTrue));
			}

		if (!input.good())
			{
			break;
			}
		}

	XDSetProgramTask* task = new XDSetProgramTask();
	assert( task != NULL );
	task->Go();
}
Esempio n. 24
0
void
GFGLink::ParseInterface
	(
	GFGMemberFunction* 	fn,
	const JIndex 		line
	)
{
	std::ifstream is(itsCurrentFile);
	if (!is.good())
		{
		return;
		}

	// skip to the function's line
	JString str;
	for (JIndex i = 1; i < line; i++)
		{
		str	= JReadLine(is);
		}

	JSize p1	= JTellg(is);

	is >> std::ws;
	str	= JReadUntilws(is);
	if (str != "virtual")
		{
		return;
		}

	is >> std::ws;

	// return type
	str	= JReadUntilws(is);
	if (str	== "const")
		{
		str	+= " " + JReadUntilws(is);
		}
	fn->SetReturnType(str);

	is >> std::ws;

	// this should be the function name
	str	= JReadUntil(is, '(');
	str.TrimWhitespace();
	if (str != fn->GetFnName())
		{
		return;
		}

	// get arguments
	JCharacter delim	= ',';
	while (delim == ',')
		{
		JBoolean ok	= JReadUntil(is, 2, ",)", &str, &delim);
		if (!ok)
			{
			return;
			}
		JIndex findex;
		if (str.LocateSubstring("//", &findex))
			{
			JIndex eindex;
			if (str.LocateSubstring("\n", &eindex) &&
				eindex >= findex)
				{
				str.RemoveSubstring(findex, eindex);
				}
			}
		str.TrimWhitespace();
		if (!str.IsEmpty())
			{
			fn->AddArg(str);
			}		
		}

	is >> std::ws;

	// is it const;
	str	= JReadUntil(is, ';');
	if (str.Contains("const"))
		{
		fn->ShouldBeConst(kJTrue);
		}

	JSize p2	= JTellg(is);
	JSeekg(is, p1);

	str	= JRead(is, p2 - p1);
	fn->SetInterface(str);
}
Esempio n. 25
0
JBoolean
JExpandHomeDirShortcut
	(
	const JCharacter*	path,
	JString*			result,
	JString*			homeDir,
	JSize*				homeLength
	)
{
	assert( !JStringEmpty(path) && result != NULL );

	JBoolean ok = kJTrue;
	if (path[0] == '~' && path[1] == '\0')
		{
		ok = JGetHomeDirectory(result);
		if (ok && homeDir != NULL)
			{
			*homeDir = *result;
			}
		if (ok && homeLength != NULL)
			{
			*homeLength = 1;
			}
		}
	else if (path[0] == '~' && path[1] == '/')
		{
		ok = JGetHomeDirectory(result);
		if (ok && homeDir != NULL)
			{
			*homeDir = *result;
			}
		if (ok && homeLength != NULL)
			{
			*homeLength = 2;
			}
		if (ok && path[2] != '\0')
			{
			*result = JCombinePathAndName(*result, path+2);
			}
		}
	else if (path[0] == '~')
		{
		JString userName = path+1;
		JIndex i;
		const JBoolean found = userName.LocateSubstring("/", &i);
		if (found)
			{
			userName.RemoveSubstring(i, userName.GetLength());
			}

		ok = JGetHomeDirectory(userName, result);
		if (ok && homeDir != NULL)
			{
			*homeDir = *result;
			}
		if (ok && homeLength != NULL)
			{
			*homeLength = found ? i+1 : strlen(path);
			}
		if (ok && found && path[i+1] != '\0')
			{
			*result = JCombinePathAndName(*result, path+i+1);
			}
		}
	else
		{
		*result = path;
		if (homeDir != NULL)
			{
			homeDir->Clear();
			}
		if (homeLength != NULL)
			{
			*homeLength = 0;
			}
		}

	if (ok)
		{
		return kJTrue;
		}
	else
		{
		result->Clear();
		if (homeDir != NULL)
			{
			homeDir->Clear();
			}
		if (homeLength != NULL)
			{
			*homeLength = 0;
			}
		return kJFalse;
		}
}
Esempio n. 26
0
JString
JConvertToRelativePath
	(
	const JCharacter* origPath,
	const JCharacter* origBase
	)
{
	// Check that they are both absolute paths.

	assert( origPath != NULL && origPath[0] == '/' &&
			origBase != NULL && origBase[0] == '/' );

	// Remove extra directory separators
	// and make sure that base ends with one.

	JString path = origPath;
	JCleanPath(&path);

	JString base = origBase;
	JCleanPath(&base);
	JAppendDirSeparator(&base);

	// Find and remove the matching directories at the beginning.
	// The while loop backs us up so we only consider complete directory names.

	JBoolean hadTDS = kJTrue;
	if (path.GetLastCharacter() != '/')
		{
		path.AppendCharacter('/');
		hadTDS = kJFalse;
		}

	JSize matchLength = JCalcMatchLength(path, base);

	if (!hadTDS)
		{
		path.RemoveSubstring(path.GetLength(), path.GetLength());
		}

	while (base.GetCharacter(matchLength) != '/')
		{
		matchLength--;
		}
	assert( matchLength >= 1 );
	if (matchLength == 1)
		{
		return path;
		}

	if (matchLength > path.GetLength())
		{
		base.RemoveSubstring(matchLength, matchLength);
		matchLength--;
		}

	path.RemoveSubstring(1, matchLength);
	base.RemoveSubstring(1, matchLength);

	if (base.IsEmpty())
		{
		path.Prepend("./");
		return path;
		}

	// The number of remaining directory separators in base
	// is the number of levels to go up.

	JSize upCount = 0;

	const JSize baseLength = base.GetLength();
	for (JIndex i=1; i<=baseLength; i++)
		{
		if (base.GetCharacter(i) == '/')
			{
			upCount++;
			path.Prepend("../");
			}
		}
	assert( upCount > 0 );

	return path;
}
void
CBCompileDocument::AppendText
	(
	const JString& origText
	)
{
	const JString* text = &origText;
	JBoolean deleteText = kJFalse;
	if (strchr(*text, kMultibyteMarker) != NULL)
		{
		JString* s = jnew JString(origText);
		assert( s != NULL );
		text       = s;
		deleteText = kJTrue;

		JSize length = s->GetLength();
		for (JIndex i=1; i<=length; i++)
			{
			if (s->GetCharacter(i) == kMultibyteMarker && i <= length-2)
				{
				const unsigned char c1 = s->GetCharacter(i+1);
				const unsigned char c2 = s->GetCharacter(i+2);
				const JIndex u = (((unsigned int) (unsigned char) c1) << 8) |
								 ((unsigned int) (unsigned char) c2);

				if (u == 32920 || u == 32921)
					{
					s->ReplaceSubstring(i, i+2, "'");
					}
				else
					{
					std::cout << "jcc: AppendText: unicode: " << u << std::endl;
					s->ReplaceSubstring(i, i+2, "\x80");
					}

				length -= 2;
				}
			}
		}

	const JBoolean isJavacError = javacOutputRegex.Match(*text);

	JIndexRange gccPrevLineRange, gccRange;
	const JBoolean isGCCError = JI2B(!isJavacError && gccErrorRegex.Match(*text, &gccRange));

	JIndexRange flexRange;
	const JBoolean isFlexError = flexErrorRegex.Match(*text, &flexRange);

	JIndexRange bisonRange;
	const JBoolean isBisonError = bisonErrorRegex.Match(*text, &bisonRange);

	JIndexRange makeRange;
	const JBoolean isMakeError = JI2B(
		makeErrorRegex.Match(*text, &makeRange) && !text->EndsWith(makeIgnoreErrorStr) );

	JArray<JIndexRange> absoftRangeList;
	const JBoolean isAbsoftError = absoftErrorRegex.Match(*text, &absoftRangeList);

	JArray<JIndexRange> maven2RangeList;
	const JBoolean isMaven2Error = maven2ErrorRegex.Match(*text, &maven2RangeList);

	JArray<JIndexRange> maven3RangeList;
	const JBoolean isMaven3Error = maven3ErrorRegex.Match(*text, &maven3RangeList);

	if (isGCCError &&
		gccErrorRegex.Match(itsPrevLine, &gccPrevLineRange) &&
		gccPrevLineRange == gccRange &&
		JCompareMaxN(itsPrevLine, *text, gccRange.last, kJTrue))
		{
		JString s = *text;
		s.RemoveSubstring(1, gccRange.last - 1);
		s.Prepend(" /");

		// in front of 1 or 2 trailing newlines

		CBTextEditor* te = GetTextEditor();
		te->SetCaretLocation(te->GetTextLength() - (theDoubleSpaceFlag ? 1 : 0));
		te->Paste(s);
		}
	else if (!isJavacError && !isGCCError &&
			 gccErrorRegex.Match(itsPrevLine, &gccPrevLineRange) &&
			 text->BeginsWith(gccMultilinePrefix) &&
			 text->GetLength() > kGCCMultilinePrefixLength &&
			 !isspace(text->GetCharacter(kGCCMultilinePrefixLength+1)))
		{
		JString s = *text;
		s.RemoveSubstring(1, strlen(gccMultilinePrefix));

		CBTextEditor* te = GetTextEditor();
		te->SetCaretLocation(te->GetTextLength() - (theDoubleSpaceFlag ? 1 : 0));
		te->Paste(s);
		}
	else
		{
		CBTextEditor* te        = GetTextEditor();
		const JIndex startIndex = te->GetTextLength() + 1;

		CBExecOutputDocument::AppendText(*text);
		if (theDoubleSpaceFlag)
			{
			te->Paste("\n");
			}

		itsPrevLine = *text;

		// display file name in bold and activate Errors menu

		JIndexRange boldRange;
		if (isJavacError)
			{
			JArray<JIndexRange> javacMatchList;
			if (javacErrorRegex.Match(*text, &javacMatchList))
				{
				const JIndexRange r = javacMatchList.GetElement(2);
				boldRange.Set(startIndex + r.first-1, startIndex + r.last-1);
				}
			}
		else if (isGCCError)
			{
			boldRange.Set(startIndex, startIndex + gccRange.first - 1);
			}
		else if (isFlexError)
			{
			boldRange.Set(startIndex+1, startIndex + flexRange.first);
			}
		else if (isBisonError)
			{
			boldRange.Set(startIndex+2, startIndex + bisonRange.first + 1);
			}
		else if (isMakeError)
			{
			boldRange.SetFirstAndLength(startIndex, text->GetLength());
			}
		else if (isAbsoftError)
			{
			boldRange  = absoftRangeList.GetElement(2);
			boldRange += startIndex-1;
			}
		else if (isMaven2Error)
			{
			boldRange  = maven2RangeList.GetElement(2);
			boldRange += startIndex-1;
			}
		else if (isMaven3Error)
			{
			boldRange  = maven3RangeList.GetElement(2);
			boldRange += startIndex-1;
			}

		if (!boldRange.IsEmpty())
			{
			te->JTextEditor::SetFont(boldRange.first, boldRange.last, GetErrorFont(), kJTrue);

			if (!itsHasErrorsFlag)
				{
				itsHasErrorsFlag = kJTrue;
				itsErrorMenu->Activate();

				JXWindow* window    = GetWindow();
				JString windowTitle = window->GetTitle();
				windowTitle.SetCharacter(1, '!');
				windowTitle.SetCharacter(2, '!');
				windowTitle.SetCharacter(3, '!');
				window->SetTitle(windowTitle);
				}
			}
		}

	if (deleteText)
		{
		jdelete text;
		}
}
void
CBCommandTable::TableDrawCell
	(
	JPainter&		p,
	const JPoint&	cell,
	const JRect&	rect
	)
{
	JPoint editCell;
	if (GetEditedCell(&editCell) && cell == editCell)
		{
		return;
		}

	HilightIfSelected(p, cell, rect);

	const CBCommandManager::CmdInfo info = itsCmdList->GetElement(cell.y);
	if (info.separator)
		{
		JPoint pt1 = rect.bottomLeft(), pt2 = rect.bottomRight();
		pt1.y--;
		pt2.y--;
		p.Line(pt1, pt2);
		}

	if (cell.x == kOptionsColumn)
		{
		JString s;
		if (info.isMake)
			{
			s += ",M";
			}
		if (info.isVCS)
			{
			s += ",V";
			}
		if (info.saveAll)
			{
			s += ",S";
			}
		if (info.oneAtATime)
			{
			s += ",O";
			}
		if (info.useWindow)
			{
			s += ",W";
			}
		if (info.raiseWindowWhenStart)
			{
			s += ",R";
			}
		if (info.beepWhenFinished)
			{
			s += ",B";
			}

		if (!s.IsEmpty())
			{
			if (s.GetFirstCharacter() == ',')
				{
				s.RemoveSubstring(1,1);
				}
			p.String(rect, s, JPainter::kHAlignCenter, JPainter::kVAlignCenter);
			}
		}
	else
		{
		const JString* s = NULL;
		JFontStyle style;
		if (cell.x == kMenuTextColumn)
			{
			s = info.menuText;
			}
		else if (cell.x == kMenuShortcutColumn)
			{
			s = info.menuShortcut;
			}
		else if (cell.x == kScriptNameColumn)
			{
			s = info.name;
			}
		else if (cell.x == kCommandColumn)
			{
			s = info.cmd;
			}
		else if (cell.x == kPathColumn)
			{
			s = info.path;
			style.color =
				CBCommandPathInput::GetTextColor(*s, itsBasePath, kJFalse, p.GetColormap());
			}
		assert( s != NULL );

		p.SetFont(itsFontName, itsFontSize, style);

		JRect r = rect;
		r.left += kHMarginWidth;
		p.String(r, *s, JPainter::kHAlignLeft, JPainter::kVAlignCenter);
		}
}
JBoolean
CBCtagsUser::HasExuberantCtags()
{
	if (itsHasExuberantCtagsFlag == kUntested)
		{
		itsHasExuberantCtagsFlag = kFailure;

		// this hack is required on Linux kernel 2.3.x (4/19/2000)
		j_sig_func*	origHandler = signal(SIGCHLD, emptyHandler);

		pid_t pid;

		#if defined _J_SUNOS
		pid_t* ppid = NULL;
		#else
		pid_t* ppid = &pid;
		#endif

		int fromFD;
		JError err = JExecute(kCheckVersionCmd, ppid,
							  kJIgnoreConnection, NULL,
							  kJCreatePipe, &fromFD,
							  kJTossOutput, NULL);
		if (err.OK())
			{
			JString vers;
			JReadAll(fromFD, &vers);

			JArray<JIndexRange> matchList;
			if (versionPattern.Match(vers, &matchList))
				{
				matchList.RemoveElement(1);

				const JSize count = matchList.GetElementCount();
				JString s;
				for (JIndex i=1; i<=count; i++)
					{
					JUInt v = 0;
					const JIndexRange r = matchList.GetElement(i);
					if (!r.IsEmpty())
						{
						s = vers.GetSubstring(r);
						while (!isdigit(s.GetFirstCharacter()))
							{
							s.RemoveSubstring(1, 1);
							}
						const JBoolean ok = s.ConvertToUInt(&v);
						assert( ok );
						}

					if (v > kMinVersion[i-1] ||
						(i == count && v == kMinVersion[i-1]))
						{
						itsHasExuberantCtagsFlag = kSuccess;
						break;
						}
					else if (v < kMinVersion[i-1])
						{
						break;
						}
					}
				}
			}

		if (origHandler != SIG_ERR)
			{
			signal(SIGCHLD, origHandler);
			}
		}

	return JI2B( itsHasExuberantCtagsFlag == kSuccess );
}
int
main
	(
	int		argc,
	char*	argv[]
	)
{
	// find the configuration files

	if (!FindConfigFile(&classMapFile) ||
		!FindConfigFile(&optionMapFile) ||
		!FindConfigFile(&needFontListFile) ||
		!FindConfigFile(&needStringListFile) ||
		!FindConfigFile(&needCreateListFile))
		{
		return 1;
		}

	// parse the command line options

	JString inputName, codePath, stringPath, codeSuffix, headerSuffix;
	JString postCmd;
	JPtrArray<JString> userFormList(JPtrArrayT::kDeleteAll);	// empty => generate all forms
	JPtrArray<JString> backupList(JPtrArrayT::kDeleteAll);		// forms that have been backed up
	GetOptions(argc, argv, &inputName, &codePath, &stringPath,
			   &codeSuffix, &headerSuffix, &postCmd, &userFormList);

	// generate each requested form

	JBoolean changed = kJFalse;

	ifstream input(inputName);
	while (!input.eof() && !input.fail())
		{
		const JString line = JReadLine(input);
		if (line == kBeginFormLine)
			{
			// get form name

			JString formName = JReadLine(input);
			RemoveIdentifier(kFormNameMarker, &formName);

			// look for custom tag

			const JSize formNameLength = formName.GetLength();
			JString tagName            = kDefaultDelimTag;
			JString enclName;
			JIndex tagMarkerIndex;
			if (formName.LocateSubstring(kCustomTagMarker, &tagMarkerIndex) &&
				tagMarkerIndex <= formNameLength - kCustomTagMarkerLength)
				{
				tagName = formName.GetSubstring(
					tagMarkerIndex + kCustomTagMarkerLength, formNameLength);
				formName.RemoveSubstring(tagMarkerIndex, formNameLength);

				// get enclosure name

				const JSize tagNameLength = tagName.GetLength();
				JIndex enclMarkerIndex;
				if (tagName.LocateSubstring(kCustomTagMarker, &enclMarkerIndex) &&
					enclMarkerIndex <= tagNameLength - kCustomTagMarkerLength)
					{
					enclName = tagName.GetSubstring(
						enclMarkerIndex + kCustomTagMarkerLength, tagNameLength);
					tagName.RemoveSubstring(enclMarkerIndex, tagNameLength);
					}

				// report errors

				if (tagName != kDefaultDelimTag)
					{
					if (enclName.IsEmpty())
						{
						cerr << formName << ", " << tagName;
						cerr << ": no enclosure specified" << endl;
						}
					}
				else if (!enclName.IsEmpty() && enclName != kDefTopEnclVarName)
					{
					cerr << formName << ", " << tagName;
					cerr << ": not allowed to specify enclosure other than ";
					cerr << kDefTopEnclVarName << endl;
					}
				}

			if (ShouldGenerateForm(formName, userFormList))
				{
				GenerateForm(input, formName, tagName, enclName,
							 codePath, stringPath, codeSuffix, headerSuffix, &backupList);
				changed = kJTrue;
				}
			}
		}

	if (changed && !postCmd.IsEmpty())
		{
		const JError err = JExecute(postCmd, NULL);
		err.ReportIfError();
		}

	return 0;
}