#!/usr/bin/perl -w
# http://download.finance.yahoo.com/d/quotes.csv?f=snl1t1d1pghjkvxei&s=MSFT
# updated 2003-09-11 to use finance.yahoo.com instead (quote.* broke)
# or not; looks like they're using a load balancer of some sort

use Socket;

# stock host
$stock{'host'} = "download.finance.yahoo.com";
# stock port
$stock{'port'} = 80;
# this is the stock format string-- change your output function if you change
# this!
$stock{'format'} = "snl1t1d1pghjkvxei";
# this is the argument with the stock name-- we could figure it out, but
# this is faster.
$stock{'namearg'} = 1;
# path must end with s=, because the stock symbol is tacked onto the end
$stock{'path'} = "/d/quotes.csv?f=" . $stock{'format'} . "&s=";
# maximum number of failed stocks before bailing out
$stock{'maxbad'} = 2;

%remap = (
	"DJIA" => "^DJI",
	"PENIS" => "PENX",
	"NYSE" => "^TV.N",
	"NASDAQ" => "^IXIC",
	"NSDQ" => "^IXIC",
	"NASVOL" => "^TV.O",
	"S&P" => "^SPC",
	"3COM" => "COMS",
	"MP3" => "MPPP",
	"3M" => "MMM",
	"CLRX" => "CLX",
	"CLOR" => "CLX",
	"APPL" => "AAPL",
	"TYCO" => "TYC",
	"LEXM" => "LXK",
	"LEX" => "LXK",
);

sub get_stock_data
{
	my ($symbol) = @_;
	my ($addr, $proto, $sockaddr);

	if (!($addr = inet_aton($stock{'host'}))) {
		print "DNS resolution of " . $stock{'host'} . " failed!\n";
		exit;
	}
	$proto = getprotobyname("tcp");
	$sockaddr = sockaddr_in($stock{'port'}, $addr);
	socket(STOCK_SOCK, PF_INET, SOCK_STREAM, $proto) || return;
	connect(STOCK_SOCK, $sockaddr) || return;
	select(STOCK_SOCK);
	$| = 1;
	select(STDOUT);
	print STOCK_SOCK "GET " . $stock{'path'} . uc($symbol) . " HTTP/1.0\r\n";
	print STOCK_SOCK "Host: finance.yahoo.com\r\n";
	print STOCK_SOCK "User-Agent: stock.pl\r\n";
#	print STOCK_SOCK "Accept: text/plain,application/octet-stream;q=0.9,*/*\r\n";
	print STOCK_SOCK "Connection: close\r\n";
	print STOCK_SOCK "\r\n";

	while (!eof(STOCK_SOCK)) {
		$line = <STOCK_SOCK>;
		chomp($line);
		
		if ($line =~ 'HTTP/1\.[0-9]+ [345][0-9]+') {
			print "Unable to fetch data from Yahoo!  Server returned: $line\n";
			exit;
		}
	};
	close(STOCK_SOCK);

	$line =~ tr/\r\n//d;
	$x = 0;

	$_ = $line;
	undef @pieces;
	push(@pieces, defined($1) ? $1:$3)
		while m/"([^"\\]*(\\.[^"\\]*)*)"|([^,]+)/g;
	
	# @pieces = split(",", $line);
	
	foreach (@pieces) {
		$_ =~ s/^\"(.+)\"$/$1/;		# remove quotes
		$_ =~ s/\ *$//g;		# remove extra spaces
		if ($x == $stock{'namearg'}) {
			@words=split(" ", $_);
			$newname = "";
			foreach (@words) {
				$newname .= ucfirst(lc($_)) . " ";
			}
			$newname =~ s/\ *$//g;
			$_ = $newname;

			# expand some abbreviations
			$_ =~ s/\bCp\b/Corporation/g;
			$_ =~ s/\bCorp\b/Corporation/g;
			$_ =~ s/\bInc\b/Incorporated/g;
			$_ =~ s/\bIntl\b/International/g;
			$_ =~ s/\bAmer\b/America/g;
			$_ =~ s/\bDj\b/Dow\ Jones/g;
			$_ =~ s/\bIndu\b/Industrial/g;
			$_ =~ s/\bCl\b/Club/g;
			$_ =~ s/\bSystms\b/Systems/g;
			$_ =~ s/\bAdv\b/Advanced/g;
			$_ =~ s/\bIncm\b/Income/g;
			$_ =~ s/\bMgmt\b/Management/g;
			$_ =~ s/\bCdn\b/Canadian/g;
			$_ =~ s/\bComp\b/Computer/g;
			$_ =~ s/\bCo\b/Company/g;
			$_ =~ s,\bNas/nms\b,NASDAQ,g;
			$_ =~ s/\bLtd\b/Limited/g;
			$_ =~ s/\bComms\b/Communications/g;
			$_ =~ s/\bTech\b/Technologies/g;
			$_ =~ s/\bJapan Yen\b/Japanese Yen/g;
			$_ =~ s/\bCan Dollar\b/Canadian Dollar/g;
			$_ =~ s/\bMkt\b/Markets/g;
			$_ =~ s/\bAustr\b/Australian/g;
			$_ =~ s/\bPac\b/Pacific/g;
			$_ =~ s/\bIndx\b/Index/g;
			$_ =~ s/\bFin\b/Financial/g;
			$_ =~ s/\bRes\b/Resources/g;
			$_ =~ s/\bGen\b/General/g;
			$_ =~ s/\bEnterpr\b/Enterprises/g;
			$_ =~ s/\bGp\b/Group/g;
			$_ =~ s/\bVa Linux\b/VA Linux/g;
			$_ =~ s/\bSbc\b/SBC/g;
			$_ =~ s/\bEni Spa\b/ENI S.p.A./g;
			$_ =~ s/\bInt\'l\b/International/g;
			$_ =~ s/\bUs\b/US/g;
			$_ =~ s/\bHartfrd\b/Hartford/g;
			$_ =~ s/\bHi\b/High/g;
			$_ =~ s/\bYl\b/Yield/g;
			$_ =~ s/\bFinl\b/Financial/g;
			$_ =~ s/\bInter\b/Internet/g;
			$_ =~ s/\bSpart\b/Spartan/g;
			$_ =~ s/\bEq\b/Equity/g;
			$_ =~ s/\bIdx\b/Index/g;
			$_ =~ s/ S$/ Services/g;
			$_ =~ s/ I$/ Incorporated/g;
			$_ =~ s/\bWhlsl\b/Wholesale/g;
			$_ =~ s/\bFds\b/Funds/g;

			$_ =~ s/\bBristol Myers Sq\b/Bristol-Myers Squibb/g;
			$_ =~ s/\bSco\b/SCO/g;
			$_ =~ s/\bAt&t\b/AT\&T/g;
			$_ =~ s/I T T Education/ITT Educational/g;
			$_ =~ s/Procter Gamble/Procter \& Gamble/g;
			$_ =~ s/Bj's/BJ's/g;
			
# currency start
			$_ =~ s/\bAFA\b/Afghanistan Afghani/gi;
			$_ =~ s/\bALL\b/Albanian Lek/gi;
			$_ =~ s/\bDZD\b/Algerian Dinar/gi;
			$_ =~ s/\bARS\b/Argentine Peso/gi;
			$_ =~ s/\bAWG\b/Aruba Florin/gi;
			$_ =~ s/\bAUD\b/Australian Dollar/gi;
			$_ =~ s/\bBSD\b/Bahamian Dollar/gi;
			$_ =~ s/\bBHD\b/Bahraini Dinar/gi;
			$_ =~ s/\bBDT\b/Bangladesh Taka/gi;
			$_ =~ s/\bBBD\b/Barbados Dollar/gi;
			$_ =~ s/\bBYR\b/Belarus Ruble/gi;
			$_ =~ s/\bBZD\b/Belize Dollar/gi;
			$_ =~ s/\bBMD\b/Bermuda Dollar/gi;
			$_ =~ s/\bBTN\b/Bhutan Ngultrum/gi;
			$_ =~ s/\bBOB\b/Bolivian Boliviano/gi;
			$_ =~ s/\bBWP\b/Botswana Pula/gi;
			$_ =~ s/\bBRL\b/Brazilian Real/gi;
			$_ =~ s/\bGBP\b/British Pound/gi;
			$_ =~ s/\bBND\b/Brunei Dollar/gi;
			$_ =~ s/\bBGN\b/Bulgarian Lev/gi;
			$_ =~ s/\bBIF\b/Burundi Franc/gi;
			$_ =~ s/\bXOF\b/CFA Franc (BCEAO)/gi;
			$_ =~ s/\bXAF\b/CFA Franc (BEAC)/gi;
			$_ =~ s/\bKHR\b/Cambodia Riel/gi;
			$_ =~ s/\bCAD\b/Canadian Dollar/gi;
			$_ =~ s/\bCVE\b/Cape Verde Escudo/gi;
			$_ =~ s/\bKYD\b/Cayman Islands Dollar/gi;
			$_ =~ s/\bCLP\b/Chilean Peso/gi;
			$_ =~ s/\bCNY\b/Chinese Yuan/gi;
			$_ =~ s/\bCOP\b/Colombian Peso/gi;
			$_ =~ s/\bKMF\b/Comoros Franc/gi;
			$_ =~ s/\bCRC\b/Costa Rica Colon/gi;
			$_ =~ s/\bHRK\b/Croatian Kuna/gi;
			$_ =~ s/\bCUP\b/Cuban Peso/gi;
			$_ =~ s/\bCYP\b/Cyprus Pound/gi;
			$_ =~ s/\bCZK\b/Czech Koruna/gi;
			$_ =~ s/\bDKK\b/Danish Krone/gi;
			$_ =~ s/\bDJF\b/Dijibouti Franc/gi;
			$_ =~ s/\bDOP\b/Dominican Peso/gi;
			$_ =~ s/\bXCD\b/East Caribbean Dollar/gi;
			$_ =~ s/\bECS\b/Ecuador Sucre/gi;
			$_ =~ s/\bEGP\b/Egyptian Pound/gi;
			$_ =~ s/\bSVC\b/El Salvador Colon/gi;
			$_ =~ s/\bERN\b/Eritrea Nakfa/gi;
			$_ =~ s/\bEEK\b/Estonian Kroon/gi;
			$_ =~ s/\bETB\b/Ethiopian Birr/gi;
			$_ =~ s/\bEUR\b/Euro/gi;
			$_ =~ s/\bFKP\b/Falkland Islands Pound/gi;
			$_ =~ s/\bFJD\b/Fiji Dollar/gi;
			$_ =~ s/\bGMD\b/Gambian Dalasi/gi;
			$_ =~ s/\bGHC\b/Ghanian Cedi/gi;
			$_ =~ s/\bGIP\b/Gibraltar Pound/gi;
			$_ =~ s/\bXAU\b/Gold Ounces/gi;
			$_ =~ s/\bGTQ\b/Guatemala Quetzal/gi;
			$_ =~ s/\bGNF\b/Guinea Franc/gi;
			$_ =~ s/\bGYD\b/Guyana Dollar/gi;
			$_ =~ s/\bHTG\b/Haiti Gourde/gi;
			$_ =~ s/\bHNL\b/Honduras Lempira/gi;
			$_ =~ s/\bHKD\b/Hong Kong Dollar/gi;
			$_ =~ s/\bHUF\b/Hungarian Forint/gi;
			$_ =~ s/\bISK\b/Iceland Krona/gi;
			$_ =~ s/\bINR\b/Indian Rupee/gi;
			$_ =~ s/\bIDR\b/Indonesian Rupiah/gi;
			$_ =~ s/\bIRR\b/Iran Rial/gi;
			$_ =~ s/\bIQD\b/Iraqi Dinar/gi;
			$_ =~ s/\bILS\b/Israeli Shekel/gi;
			$_ =~ s/\bJMD\b/Jamaican Dollar/gi;
			$_ =~ s/\bJPY\b/Japanese Yen/gi;
			$_ =~ s/\bJOD\b/Jordanian Dinar/gi;
			$_ =~ s/\bKZT\b/Kazakhstan Tenge/gi;
			$_ =~ s/\bKES\b/Kenyan Shilling/gi;
			$_ =~ s/\bKRW\b/Korean Won/gi;
			$_ =~ s/\bKWD\b/Kuwaiti Dinar/gi;
			$_ =~ s/\bLAK\b/Lao Kip/gi;
			$_ =~ s/\bLVL\b/Latvian Lat/gi;
			$_ =~ s/\bLBP\b/Lebanese Pound/gi;
			$_ =~ s/\bLSL\b/Lesotho Loti/gi;
			$_ =~ s/\bLRD\b/Liberian Dollar/gi;
			$_ =~ s/\bLYD\b/Libyan Dinar/gi;
			$_ =~ s/\bLTL\b/Lithuanian Lita/gi;
			$_ =~ s/\bMOP\b/Macau Pataca/gi;
			$_ =~ s/\bMKD\b/Macedonian Denar/gi;
			$_ =~ s/\bMGF\b/Malagasy Franc/gi;
			$_ =~ s/\bMWK\b/Malawi Kwacha/gi;
			$_ =~ s/\bMYR\b/Malaysian Ringgit/gi;
			$_ =~ s/\bMVR\b/Maldives Rufiyaa/gi;
			$_ =~ s/\bMTL\b/Maltese Lira/gi;
			$_ =~ s/\bMRO\b/Mauritania Ougulya/gi;
			$_ =~ s/\bMUR\b/Mauritius Rupee/gi;
			$_ =~ s/\bMXN\b/Mexican Peso/gi;
			$_ =~ s/\bMDL\b/Moldovan Leu/gi;
			$_ =~ s/\bMNT\b/Mongolian Tugrik/gi;
			$_ =~ s/\bMAD\b/Moroccan Dirham/gi;
			$_ =~ s/\bMZM\b/Mozambique Metical/gi;
			$_ =~ s/\bMMK\b/Myanmar Kyat/gi;
			$_ =~ s/\bNAD\b/Namibian Dollar/gi;
			$_ =~ s/\bNPR\b/Nepalese Rupee/gi;
			$_ =~ s/\bANG\b/Neth Antilles Guilder/gi;
			$_ =~ s/\bNZD\b/New Zealand Dollar/gi;
			$_ =~ s/\bNIO\b/Nicaragua Cordoba/gi;
			$_ =~ s/\bNGN\b/Nigerian Naira/gi;
			$_ =~ s/\bKPW\b/North Korean Won/gi;
			$_ =~ s/\bNOK\b/Norwegian Krone/gi;
			$_ =~ s/\bOMR\b/Omani Rial/gi;
			$_ =~ s/\bXPF\b/Pacific Franc/gi;
			$_ =~ s/\bPKR\b/Pakistani Rupee/gi;
			$_ =~ s/\bXPD\b/Palladium Ounces/gi;
			$_ =~ s/\bPAB\b/Panama Balboa/gi;
			$_ =~ s/\bPGK\b/Papua New Guinea Kina/gi;
			$_ =~ s/\bPYG\b/Paraguayan Guarani/gi;
			$_ =~ s/\bPEN\b/Peruvian Nuevo Sol/gi;
			$_ =~ s/\bPHP\b/Philippine Peso/gi;
			$_ =~ s/\bXPT\b/Platinum Ounces/gi;
			$_ =~ s/\bPLN\b/Polish Zloty/gi;
			$_ =~ s/\bQAR\b/Qatar Rial/gi;
			$_ =~ s/\bSDP\b/Reuters/gi;
			$_ =~ s/\bROL\b/Romanian Leu/gi;
			$_ =~ s/\bRUB\b/Russian Rouble/gi;
			$_ =~ s/\bRWF\b/Rwanda Franc/gi;
			$_ =~ s/\bWST\b/Samoa Tala/gi;
			$_ =~ s/\bSTD\b/Sao Tome Dobra/gi;
			$_ =~ s/\bSAR\b/Saudi Arabian Riyal/gi;
			$_ =~ s/\bSCR\b/Seychelles Rupee/gi;
			$_ =~ s/\bSLL\b/Sierra Leone Leone/gi;
			$_ =~ s/\bXAG\b/Silver Ounces/gi;
			$_ =~ s/\bSGD\b/Singapore Dollar/gi;
			$_ =~ s/\bSKK\b/Slovak Koruna/gi;
			$_ =~ s/\bSIT\b/Slovenian Tolar/gi;
			$_ =~ s/\bSBD\b/Solomon Islands Dollar/gi;
			$_ =~ s/\bSOS\b/Somali Shilling/gi;
			$_ =~ s/\bZAR\b/South African Rand/gi;
			$_ =~ s/\bLKR\b/Sri Lanka Rupee/gi;
			$_ =~ s/\bSHP\b/St Helena Pound/gi;
			$_ =~ s/\bSDD\b/Sudanese Dinar/gi;
			$_ =~ s/\bSRG\b/Surinam Guilder/gi;
			$_ =~ s/\bSZL\b/Swaziland Lilageni/gi;
			$_ =~ s/\bSEK\b/Swedish Krona/gi;
			$_ =~ s/\bCHF\b/Swiss Franc/gi;
			$_ =~ s/\bSYP\b/Syrian Pound/gi;
			$_ =~ s/\bTWD\b/Taiwan Dollar/gi;
			$_ =~ s/\bTZS\b/Tanzanian Shilling/gi;
			$_ =~ s/\bTHB\b/Thai Baht/gi;
			$_ =~ s/\bTOP\b/Tonga Pa'anga/gi;
			$_ =~ s/\bTTD\b/Trinidad&Tobago Dollar/gi;
			$_ =~ s/\bTND\b/Tunisian Dinar/gi;
			$_ =~ s/\bTRL\b/Turkish Lira/gi;
			$_ =~ s/\bUSD\b/U.S. Dollar/gi;
			$_ =~ s/\bAED\b/UAE Dirham/gi;
			$_ =~ s/\bUGX\b/Ugandan Shilling/gi;
			$_ =~ s/\bUAH\b/Ukraine Hryvnia/gi;
			$_ =~ s/\bUYU\b/Uruguayan New Peso/gi;
			$_ =~ s/\bVUV\b/Vanuatu Vatu/gi;
			$_ =~ s/\bVEB\b/Venezuelan Bolivar/gi;
			$_ =~ s/\bVND\b/Vietnam Dong/gi;
			$_ =~ s/\bYER\b/Yemen Riyal/gi;
			$_ =~ s/\bYUM\b/Yugoslav Dinar/gi;
			$_ =~ s/\bZMK\b/Zambian Kwacha/gi;
			$_ =~ s/\bZWD\b/Zimbabwe Dollar/gi;
# currency end

		}
		$data[$x] = $_;
		$x++;
	}
	return @data;
}
sub calc_gcd
{
	my ($a, $b) = @_;

	$origa = $a;
	$origb = $b;
	$recurse = 0;

	while ($a != $b) {
		$recurse++;
		if ($recurse > 1500000) {
			print "FATAL $origa $origb $a $b\n";
			exit;
		}
		if ($a > $b) {
			$a = int($a) - int($b);
		} else {
			$b = int($b) - int($a);
		}
	}
	return $a;
}
sub to_fraction
{
	my ($decimal) = @_;

	return sprintf("%.4f", $decimal);
	# the market has completed the move to decimal

	return 0 if ($decimal == 0.0);

	$num = 1;
	$neg = 0;

	if (int($decimal) < 0) {
		$neg = 1;
		$decimal = (-$decimal);
	}

	$whole = int($decimal);
	$decimal -= $whole;

	return $whole if ($decimal == 0);

	$decimal = sprintf("%.10f", $decimal);

	while ($decimal ne int($decimal)) {
		$num *= 10;
		$decimal *= 10;
	}
	$gcd = calc_gcd($decimal, $num);
	$n = $decimal / $gcd;
	$d = $num / $gcd;

	return ($neg ? "-" : "") . "$whole" . ($n ? " $n/$d" : "");
}

sub show_stock_data
{
	my ($mode, @data) = @_;

	$diff = $data[2] - $data[5];

	if ($diff == 0) {
		$dir = ($mode == 1 ? "=" : "unchanged");
	} elsif ($diff < 0) {
		$dir = ($mode == 1 ? "-" : "down");
		$diff = -$diff;
	} elsif ($diff > 0) {
		$dir = ($mode == 1 ? "+" : "up");
	}
	if ($mode == 0) {
		print "<stock/$data[0]> $data[1]: " . to_fraction($data[2]) . " ($dir " . to_fraction($diff) . ", " . sprintf("%.2f", $diff/($data[5]+0.000001)*100) . "%) at $data[4] $data[3]\n";
	} elsif ($mode == 1) {
		print "<stock/$data[0]> " . to_fraction($data[2]) . " $dir" . to_fraction($diff) . " (" . sprintf("%.2f", $diff/($data[5]+0.000001)*100) . "%) @ $data[4] $data[3]\n";
	} elsif ($mode == 2) {
		print "<stock/$data[0]> $data[1] [$data[11]]: " . to_fraction($data[2]) . " ($dir " . to_fraction($diff) . ", " . sprintf("%.2f", $diff/($data[5]+0.000001)*100) . "%) at $data[4] $data[3].  52-week range: " . to_fraction($data[8]) . " - " . to_fraction($data[9]) . ".  Daily range: " . to_fraction($data[6]) . " - " . to_fraction($data[7]) . ".  52-week earnings/share: \$" . sprintf("%.2f", $data[12]) . "\n";
	}
}

sub show_currency_data
{
	my ($mode, @data) = @_;

	$diff = $data[2] - $data[5];

	if ($diff == 0) {
		$dir = ($mode == 1 ? "=" : "unchanged");
	} elsif ($diff < 0) {
		$dir = ($mode == 1 ? "-" : "down");
		$diff = -$diff;
	} elsif ($diff > 0) {
		$dir = ($mode == 1 ? "+" : "up");
	}
	if ($mode == 0) {
		print "<currency/$data[0]> $data[1]: " . to_fraction($data[2]) . " ($dir " . to_fraction($diff) . ", " . sprintf("%.2f", $diff/($data[5]+0.000001)*100) . "%) at $data[4] $data[3]\n";
	} elsif ($mode == 1) {
		print "<currency/$data[0]> " . to_fraction($data[2]) . " $dir" . to_fraction($diff) . " (" . sprintf("%.2f", $diff/($data[5]+0.000001)*100) . "%) @ $data[4] $data[3]\n";
	} elsif ($mode == 2) {
		print "<currency/$data[0]> $data[1] [$data[11]]: " . to_fraction($data[2]) . " ($dir " . to_fraction($diff) . ", " . sprintf("%.2f", $diff/($data[5]+0.000001)*100) . "%) at $data[4] $data[3].  52-week range: " . to_fraction($data[8]) . " - " . to_fraction($data[9]) . ".  Daily range: " . to_fraction($data[6]) . " - " . to_fraction($data[7]) . ".\n";
	}
}

sub show_exchange_data
{
	my ($mode, @data) = @_;

	if ($mode == 0 || $mode == 2) {
		print "<exchange/$data[0]> $data[1]: $data[2] at $data[4] $data[3]\n";
	} elsif ($mode == 1) {
		print "<exchange/$data[0]> $data[2] @ $data[4] $data[3]\n";
	}
}

sub show_mutual_data
{
	my ($mode, @data) = @_;

	$diff = $data[2] - $data[5];

	if ($diff == 0) {
		$dir = ($mode == 1 ? "=" : "unchanged");
	} elsif ($diff < 0) {
		$dir = ($mode == 1 ? "-" : "down");
		$diff = -$diff;
	} elsif ($diff > 0) {
		$dir = ($mode == 1 ? "+" : "up");
	}
	if ($mode == 0 || $mode == 2) {
		print "<mutual/$data[0]> $data[1]: " . sprintf("%.2f", $data[2]) . " ($dir " . sprintf("%.2f", $diff) . ", " . sprintf("%.2f", $diff/$data[5]*100) . "%) at $data[4] $data[3].\n";
	} elsif ($mode == 1) {
		print "<mutual/$data[0]> " . sprintf("%.2f", $data[2]) . "$data[2] $dir" . sprintf("%.2f", $diff) . "$diff (" . sprintf("%.2f", $diff/$data[5]*100) . "%) @ $data[4] $data[3].\n";
	}
}

sub show_volume_data
{
	my ($mode, @data) = @_;

	$diff = $data[2] - $data[5];

	if ($diff == 0) {
		$dir = ($mode == 1 ? "=" : "unchanged");
	} elsif ($diff < 0) {
		$dir = ($mode == 1 ? "-" : "down");
		$diff = -$diff;
	} elsif ($diff > 0) {
		$dir = ($mode == 1 ? "+" : "up");
	}
	if ($mode == 0) {
		print "<volume/$data[0]> $data[1]: " . to_fraction($data[2]) . " ($dir " . to_fraction($diff) . ", " . sprintf("%.2f", $diff/$data[5]*100) . "%) at $data[4] $data[3].\n";
	} elsif ($mode == 1) {
		print "<volume/$data[0]> " . to_fraction($data[2]) . " $dir" . to_fraction($diff) . " (" . sprintf("%.2f", $diff/$data[5]*100) . "%) @ $data[4] $data[3].\n";
	} elsif ($mode == 2) {
		print "<volume/$data[0]> $data[1] [$data[11]]: " . to_fraction($data[2]) . " ($dir " . to_fraction($diff) . ", " . sprintf("%.2f", $diff/$data[5]*100) . "%) at $data[4] $data[3].  52-week range: " . to_fraction($data[8]) . " - " . to_fraction($data[9]) . "" . ($data[6] ? ".  Daily range: " . to_fraction($data[6]) . " - " . to_fraction($data[7]) : "") . ".\n";
	}
}

sub show_classic
{
	my (@data) = @_;

	$diff = $data[2] - $data[5];

	if ($diff == 0) {
		$dir = "unchanged";
	} elsif ($diff < 0) {
		$dir = "DOWN";
	} elsif ($diff > 0) {
		$dir = "UP";
	}
	print "[stock] $data[1] ($data[0]) is $dir: " . ($dir eq "UP" ? "+" : "") . to_fraction($diff) . " (" . ($dir eq "UP" ? "+" : "") . sprintf("%.2f", $diff/$data[5]*100) . "%) to " . to_fraction($data[2]) . ", at $data[3] [$data[4]].\n";
}

# main code
$bad = 0;
$mode = 0;

foreach $symbol (@ARGV) {
	if ($symbol eq "-n") {
		$mode = 0; # normal
		next;
	}
	if ($symbol eq "-b" || $symbol eq "-t") {
		$mode = 1; # terse
		next;
	}
	if ($symbol eq "-f" || $symbol eq "-v") {
		$mode = 2; # verbose
		next;
	}
	if ($symbol eq "-c") {
		$mode = 3; # classic
		next;
	}
	if ($symbol eq "-h") {
		print "Options: -n = normal,  -t = terse, -v = verbose\n";
		print "         -c = classic, -h = help\n";
		next;
	}
	$sym = uc($symbol);
	while (($from, $to) = each %remap) {
		if (lc($from) eq lc($sym)) {
			$sym = $to;
			last;
		}
	}

	if ((@ret = get_stock_data($sym)) == 0) {
		print "<error/$sym> $!\n";
		$bad++;
		next;
	}
	if ($ret[2] eq "0.00") {
		print "<stock/$sym> Symbol does not exist\n";
		$bad++;
		next;
	}
	if ($bad >= ($stock{'maxbad'} + 1)) {
		print "<error/$sym> Too many failed stocks, giving up...\n";
		exit;
	}

	if ($mode == 3) {
		show_classic(@ret);
	}
#	print "$ret[0] !! $ret[1] !! $ret[2] !! $ret[3] !! $ret[4] !! $ret[5] !! $ret[6] !! $ret[7] !! $ret[8] !! $ret[9] !! $ret[10]\n";

	if ($ret[5] eq "N/A") {
		show_exchange_data($mode, @ret);
	} elsif ($ret[8] eq "N/A") {
		show_mutual_data($mode, @ret);
	} elsif ($ret[10] eq "N/A") {
		show_volume_data($mode, @ret);
	} elsif ($ret[12] eq "N/A") {
		show_currency_data($mode, @ret);
	} else {
		show_stock_data($mode, @ret);
	}
}
