code: purgatorio

ref: 82a3f55c5fb7b9f7a82449e4eb943c535ec3e491
dir: /appl/cmd/cal.b/

View raw version
implement Cal;

#
# Copyright © 1995-2002 Lucent Technologies Inc.  All rights reserved.
# Limbo transliteration 2003 by Vita Nuova
# This software is subject to the Plan 9 Open Source Licence.
#

include "sys.m";
	sys: Sys;

include "draw.m";

include "bufio.m";
	bufio: Bufio;
	Iobuf: import bufio;

include "daytime.m";
	daytime: Daytime;
	Tm: import daytime;

Cal: module
{
	init:	fn(nil: ref Draw->Context, nil: list of string);
};

dayw :=	" S  M Tu  W Th  F  S";
smon := array[] of {
	"January", "February", "March", "April",
	"May", "June", "July", "August",
	"September", "October", "November", "December",
};

mon := array[] of {
	0,
	31, 29, 31, 30,
	31, 30, 31, 31,
	30, 31, 30, 31,
};

bout: ref Iobuf;

init(nil: ref Draw->Context, args: list of string)
{
	y, m: int;

	sys = load Sys Sys->PATH;
	bufio = load Bufio Bufio->PATH;
	daytime = load Daytime Daytime->PATH;

	argc := len args;
	if(argc > 3){
		sys->fprint(sys->fildes(2), "usage: cal [month] [year]\n");
		raise "fail:usage";
	}
	bout = bufio->fopen(sys->fildes(1), Bufio->OWRITE);

#
# no arg, print current month
#
	if(argc <= 1) {
		m = curmo();
		y = curyr();
		return xshort(m, y);
	}
	args = tl args;

#
# one arg
#	if looks like a month, print month
#	else print year
#
	if(argc == 2) {
		y = number(hd args);
		if(y < 0)
			y = -y;
		if(y >= 1 && y <= 12)
			return xshort(y, curyr());
		return xlong(y);
	}

#
# two arg, month and year
#
	m = number(hd args);
	if(m < 0)
		m = -m;
	y = number(hd tl args);
	return xshort(m, y);
}

#
#	print out just month
#
xshort(m: int, y: int)
{
	if(m < 1 || m > 12)
		badarg();
	if(y < 1 || y > 9999)
		badarg();
	bout.puts(sys->sprint("   %s %ud\n", smon[m-1], y));
	bout.puts(sys->sprint("%s\n", dayw));
	lines := cal(m, y);
	for(i := 0; i < len lines; i++){
		bout.puts(lines[i]);
		bout.putc('\n');
	}
	bout.flush();
}

#
#	print out complete year
#
xlong(y: int)
{
	if(y<1 || y>9999)
		badarg();
	bout.puts("\n\n\n");
	bout.puts(sys->sprint("                                %ud\n", y));
	bout.putc('\n');
	months := array[3] of array of string;
	for(i:=0; i<12; i+=3) {
		bout.puts(sys->sprint("         %.3s", smon[i]));
		bout.puts(sys->sprint("                    %.3s", smon[i+1]));
		bout.puts(sys->sprint("                    %.3s\n", smon[i+2]));
		bout.puts(sys->sprint("%s   %s   %s\n", dayw, dayw, dayw));
		for(j := 0; j < 3; j++)
			months[j] = cal(i+j+1, y);
		for(l := 0; l < 6; l++){
			s := "";
			for(j = 0; j < 3; j++)
				s += sys->sprint("%-20.20s   ", months[j][l]);
			for(j = len s; j > 0 && s[j-1] == ' ';)
				j--;
			bout.puts(s[0:j]);
			bout.putc('\n');
		}
	}
	bout.flush();
}

badarg()
{
	sys->fprint(sys->fildes(2), "cal: bad argument\n");
	raise "fail:bad argument";
}

dict := array[] of {
	("january",	1),
	("february",	2),
	("march",	3),
	("april",	4),
	("may",		5),
	("june",		6),
	("july",		7),
	("august",	8),
	("sept",		9),
	("september",	9),
	("october",	10),
	("november",	11),
	("december",	12),
};

#
# convert to a number.
# if its a dictionary word,
# return negative  number
#
number(s: string): int
{
	if(len s >= 3){
		for(n:=0; n < len dict; n++){
			(word, val) := dict[n];
			if(s == word || s == word[0:3])
				return -val;
		}
	}
	n := 0;
	for(i := 0; i < len s; i++){
		c := s[i];
		if(c<'0' || c>'9')
			badarg();
		n = n*10 + c-'0';
	}
	return n;
}

pstr(str: string, n: int)
{
	bout.puts(sys->sprint("%-*.*s\n", n, n, str));
}

cal(m: int, y: int): array of string
{
	d := jan1(y);
	mon[9] = 30;

	case (jan1(y+1)+7-d)%7 {

	#
	#	non-leap year
	#
	1 =>
		mon[2] = 28;

	#
	#	leap year
	#
	2 =>
		mon[2] = 29;

	#
	#	1752
	#
	* =>
		mon[2] = 29;
		mon[9] = 19;
	}
	for(i:=1; i<m; i++)
		d += mon[i];
	d %= 7;
	lines := array[6] of string;
	l := 0;
	s := "";
	for(i = 0; i < d; i++)
		s += "   ";
	for(i=1; i<=mon[m]; i++) {
		if(i==3 && mon[m]==19) {
			i += 11;
			mon[m] += 11;
		}
		s += sys->sprint("%2d", i);
		if(++d == 7) {
			d = 0;
			lines[l++] = s;
			s = "";
		}else
			s[len s] = ' ';
	}
	if(s != nil){
		while(s[len s-1] == ' ')
			s = s[:len s-1];
		lines[l] = s;
	}
	return lines;
}

#
#	return day of the week
#	of jan 1 of given year
#
jan1(y: int): int
{
#
#	normal gregorian calendar
#	one extra day per four years
#

	d := 4+y+(y+3)/4;

#
#	julian calendar
#	regular gregorian
#	less three days per 400
#

	if(y > 1800) {
		d -= (y-1701)/100;
		d += (y-1601)/400;
	}

#
#	great calendar changeover instant
#

	if(y > 1752)
		d += 3;

	return d%7;
}

#
# get current month and year
#
curmo(): int
{
	tm := daytime->local(daytime->now());
	return tm.mon+1;
}

curyr(): int
{
	tm := daytime->local(daytime->now());
	return tm.year+1900;
}