/* -*- LPC -*- */
/**
* The finger object, this handles all the finger and refer information
* for the players.
* @author Pinkfish
* @started way back in 1991 sometime
*/
/*
* $Locker: $
* $Id: finger.c,v 1.114 2003/07/16 18:40:28 pinkfish Exp $
*
*/
#include <config.h>
#include <mail.h>
#include <finger.h>
#include <player_handler.h>
#include <player.h>
#include <access.h>
#include <login.h>
#include <clubs.h>
#include <alias.h>
#include <library.h>
#include <nomic_system.h>
inherit "/std/basic/club_listing";
inherit "/global/family";
// 7776000 == 90 days.
#define NO_EMAIL_TIME 7776000
#define WWW_REPLACEMENTS (["%^BOLD%^" : "<b>", \
"%^EBOLD%^" : "</b>", \
"%^RESET%^" : "", \
"\n" : "<br>\n" ])
#define REPLACEMENTS (["%^EBOLD%^" : "%^RESET%^", \
"<br>" : ""])
void create() {
seteuid("Root");
} /* create() */
/**
* This method fixes up the output for html.
* @param str the string to check
* @return the htmlised string
*/
string htmlise(string str) {
return replace_string(str, "<", "<");
} /* htmlise() */
/**
* This method returns the infomation printed when a banished person is
* fingered;
* @param name the name of the banished person
* @return the banished information
*/
string banish_finger(string name, int caller) {
string retval;
string *file;
file = explode(read_file("/banish/" + name[0..0] + "/" + name + ".o"), "\n");
retval = sprintf("%35-s%35-s\n", "Login name : " + name,
"Real name : Banished");
retval += "Not really many seconds old.\n";
retval += "No mail ever.\n";
retval += "No plan.\nNo future.\n";
if (caller) {
if (sizeof(file) == 3) {
retval += implode(file, "\n") + "\n";
} else {
retval += "Banish info not in the correct format.\n";
}
}
if (CLUB_HANDLER->is_club(name)) {
retval += "\nThere is also a club of this name, try: 'refer club " +
name + "' for information on the club.\n";
}
return replace(retval, "@@", "@ @ ");
}
/**
* This method turns a time elapsed into a string. Like: ten minutes
* and one second
* @param time_elapsed the amount of time elapsed
* @return a nice time elapsed string
*/
string time_elapsed_string(int time_elapsed) {
return query_time_string(time_elapsed, -1);
} /* time_elapsed_string() */
/**
* This method returns additional information about the player. This is
* stuff like, are they a playtester, are they a trustee etc.
* @param name the name of the player to get addional information about
* @param user_colour if it should user colour codes
* @param caller the grade of the caller
* (player, creator, director : 0, 1, 2)
* @return the additional information
*/
private string get_additional_info(string name, int no_colour,
int user_colour, int width, int caller) {
string retval, tmp, tmp_leader;
string *bing;
int i, used_colour, need_new, domain_leader, domain_deputy,
domain_developer;
object ob;
retval = "%^BOLD%^";
if (PLAYER_HANDLER->test_creator(name)) {
if (master()->query_trustee(name))
retval += capitalize(name) + " is a Trustee.\n";
else if (master()->query_director(name))
retval += capitalize(name) + " is a Director.\n";
else if (master()->query_senior(name))
retval += capitalize(name) + " is a Senior Creator.\n";
else
retval += capitalize(name) + " is a Creator.\n";
bing = "/secure/master"->query_domains();
tmp = "";
tmp_leader = "";
for (i = 0; i < sizeof(bing); i++) {
if ((string) ("/d/" + bing[i] + "/master")->query_lord() == name) {
bing[i] = capitalize(bing[i]);
tmp_leader += "Leader of the " + bing[i] + " domain.\n";
domain_leader = 1;
continue;
} else if (("/d/" + bing[i] + "/master")->query_deputy(name)) {
tmp += "Deputy of the " + capitalize(bing[i]) + " domain.\n";
domain_deputy = 1;
} else if (!("/d/" + bing[i] + "/master")->query_member(name)) {
if(bing[i] != "fluffy" && bing[i] != "liaison")
domain_developer = 1;
continue;
}
tmp += "Project in the " + capitalize(bing[i]) + " domain: " +
("/d/" + bing[i] + "/master")->query_project(name) + "\n";
bing[i] = capitalize(bing[i]);
}
tmp = tmp_leader + tmp;
if (!sizeof(bing) && !domain_leader && !domain_deputy &&
!domain_developer)
tmp += "Not a member of any domain.\n";
bing = ({ });
if("/d/admin/master"->query_member(name))
bing += ({ "Administrator" });
if(domain_leader)
bing += ({ "Domain Leader" });
if(domain_deputy)
bing += ({ "Domain Deputy" });
if("/d/liaison/master"->query_member(name))
bing += ({ "Liaison" });
else if(domain_developer)
bing += ({ "Developer" });
if(sizeof(bing))
retval += "Roles : " + query_multiple_short(bing) + ".\n";
if(tmp)
retval += sprintf("Domains : %-=*s", width - 10, tmp);
} else if (PLAYTESTER_HAND->query_playtester(name)) {
bing = ({"Playtester"});
if (PLAYTESTER_HAND->query_pt_exec (name)) {
bing += ({"Playtesting Executive"});
}
if (PLAYTESTER_HAND->query_senior_playtester(name)) {
retval += capitalize(name) + " is a Senior Playtester.\n";
} else {
retval += capitalize(name) + " is a Playtester.\n";
}
retval += "Roles : " + query_multiple_short(bing) + ".\n";
}
retval += "%^EBOLD%^";
i = PLAYER_HANDLER->test_start_time(name);
if (i)
retval += "First logged on " + ctime(i) + ".\n";
retval += capitalize(time_elapsed_string(-PLAYER_HANDLER->test_age(name))) + " old.\n";
// On since or last logged on info.
if (find_player(name) &&
(caller >= find_player(name)->query_invis() ||
(this_player() &&
reference_allowed(find_player(name), this_player())))) {
retval += "On since " + ctime(PLAYER_HANDLER->test_last(name)) + " (" +
capitalize(time_elapsed_string(time() - PLAYER_HANDLER->test_last(name))) +
").\n";
} else {
/* Should be a nice number.... */
i = time() - PLAYER_HANDLER->test_last(name);
retval += "Last logged off " + time_elapsed_string(i) + " ago.\n";
}
if (caller > 1) {
if (!find_player(name)) {
retval += PLAYER_HANDLER->test_last_on_from(name) + " ";
need_new = 1;
} else {
retval += query_ip_name(find_player(name)) + " (" +
query_ip_number(find_player(name)) + ")\n";
}
}
ob = find_player(name);
if (ob && (caller >= ob->query_invis() ||
(this_player() &&
reference_allowed (ob, this_player())))) {
if (interactive(ob)) {
retval += "Idle for " + time_elapsed_string(query_idle(ob)) + ".\n";
need_new = 0;
} else {
retval += "Net dead.\n";
need_new = 0;
}
}
if (need_new)
retval += "\n";
retval += (string) MAILER->finger_mail(name);
tmp = PLAYER_HANDLER->query_project(name);
if(no_colour || !user_colour) {
tmp = strip_colours(tmp);
tmp = replace(tmp, "%^", " ");
}
if (tmp && tmp != "") {
if (strsrch(tmp, "%^") != -1)
used_colour = 1;
retval += "%^BOLD%^Project:%^EBOLD%^\n" + tmp + "\n%^RESET%^";
} else
retval += "No project.\n";
tmp = PLAYER_HANDLER->query_plan(name);
if(no_colour || !user_colour) {
tmp = strip_colours(tmp);
tmp = replace(tmp, "%^", " ");
}
if (tmp && tmp != "") {
if (strsrch(tmp, "%^") != -1)
used_colour = 1;
retval += "%^BOLD%^Plan:%^EBOLD%^\n" + tmp + "\n%^RESET%^";
} else
retval += "No plan.\n";
if (used_colour)
retval += "%^RESET%^%^BOLD%^--- End of finger ---%^EBOLD%^\n";
return retval;
}
/**
* This method returns the intermud finger information.
* @param name the name to get the intermud info on
* @return the intermud information
*/
public mixed *remote_finger(string name) {
mixed *retval;
object ob;
string cname;
if (!strlen(name) || !PLAYER_HANDLER->test_user(name))
return 0;
ob = find_player(name);
cname = PLAYER_HANDLER->query_cap_name(name);
if (!cname) {
cname = capitalize(name);
}
retval = allocate(9);
retval[0] = cname;
retval[1] = cname;
retval[0] = cname;
retval[1] = cname;
retval[2] = (PLAYER_HANDLER->test_real_name(name) ?
PLAYER_HANDLER->test_real_name(name) : "???");
retval[3] = "0";
retval[4] = (ob && !ob->query_invis() ? ctime(ob->query_last_log_on()) :
capitalize(time_elapsed_string(time() -
PLAYER_HANDLER->test_last(name))));
retval[5] = (ob && !ob->query_invis() ? query_idle(ob) : -1);
retval[6] = 0;
retval[7] = 0;
retval[8] = get_additional_info(name, 1, 0, 80, 0);
return retval;
} /* remote_finger() */
/* Wraps the name with a link to its www finger info. */
private string link_name(string name) {
return "<a href=\"finger.c?player="+ name +"\">"+ capitalize(name) + "</a>";
}
/**
* This method returns the information used to do a domain finger of
* the domain.
* @param name the name of the domain to finger
* @param www 1 if called from www_finger_info()
* @return the information about the domain
*/
string domain_finger(string name,
int width,
int www)
{
string ret;
string master;
string *members;
string *deputies;
string idle_str;
int i;
string projects;
master = "/d/" + name + "/master";
if (!stringp(ret = master->generate_string())) {
ret = "%^BOLD%^The domain of " + capitalize(name) + ".\n";
ret += "The leader for this domain is " +
(www?
link_name((string) master->query_lord()) + ".\n":
capitalize((string) master->query_lord()) + ".\n");
members = master->query_members();
deputies = master->query_deputies();
if (deputies && sizeof(deputies)) {
if (sizeof(deputies) > 1) {
ret += "The deputies for the domain are ";
} else {
ret += "The deputy for the domain is ";
}
if(www) {
ret +=
query_multiple_short(map(deputies, (: link_name($1) :)));
} else {
ret +=
query_multiple_short(map(deputies, (: capitalize($1) :)));
}
ret += ".\n";
}
ret += "%^EBOLD%^";
ret += "The current members of this domain are :\n";
projects = "";
if(www) {
// Begin table
ret +=
"<table cols=\"2\" rules=\"none\""
"border=\"0\" width=\"100%\">";
}
for (i = 0; i < sizeof(members); i++) {
if (master->query_deputy(members[i])) {
projects = "[deputy] " + master->query_project(members[i]) + "\n";
} else {
projects = master->query_project(members[i]) + "\n";
}
if(www) {
ret += "<tr> <td>"+ link_name(members[i]) +
"</td> <td>"+ projects +"</td> <tr>";
} else {
ret += sprintf("%-=12s %-=*s",
capitalize(members[i]), width - 14, projects);
}
}
if(www) {
// Close the table
ret += "</table>";
}
}
if (master->query_info()) {
ret += sprintf("%-=*s", width, (string) master->query_info());
}
if ( ( idle_str = master->query_idle_mess() ) ) {
ret += idle_str;
}
else {
ret += "It hasn't been idle, it just hasn't had a lunch break in "
"years.\n";
}
return ret;
}
/**
* This method returns the information about the specified club.
* @param name the name of the club to finger
* @return the club information
*/
string club_finger(string name)
{
return club_info_string(name, 0, "gumball lord");
} /* club_finger() */
/**
* This method returns the information about the specified family.
* @param name the name of the family to finger
* @return the family information
*/
string family_finger(string name)
{
return family_info_string(name, "gumball lord");
} /* family_finger() */
/**
* This method returns the information about the specified deity.
* @param name the name of the deity to finger
* @return the deity information
*/
string deity_finger(string name)
{
return 0;
}
/** @ignore yes
* This handles fingering of things other than players.
* It currently supports: domains, banished names, clubs and families.
*/
private string other_finger(string name,
int no_colour,
int user_colour,
int width,
int caller,
int www)
{
// Im sure I wrote a function for this.
if (member_array(name, "/secure/master"->query_domains()) != -1) {
return domain_finger(name, width, www);
}
// have to be before test for banish as deities are banished
if (file_size("/save/deities/" + name + ".o") > 0 ||
file_size("/save/deities/" + name + ".o.gz") > 0) {
return deity_finger(name);
}
if (name[0..4] == "club ") {
if (CLUB_HANDLER->is_club(name[5..]) &&
!CLUB_HANDLER->is_family(name[5..])) {
return club_finger(name[5..]);
}
} else if (name[0..6] == "domain ") {
if (member_array(name[7..], "/secure/master"->query_domains()) != -1) {
return domain_finger(name[7..], width, www);
}
} else if (name[0..6] == "family ") {
if (CLUB_HANDLER->is_family(name[7..])) {
return family_finger(name[7..]);
}
} else if (CLUB_HANDLER->is_club(name)) {
if (CLUB_HANDLER->is_family(name)) {
return family_finger(name);
} else {
return club_finger(name);
}
}
if (file_size("/banish/" + name[0..0] + "/" + name + ".o") > 0) {
return banish_finger(name, caller);
}
return 0;
}
private string internal_finger_info(string name,
int no_colour,
int user_colour,
int width,
int caller,
int www) // 1 if webfinger
{
string retval,
tmp,
tmp2;
int i;
int days_left;
mixed *suspended, *lockedout;
// Call other_finger to handle fingering things that aren't actually players.
if (!find_player(name) && !PLAYER_HANDLER->test_user(name)) {
// if ( !PLAYER_HANDLER->test_user(name) ) {
return other_finger(name, no_colour, user_colour, width, caller, www);
}
// Do the first line of real & login name
retval = (no_colour ? "" : "%^BOLD%^") +
sprintf("%*-sReal name : %*-=s\n", (width - 2) / 2,
"Login name : " +
strip_colours(PLAYER_HANDLER->query_cap_name(name) + "<br>"),
(width - 2) / 2 - 12,
(PLAYER_HANDLER->test_real_name(name) ?
htmlise(strip_colours(PLAYER_HANDLER->test_real_name(name)[0..30])) :
"???"));
// Calculate and show the email & birthday line.
tmp = PLAYER_HANDLER->test_email(name);
tmp2 = PLAYER_HANDLER->test_birthday(name);
// Liaison deputies and above.
if ( strlen( tmp ) ) {
if(tmp[0] == ':' || www ||
PLAYER_HANDLER->test_last( name ) + NO_EMAIL_TIME < time()) {
if(caller == 0) {
tmp = "";
}
}
}
if (tmp) {
tmp = htmlise(tmp);
if (tmp2 && tmp2 != UNKNOWN_BIRTHDAY) {
tmp2 = htmlise(tmp2);
retval += sprintf("%*-sEmail : %*-=s\n", (width - 2) / 2,
"Birthday : " + tmp2 + "<br>", (width - 2) / 2,
tmp);
} else {
retval += sprintf("Email : %-*s\n", (width - 2) / 2 - 8, tmp[0..30]);
}
} else if (tmp2 && tmp2 != UNKNOWN_BIRTHDAY) {
tmp2 = htmlise(tmp2);
retval += sprintf("Birthday : %-*s\n", (width - 2) / 2 - 11, tmp2 +
"<br>");
}
// Indicate if they're marked for deletion.
i = PLAYER_HANDLER->test_deleting(name);
if (i) {
retval += (no_colour ? "" : "%^RED%^") +
"This character is marked to be deleted in ";
if (time() - i > (10 * 60 * 60 * 24)) {
retval += "1 day.\n";
} else {
days_left = (10 - ((time() - i) / (60 * 60 * 24)));
if (days_left > 1) {
retval += days_left + " days.\n";
} else {
retval += days_left + " day.\n";
}
}
if (!no_colour)
retval += "%^RESET%^";
}
// Indicate if they're marked for deletion.
i = PLAYER_HANDLER->test_appealing(name);
if (i) {
retval += (no_colour ? "" : "%^RED%^") +
"This character is marked to be deleted in ";
if (time() - i > (28 * 60 * 60 * 24)) {
retval += "1 day";
} else {
days_left = (28 - ((time() - i) / (60 * 60 * 24)));
if (days_left > 1) {
retval += days_left + " days";
} else {
retval += days_left + " day";
}
}
retval += " pending appeal.\n";
if (!no_colour)
retval += "%^RESET%^";
}
tmp = PLAYER_HANDLER->test_home_dir(name);
if (tmp)
retval +=
sprintf("%*-s", (width - 2) / 2, "Home directory : " + tmp + "<br>");
tmp = PLAYER_HANDLER->test_guild(name);
if (tmp) {
if (catch(retval += "Member of the " + tmp->query_short() + ".\n"))
retval += "Member of a very broken guild.\n";
} else
retval += "Member of the Adventurers' guild.\n";
if(!no_colour)
retval += "%^EBOLD%^";
tmp = PLAYER_HANDLER->test_location(name);
if (strlen(tmp)) {
tmp = htmlise(tmp);
retval += "Location : " + tmp[0..30] + "\n";
}
tmp = PLAYER_HANDLER->test_homepage(name);
if (stringp(tmp)) {
tmp = htmlise(tmp);
retval += "Home Page: " + tmp + "\n";
}
// Show their suspension if they're suspended.
suspended = "/secure/bastards"->query_suspended(name);
if (suspended) {
tmp = capitalize(name) + " has been suspended until " +
ctime(suspended[SUSPEND_TIME]);
if (caller) {
tmp += " for " + suspended[SUSPEND_REASON] + ".\n";
} else {
tmp += ".\n";
}
retval += "%^YELLOW%^" + sprintf("%s%-=*s", tmp[0..10], width - 11,
tmp[11..]) + "%^RESET%^";
}
// Show their suspension if they're suspended.
lockedout = "/secure/bastards"->query_lockedout(name);
if (lockedout) {
tmp = capitalize(name) + " is locked out until " +
ctime(lockedout[SUSPEND_TIME]);
if (caller) {
tmp += " because " + lockedout[SUSPEND_REASON] + ".\n";
} else {
tmp += ".\n";
}
retval += "%^YELLOW%^" + sprintf("%s%-=*s", tmp[0..10], width - 11,
tmp[11..]) + "%^RESET%^";
}
retval += htmlise(get_additional_info(name, no_colour, user_colour, width, caller));
return retval;
}
private string make_html(string str)
{
int pos;
pos = strsrch(str, "http:");
if (pos == -1) {
str = "http://" + str;
} else {
str = str[pos..];
}
pos = strsrch(str, " ");
if (pos != -1) {
str = str[0..pos - 1];
}
return str;
} /* make_html() */
/**
* This is the main finger method. It determines the type of finger
* and prints out the relevant information.
* @param name the name of the thing being fingered
* @param no_colour remove all the colour codes
* @param user_colour show user defined colours
* @return the shorterned finger information
*/
string finger_info(string name,
int no_colour)
{
int caller,
width,
user_colour;
string retval,
str;
string user,
nick;
user = this_player()->query_name();
user_colour = this_player()->query_property(PLAYER_ALLOW_COLOURED_SOULS);
if (this_player()) {
nick = (string) this_player()->expand_nickname(name);
if (nick)
name = nick;
width = this_player()->query_cols();
}
if (!width) {
width = 80;
}
if (master()->query_director(user)) {
caller = 3;
} else if (master()->is_liaison_deputy(user)) {
caller = 2;
} else if (PLAYER_HANDLER->test_creator(user)) {
caller = 1;
} else {
caller = 0;
}
retval = internal_finger_info(name, no_colour, user_colour,
width, caller, 0);
if (!retval) {
return 0;
}
foreach(str in keys(REPLACEMENTS)) {
retval = replace_string(retval, str, REPLACEMENTS[str]);
}
return retval;
}
/**
* This method returns the finger information to the web pages.
* @param name the name of the thing to finger
* @param user the user who is doing the fingering
* @return the html code for the finger
*/
string www_finger_info(string name,
string user)
{
int caller;
string retval,
str;
if (!user || user == "")
caller = 0;
else if (master()->query_director(user))
caller = 3;
else if (master()->is_liaison_deputy(user))
caller = 2;
else if (PLAYER_HANDLER->test_creator(user))
caller = 1;
else
caller = 0;
retval = internal_finger_info(name, 0, 1, 80, caller, 1);
if (!retval)
return 0;
foreach(str in keys(WWW_REPLACEMENTS))
retval = replace_string(retval, str, WWW_REPLACEMENTS[str]);
retval = strip_colours(retval);
return retval;
}
/**
* This method returns the refer information of the player.
* @param name the name of the player to get the refer information on
* @return the refer information
*/
string refer_info(string name) {
string retval, nick, tmp, family, rels, area, str;
object ob;
int width, sex;
retval = "";
width = 80;
if (this_player()) {
nick = (string) this_player()->expand_nickname(name);
width = this_player()->query_cols();
}
if (nick) {
name = nick;
}
if (!PLAYER_HANDLER->test_user(name)) {
retval = other_finger(name, 0, 0, width, 0, 0);
if(retval)
foreach(str in keys(REPLACEMENTS))
retval = replace_string(retval, str, REPLACEMENTS[str]);
return retval;
}
family = "";
tmp = PLAYER_HANDLER->test_player_title(name);
if (tmp) {
tmp += " ";
} else {
tmp = "";
}
retval = PLAYER_HANDLER->query_cap_name(name);
if (!retval) {
return 0;
}
tmp = PLAYER_HANDLER->test_family_name(name);
if (tmp) {
retval += " " + tmp;
}
if ("/d/liaison/master"->query_member(name)) {
retval += " (%^YELLOW%^Liaison%^RESET%^)";
} else if (PLAYER_HANDLER->test_creator(name)) {
if ("/secure/master"->query_trustee(name)) {
retval += " (%^RED%^Trustee%^RESET%^)";
} else if ("/secure/master"->query_director(name)) {
retval += " (%^RED%^Director%^RESET%^)";
} else if ("/secure/master"->query_senior(name)) {
retval += " (%^RED%^Senior%^RESET%^)";
} else {
retval += " (%^RED%^Creator%^RESET%^)";
}
}
ob = find_player(name);
if (ob) {
if (tmp = (string) ob->query_gtitle()) {
retval += " " + tmp;
} else {
retval += " the Adventurer";
}
if (tmp = (string)ob->query_property("player_title")) {
retval += ", " + tmp;
}
tmp = (string) ob->query_title();
if (tmp && tmp != "") {
retval += ", " + tmp;
}
}
if (PLAYTESTER_HAND->query_playtester(name)) {
if (PLAYTESTER_HAND->query_senior_playtester(name)) {
retval += ", (%^BOLD%^%^CYAN%^Senior Playtester%^RESET%^)";
} else {
retval += ", (%^CYAN%^Playtester%^RESET%^)";
}
}
if(ob) {
retval += ".";
if(ob->query_nationality_name()) {
retval += "\n" + capitalize(ob->query_possessive()) +
" nationality is " + capitalize(ob->query_nationality_name());
if(ob->query_nationality_region())
retval += " and " + ob->query_pronoun() + " is from "+
capitalize(ob->query_nationality_region());
retval += ".\n";
}
} else {
retval += " (not currently logged in)\n";
}
tmp = PLAYER_HANDLER->test_desc(name);
if (tmp && tmp != "") {
if(!this_player()->query_property(PLAYER_ALLOW_COLOURED_SOULS))
tmp = strip_colours(tmp);
sex = PLAYER_HANDLER->test_gender(name);
if (sex == 1) {
retval += "He " + tmp + "\n";
} else if (sex == 2) {
retval += "She " + tmp + "\n";
} else {
retval += "It " + tmp + "\n";
}
} else {
retval += "No description.\n";
}
//
// Do some magistrate and citizen stuff.
//
foreach(area in NOMIC_HANDLER->query_citizenship_areas()) {
if (NOMIC_HANDLER->is_magistrate_of(area, name)) {
retval += "Magistrate in " + area + ".\n";
} else if (NOMIC_HANDLER->is_citizen_of(area, name)) {
retval += "Citizen in " + area + ".\n";
}
}
rels = "/std/basic/club_listing"->player_relationships_string(name);
if (rels != "") {
retval += PLAYER_HANDLER->query_cap_name(name) + " " +
PLAYER_HANDLER->test_family(name) + " is the " + rels + "\n";
}
if(PLAYER_HANDLER->test_player_killer(name)) {
retval += PLAYER_HANDLER->query_cap_name(name) + " is a player killer.\n";
}
tmp = PLAYER_HANDLER->query_reference(name);
if(!this_player()->query_property(PLAYER_ALLOW_COLOURED_SOULS))
tmp = strip_colours(tmp);
if (tmp && tmp != "") {
retval += "\n" + tmp + "\n%^RESET%^";
if (strsrch(tmp, "%^") != -1) {
retval += "--- End of Reference --\n";
}
} else {
retval += "No reference.\n";
}
return retval;
}