void
CBSymbolList::ReadSymbolList
	(
	istream&			input,
	const CBLanguage	lang,
	const JCharacter*	fullName,
	const JFAID_t		fileID
	)
{
	JString path, fileName;
	JSplitPathAndName(fullName, &path, &fileName);

	input >> ws;
	while (input.peek() == '!')
		{
		JIgnoreLine(input);
		input >> ws;
		}

	JStringPtrMap<JString> flags(JPtrArrayT::kDeleteAll);
	while (1)
		{
		JString* name = new JString;
		assert( name != NULL );

		input >> ws;
		*name = JReadUntil(input, '\t');		// symbol name
		if (input.eof() || input.fail())
			{
			delete name;
			break;
			}

		JIgnoreUntil(input, '\t');				// file name

		JIndex lineIndex;
		input >> lineIndex;						// line index

		ReadExtensionFlags(input, &flags);

		JCharacter typeChar = ' ';
		JString* value;
		if (flags.GetElement("kind", &value) && !value->IsEmpty())
			{
			typeChar = value->GetFirstCharacter();
			}

		JString* signature = NULL;
		if (flags.GetElement("signature", &value) && !value->IsEmpty())
			{
			signature = new JString(*value);
			assert( signature != NULL );
			signature->PrependCharacter(' ');
			}

		if (IgnoreSymbol(*name))
			{
			delete name;
			}
		else
			{
			const Type type = DecodeSymbolType(lang, typeChar);
			if (signature == NULL &&
				(IsFunction(type) || IsPrototype(type)))
				{
				signature = new JString(" ( )");
				assert( signature != NULL );
				}

			const SymbolInfo info(name, signature, lang, type,
								  kJFalse, fileID, lineIndex);
			itsSymbolList->InsertSorted(info);

			// add file:name

			if (IsFileScope(type))
				{
				JString* name1 = new JString(fileName);
				assert( name1 != NULL );
				*name1 += ":";
				*name1 += *name;

				JString* sig1 = NULL;
				if (signature != NULL)
					{
					sig1 = new JString(*signature);
					assert( sig1 != NULL );
					}

				const SymbolInfo info1(name1, sig1, lang, type,
									   kJTrue, fileID, lineIndex);
				itsSymbolList->InsertSorted(info1);
				}
			}
		}
}
void
CBFnMenuUpdater::ReadFunctionList
	(
	std::istream&	input,
	CBLanguage		lang,
	const JBoolean	sort,
	const JBoolean	includeNS,
	JXTextMenu*		menu,
	JArray<JIndex>*	lineIndexList
	)
{
	JPtrArray<JString> fnNameList(JPtrArrayT::kDeleteAll);
	fnNameList.SetCompareFunction(JCompareStringsCaseInsensitive);
	fnNameList.SetSortOrder(JOrderedSetT::kSortAscending);

	lineIndexList->SetCompareFunction(JCompareIndices);
	lineIndexList->SetSortOrder(JOrderedSetT::kSortAscending);

	// build symbol list

	const JBoolean hasNS = CBHasNamespace(lang);
	JString fnName;
	while (1)
		{
		input >> std::ws;
		fnName = JReadUntil(input, '\t');	// fn name
		if (input.eof() || input.fail())
			{
			break;
			}

		JIgnoreUntil(input, '\t');			// file name

		JIndex lineIndex;
		input >> lineIndex;					// line index

		if (IgnoreSymbol(fnName))
			{
			continue;
			}

		// toss qualified or unqualified version

		if (hasNS && !includeNS && cbIsQualified(fnName))
			{
			continue;
			}

		// save symbol

		JIndex i;
		if (sort)
			{
			i = fnNameList.GetInsertionSortIndex(&fnName);
			}
		else
			{
			i = lineIndexList->GetInsertionSortIndex(lineIndex);
			}
		fnNameList.InsertAtIndex(i, fnName);
		lineIndexList->InsertElementAtIndex(i, lineIndex);
		}

	if (hasNS && includeNS)
		{
		JSize count = fnNameList.GetElementCount();
		for (JIndex i=1; i<=count; i++)
			{
			const JString* fnName  = fnNameList.NthElement(i);
			const JIndex lineIndex = lineIndexList->GetElement(i);
			if (cbIsQualified(*fnName))
				{
				for (JIndex j=1; j<=count; j++)
					{
					if (j != i && lineIndexList->GetElement(j) == lineIndex)
						{
						fnNameList.DeleteElement(j);
						lineIndexList->RemoveElement(j);
						count--;
						if (j < i)
							{
							i--;
							}
						break;
						}
					}
				}
			}
		}

	// build menu

	const JSize count = fnNameList.GetElementCount();
	for (JIndex i=1; i<=count; i++)
		{
		menu->AppendItem(*(fnNameList.NthElement(i)));
		}
}