#!@@MHC_RUBY_PATH@@ -Ke # -*- ruby -*- ## today ## ## Author: Yoshinari Nomura ## ## Created: 1999/11/11 ## Revised: 2004/01/12 10:24:37 ## ## today was originally written in perl. ## This is a ruby version of it. Original authors were ## ## Yoshinari Nomura ## OHARA Shigeki ## $DEBUG2 = false MailServer = 'localhost' ## for --mail option MyHostName = 'localhost' ## for --mail option require 'mhc-schedule' require 'mhc-kconv' require 'net/smtp' class String # digest a string upto max size. # when the bound is unsafe for muti-bytes, replace the character with '$' def digest (max, tail_adjust = '$', fill_up = nil) euc = (Kconv .toeuc self)[0..max-1] if fill_up and euc .size < max return euc + fill_up * (max - euc .size) end is2byte = false euc .each_byte do |char| is2byte = ! is2byte if char & 0x80 == 0x80 end if is2byte euc[max-1] = tail_adjust end return euc end end def usage(do_exit = true) STDERR .print "usage: today [options] Show your today's schedules. --help show this message. --format=FORMAT change output format to one of: ps, html --category=CATEGORY pick only in CATEGORY. '!' and space separated multiple values are allowed. --date=strig[+n] set a period of date. string is one of these: today, tomorrow, sun ... sat, yyyymmdd, yyyymm yyyymm lists all days in the month. list n+1 days of schedules if +n is given. default value is 'today+0' --mail=ADDRESS send a e-mail to ADDRESS instead of listing to stdout.\n" exit if do_exit end def string_to_date(string, range) date_from = nil date_to = nil case (string .downcase) when 'today' date_from = MhcDate .new when 'tomorrow' date_from = MhcDate .new .succ when /^(sun|mon|tue|wed|thu|fri|sat)/ date_from = MhcDate .new .w_this(string .downcase) when /^\d{8}$/ date_from = MhcDate .new(string) when /^\d{6}$/ date_from = MhcDate .new(string + '01') if range date_to = date_from .succ(range .to_i) else date_to = MhcDate .new(string + format("%02d", date_from .m_days)) end else return nil end date_to = date_from .succ((range || '0') .to_i) if !date_to return [date_from, date_to] end def get_schedule(db, from, to, category, formatter, hook = nil) ret = '' db .search(from, to, category) .each{|date, items| ret += formatter .call(date, items) } if hook return hook .call(db, from, to, category, ret) else return ret end end ################################################################ ## formatter formatter_normal = Proc .new{|date, items| ret = '' heading = format("%02d/%02d %s ", date .m, date .d, date .w_s) first = true items .each{|sch| heading = heading .gsub(/./, ' ') if !first ret += heading + format("%-11s %s%s\n", sch .time_as_string, sch .subject, if sch .location and sch .location != '' ' [' + sch .location + ']' else '' end) first = false } MhcKconv::todisp(ret) } formatter_ps = Proc .new{|date, items| ret = '' heading = format("%d", date .d) items .each{|sch| if sch .in_category?('Holiday') ret += '__HOLIDAY__' end ret += heading + format(" (%-5s %s)__XXX__", sch .time_b .to_s, sch .subject .digest(15) .gsub(/([()])/, '\\\\\1')) } MhcKconv::tops(ret) } formatter_html = Proc .new{|date, items| buffer_max, buffer = 30, '' week = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][date .w] items2 = [] items .each do |schedule| time = schedule .time_b time &&= "#{time} " items2 .push Kconv .toeuc("#{time}#{schedule .subject}") end if (string = items2 .join(", ")) .size > buffer_max string = items2 .collect {|item| item[0,10]} .join(", ") end buffer += format("%02d/%02d(%s)", date .m, date .d, week) + " " buffer += string .digest(buffer_max) + "
\n" MhcKconv::tohtml(buffer) } ################################################################ ## hook hook_ps = Proc .new {|db, from, to, category, ret| ps_contents = open(__FILE__) .gets(nil) .split("__END__\n")[1] landscape = true # xxx holiday = '' schedule = '' ret .split('__XXX__') .each{|item| if item =~ /^__HOLIDAY__(.*)/ holiday += $1 else schedule += item end } trans_table = { '@MONTH@' => from .m .to_s, '@YEAR@' => from .y .to_s, '@TFONT@' => 'Times-Bold', '@DFONT@' => 'Helvetica-Bold', '@EFONT@' => 'Times-Roman', '@JFONT@' => 'Ryumin-Light-EUC-H', '@HOLIDAYS@' => holiday, '@SCHEDULES@' => schedule, '@BANNER@' => '', '@LFOOT@' => '', '@RFOOT@' => '', '@CFOOT@' => '', '@SCALE@' => landscape ? '1.0 1.0' : '0.75 0.75', '@ROTATE@' => landscape ? '90' : '0', '@TRANSLATE@' => landscape ? '50 -120' : '50 900' } trans_table .each{|key,val| ps_contents .gsub!(key, val) } ps_contents } ################################################################ ## main date_from = date_to = MhcDate .new formatter = formatter_normal hook_proc = nil while option = ARGV .shift case (option) when /^--category=(.+)/ category = $1 when /^--format=(.+)/ case $1 when 'html' formatter = formatter_html when 'ps' formatter = formatter_ps hook_proc = hook_ps date_from = date_from .m_first_day date_to = date_from .m_last_day else formatter = formatter_normal end when /^--date=([^+]+)(\+(-?[\d]+))?/ date_from, date_to = string_to_date($1, $3) || usage() when /^--mail=(.+)/ mail_address = $1 else usage() end end user_name = ENV['USERNAME'] || ENV['USER'] || ENV['LOGNAME'] || mail_address print "date_from: #{date_from .to_s}\n" if $DEBUG2 print "date_to: #{date_to .to_s}\n" if $DEBUG2 print "e-mail: #{mail_address}\n" if $DEBUG2 db = MhcScheduleDB .new if mail_address header = "To: #{mail_address}\n" header += "From: secretary-of-#{mail_address}\n" header += "Subject: Today's schedule (#{date_from .to_s})\n" header += "\n" header += "#{user_name}'s schedule: \n\n" contents = get_schedule(db, date_from, date_to, category, formatter, hook_proc) if contents && contents != '' message = MhcKconv::tomail(header + contents) Net::SMTPSession .start(MailServer, 25, MyHostName) {|server| server .sendmail(message, mail_address, [mail_address]) } end else print get_schedule(db, date_from, date_to, category, formatter, hook_proc) end ################################################################ # postscript code from pscalj.sh ################################################################ __END__ %! % PostScript program to draw calendar % Copyright (C) 1987 by Pipeline Associates, Inc. % Permission is granted to modify and distribute this free of charge. % The number after /month should be set to a number from 1 to 12. % The number after /year should be set to the year you want. % You can change the title and date fonts, if you want. % We figure out the rest. % This program won't produce valid calendars before 1800 due to the switch % from Julian to Gregorian calendars in September of 1752 wherever English % was spoken. %% For Japanese. Added by ichimal, 2000/2/6. %% Original code is generated by k2ps. /copyfont { % font-dic extra-entry-count copyfont font-dic 1 index maxlength add dict begin { 1 index /FID ne 2 index /UniqueID ne and {def}{pop pop} ifelse } forall currentdict end } bind def %% For Japanese. Added by ichimal, 2000/2/6. %% Original code is generated by k2ps. /narrowfont { % ASCIIFontName EUCFontName compositefont font' findfont dup /FontType get 0 eq { 12 dict begin % % 7+8 bit EUC font % 12 dict begin /EUCFont exch def /FontInfo (7+8 bit EUC font) readonly def /PaintType 0 def /FontType 0 def /FontMatrix matrix def % /FontName /Encoding [ 16#00 1 16#20 { pop 0 } for 16#21 1 16#28 { 16#20 sub } for 16#29 1 16#2F { pop 0 } for 16#30 1 16#74 { 16#27 sub } for 16#75 1 16#FF { pop 0 } for ] def /FMapType 2 def EUCFont /WMode known { EUCFont /WMode get /WMode exch def } { /WMode 0 def } ifelse /FDepVector [ EUCFont /FDepVector get 0 get [ 16#21 1 16#28 {} for 16#30 1 16#74 {} for ] { 13 dict begin /EUCFont EUCFont def /UpperByte exch 16#80 add def % /FontName /FontInfo (EUC lower byte font) readonly def /PaintType 0 def /FontType 3 def /FontMatrix matrix def /FontBBox {0 0 0 0} def /Encoding [ 16#00 1 16#A0 { pop /.notdef } for 16#A1 1 16#FE { 16#80 sub 16 2 string cvrs (cXX) dup 1 4 -1 roll putinterval cvn } for /.notdef ] def % /UniqueID % /WMode /BuildChar { gsave exch dup /EUCFont get setfont /UpperByte get 2 string dup 0 4 -1 roll put dup 1 4 -1 roll put dup stringwidth setcharwidth 0 0 moveto show grestore } bind def currentdict end /lowerbytefont exch definefont } forall ] def currentdict end /eucfont exch definefont exch findfont 1 copyfont dup begin /FontMatrix FontMatrix [.83 0 0 1 0 0.05] matrix concatmatrix def end /asciifont exch definefont exch /FDepVector [ 4 2 roll ] def /FontType 0 def /WMode 0 def /FMapType 4 def /FontMatrix matrix def /Encoding [0 1] def /FontBBox {0 0 0 0} def currentdict end }{ pop findfont 0 copyfont } ifelse } def /month @MONTH@ def /year @YEAR@ def /titlefont /@TFONT@ def /dayfont /@DFONT@ def %% For Japanese. Changed by ichimal, 2000/2/6. %% Original code is generated by k2ps. % /eventfont /@EFONT@ def /Courier-Ryumin /Courier /@JFONT@ narrowfont definefont pop /eventfont /Courier-Ryumin def /holidays [ @HOLIDAYS@ ] def /schedules [ @SCHEDULES@ ] def /Bannerstring (@BANNER@) def /Lfootstring (@LFOOT@) def /Rfootstring (@RFOOT@) def /Cfootstring (@CFOOT@) def % calendar names - change these if you don't speak english % "August", "April" and "February" could stand to be kerned even if you do /month_names [ (January) (February) (March) (April) (May) (June) (July) (August) (September) (October) (November) (December) ] def /day_names [ (Sunday) (Monday) (Tuesday) (Wednesday) (Thursday) (Friday) (Saturday) ] def % layout parameters - you can change these, but things may not look nice /daywidth 100 def /dayheight 95 def /titlefontsize 48 def /weekdayfontsize 10 def /datefontsize 30 def /footfontsize 20 def /topgridmarg 35 def /leftmarg 35 def /daytopmarg 10 def /dayleftmarg 5 def % layout constants - don't change these, things probably won't work /rows 5 def /subrows 6 def % calendar constants - change these if you want a French revolutionary calendar /days_week 7 def /days_month [ 31 28 31 30 31 30 31 31 30 31 30 31 ] def /isleap { % is this a leap year? year 4 mod 0 eq % multiple of 4 year 100 mod 0 ne % not century year 1000 mod 0 eq or and % unless it's a millenia } def /ndays { % number of days in this month days_month month 1 sub get month 2 eq % February isleap and { 1 add } if } def /weekday { % weekday (range 0-6) for integer date days_week mod } def /startday { % starting day-of-week for this month /off year 2032 sub def % offset from start of "epoch" off off 4 idiv add % number of leap years off 100 idiv sub % number of centuries off 1000 idiv add % number of millenia 4 add weekday days_week add % offset from Jan 1 2032 /off exch def 1 1 month 1 sub { /idx exch def days_month idx 1 sub get idx 2 eq isleap and { 1 add } if /off exch off add def } for off weekday % 0--Sunday, 1--monday, etc. } def /prtevent { % event-string day prtevent % print out an event /start startday def /day 2 1 roll def day start add 1 sub 7 mod daywidth mul day start add 1 sub 7 div truncate dayheight neg mul -5 numevents day start add get -10 mul add numevents day start add numevents day start add get 1 add put add moveto show } def /drawevents { % read in a file full of events; print % the events for this month /numevents [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] def eventfont findfont 9 scalefont setfont 0 2 holidays length 2 sub { % for the "Holidays" dup 1 add holidays 2 1 roll get 2 1 roll holidays 2 1 roll get prtevent } for 0 2 schedules length 2 sub { % for the "Schedules" dup 1 add schedules 2 1 roll get 2 1 roll schedules 2 1 roll get prtevent } for } def % ------------------------------------------------------------------------ /prtnum { 3 string cvs show } def /center { % center string in given width /width exch def /str exch def width str stringwidth pop sub 2 div 0 rmoveto str show } def /centernum { exch 3 string cvs exch center } def /drawgrid { % draw calendar boxes titlefont findfont weekdayfontsize scalefont setfont currentpoint /y0 exch def /x0 exch def 0 1 days_week 1 sub { submonth 0 eq { x0 y0 moveto dup dup daywidth mul 40 rmoveto day_names exch get daywidth center } if x0 y0 moveto daywidth mul topgridmarg rmoveto 1.0 setlinewidth submonth 0 eq { /rowsused rows 1 sub def } { /rowsused rows def } ifelse 0 1 rowsused { gsave daywidth 0 rlineto 0 dayheight neg rlineto daywidth neg 0 rlineto closepath stroke grestore 0 dayheight neg rmoveto } for } for } def /drawnums { % place day numbers on calendar dayfont findfont datefontsize submonth 0 ne { 2.5 mul } if scalefont setfont /start startday def /days ndays def start daywidth mul dayleftmarg add daytopmarg rmoveto submonth 0 ne { dayleftmarg neg dayheight -2 div rmoveto } if 1 1 days { /day exch def gsave day start add weekday 0 eq { submonth 0 eq { .7 setgray } if } if day start add weekday 1 eq { submonth 0 eq { .7 setgray } if } if %% Added by ichimal, 2000.2 submonth 0 eq { 0 2 holidays length 2 sub { holidays 2 1 roll get day eq { .7 setgray exit } if } for } if submonth 0 eq { isdouble { day prtdouble } { day prtnum } ifelse } { day daywidth centernum } ifelse grestore day start add weekday 0 eq { currentpoint exch pop dayheight sub 0 exch moveto submonth 0 eq { dayleftmarg 0 rmoveto } if } { daywidth 0 rmoveto } ifelse } for } def /isdouble { % overlay today with next/last week? days start add rows days_week mul gt { day start add rows days_week mul gt { true true } { day start add rows 1 sub days_week mul gt day days_week add days le and { false true } { false } ifelse } ifelse } { false } ifelse } def /prtdouble { gsave dayfont findfont datefontsize 2 mul 3 div scalefont setfont exch { (23/) stringwidth pop dayheight rmoveto prtnum } { 0 datefontsize 5 div rmoveto prtnum 0 datefontsize -5 div rmoveto gsave dayfont findfont datefontsize scalefont setfont (/) show grestore } ifelse grestore } def /drawfill { % place fill squares on calendar /start startday def /days ndays def currentpoint /y0 exch def /x0 exch def submonth 0 eq { usefirst { /fillstart 2 def } { /fillstart 0 def } ifelse } { /fillstart 0 def } ifelse fillstart daywidth mul topgridmarg rmoveto 1.0 setlinewidth fillstart 1 start 1 sub { gsave .9 setgray daywidth 0 rlineto 0 dayheight neg rlineto daywidth neg 0 rlineto closepath fill grestore daywidth 0 rmoveto } for x0 y0 moveto submonth 0 ne { /lastday rows 1 add days_week mul def days_week 1 sub daywidth mul -440 rmoveto } { /lastday rows days_week mul 2 sub fillstart add def days_week 3 sub fillstart add daywidth mul -440 dayheight add rmoveto } ifelse lastday -1 ndays start 1 add add { /day exch def gsave .9 setgray daywidth 0 rlineto 0 dayheight neg rlineto daywidth neg 0 rlineto closepath fill grestore day weekday 1 eq { x0 y0 moveto days_week 1 sub daywidth mul -440 dayheight add rmoveto } { daywidth neg 0 rmoveto } ifelse } for } def /usefirst { % are last two boxes used by days? start ndays add rows days_week mul 3 sub gt start 2 ge and } def /calendar { titlefont findfont titlefontsize scalefont setfont 0 60 moveto /month_name month_names month 1 sub get def month_name show /yearstring year 10 string cvs def daywidth days_week mul yearstring stringwidth pop sub 60 moveto yearstring show eventflag { % Show a centered Banner if any at the Top daywidth days_week mul 2 div Bannerstring stringwidth pop 2 div sub 60 moveto Bannerstring show % Show footnotes left-center-right eventfont findfont footfontsize scalefont setfont /bottomrow { dayheight rows mul 5 sub neg } def 0 bottomrow moveto Lfootstring show daywidth days_week mul Rfootstring stringwidth pop sub bottomrow moveto Rfootstring show daywidth days_week mul Cfootstring stringwidth pop sub 2 div bottomrow moveto Cfootstring show } if 0 -5 moveto drawnums 0 -5 moveto drawfill eventflag { 0 0 moveto drawevents } if 0 -5 moveto drawgrid } def /eventflag true def @SCALE@ scale @ROTATE@ rotate @TRANSLATE@ translate /submonth 0 def calendar /eventflag false def month 1 sub 0 eq { /lmonth 12 def /lyear year 1 sub def } { /lmonth month 1 sub def /lyear year def } ifelse month 1 add 13 eq { /nmonth 1 def /nyear year 1 add def } { /nmonth month 1 add def /nyear year def } ifelse usefirst { 0 30 translate } { days_week 2 sub daywidth mul -350 translate } ifelse /submonth 1 def /year lyear def /month lmonth def gsave .138 .138 scale 12 -120 translate calendar grestore /submonth 1 def /year nyear def /month nmonth def daywidth 0 translate gsave .138 .138 scale 12 -120 translate calendar grestore showpage __END__ ### Copyright Notice: ## Copyright (C) 1999, 2000 Yoshinari Nomura. All rights reserved. ## Copyright (C) 2000 MHC developing team. All rights reserved. ## Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions ## are met: ## ## 1. Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## 2. Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in the ## documentation and/or other materials provided with the distribution. ## 3. Neither the name of the team nor the names of its contributors ## may be used to endorse or promote products derived from this software ## without specific prior written permission. ## ## THIS SOFTWARE IS PROVIDED BY THE TEAM AND CONTRIBUTORS ``AS IS'' ## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ## FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ## THE TEAM OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, ## INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ## (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ## SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ## HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, ## STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED ## OF THE POSSIBILITY OF SUCH DAMAGE. ### today ends here