A C program for a Calendar in LaTeX.
B. Narasimhan
naras at stat.fsu.edu
Wed Nov 29 01:36:41 AEST 1989
/* This is "lxcal.c", a C program to produce a Calendar in LaTeX. No
* claim whatsoever is made regrading this code or the calendar-design.
* Note that no error checking is done.
* The calendar is printed on a 11in by 8.5in paper. Use your favorite
* DVI2PS with the landsacpe option.
* I found this to be a useful programming exercise both in C and LaTeX.
* Comments, suggestions and reports regarding bugs are welcome.
* Author: B. Narasimhan
* Dept. of Statistics
Florida State Univesrity
* Tallahassee, FL 32306.
* E-mail: naras at stat.fsu.edu
* I wish to thank
* William Poser
* Dept. of Linguistics
* Stanford University
* who provided the string handling routines I needed, including a new
* "itoa" and the person (I don't know the name) who posted the
* day_of_week algorithm on the net.
* Usage: lxcal [input_file] [output_file]
*
* Input should be as follows:
YEAR MONTH - Integers
DAY_NO - Integer
\begin{itemize}
\item Item_1 \\
\item Item_2 \\
......
\item Item_N \\
\end{itemize}
%
ANOTHER DAY_NO - Integer
\begin{itemize}
....
\end{itemize}
%
* and so on. The % is important. This preempts the use of %. Can anyone
* out there change this to %% instead? I tried the usual tricks and for
* some reason they all bombed and I am not willing to spend more time on
* this now.
*/
/* AN EXERCISE [M15]: This is a wrap-around calendar with 5 rows.
Some time in the future the last row will be empty, i.e.
February will have 28 days with the Ist day beginning on a Sunday.
Which is the earliest year when this happens? Can you
characterize those years (at least during your lifetime) ?
*/
#include <stdio.h>
#include <string.h>
#define CNULL ( ( char *) 0)
#define MAXLEN 1000
static char *month_name[] = {
"", "JANUARY", "FEBRUARY", "MARCH", "APRIL",
"MAY", "JUNE", "JULY", "AUGUST",
"SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER"
};
static int no_of_days[2][13] = {
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
static int offsets[] = {0, 0, 3, 3, 6, 1, 4, 6, 2, 5, 7, 3, 5};
char *create_copy(string)
char *string;
/*
* Copy a string and return a pointer to the new storage allocated
* to contain it. This function differs from strcpy(3) in that it
* allocates the necessary storage instead of just copying into
* storage provided by the caller.
*/
{
char *new;
extern char *malloc();
if( (new = malloc( (unsigned) (strlen(string) + 1) ) ) != CNULL){
strcpy(new,string);
}
else fprintf(stderr,"create_copy: malloc error\n");
return(new);
}
char *copy_cat(string1,string2)
char *string1, *string2;
/*
* Concatenate the strings in the two arguments, allocating the
* necessary storage, freeing the storage allocated to the first
* argument, and returning a pointer to the newly created object.
* This function is similar to strcat(3) but differs from it in
* handling the storage allocation as well as in destroying the
* original first argument.
*/
{
char *new;
extern char *malloc();
extern void free();
if( (new = malloc( (unsigned) (strlen(string1) + strlen(string2) + 1) )) != CNULL){
strcpy(new,string1);
strcat(new,string2);
if(string1 != CNULL) free(string1);
}
else fprintf(stderr,"copy_cat: malloc error\n");
return(new);
}
/*
* This is a fast subroutine for converting a binary
* integer into a decimal ASCII string. It is very much
* like the itoa routine described on p.60 of K&R, but differs
* from it in several respects. One is the order of arguments.
* The name of the string in which to write is given first in order
* to be consistent with sprintf and kindred functions.
*
* This algorithm fails on the most negative integer.
*
*/
void itoa(string,k)
char *string; /* String in which to write */
int k; /* Integer to convert */
{
register int temp;
register char *s;
register char *p;
register int n;
register int q;
register int r;
int sign;
n = k; /* Copy integer into register */
p = s = string; /* Copy pointer into registers */
if( (sign = n) < 0) n = -n; /* Make n positive */
/* Do conversion */
do {
q = n / 10;
r = (q << 3) + (q << 1); /* Multiply by 10 (8x + 2x = 10x) */
*s++ = n + '0' - r;
} while (( n = q) > 0);
if(sign < 0) *s++ = '-'; /* If negative, add sign */
*s = '\0'; /* Null terminate string */
s--; /* Point s at last non-null char */
/*
* Now we reverse the string. When we begin, p points at the first
* character of the string, s at the last non-null character.
*/
while(p < s){
temp = *p;
*p++ = *s;
*s-- = temp;
}
return;
}
int day_of_week(year,month, day)
int year, month,day;
{
int dw;
dw=4+year+((year+3)/4)+offsets[month]+day;
if( ((year%4) ==0) && (month > 2)) dw++;
if( (year==0) && (month < 3)) dw++;
dw=(dw%7);
return(dw);
}
int days_in_month(year, month)
int year, month;
{
int leap;
leap = year%4 == 0 && year%100 !=0 || year%400 == 0;
return(no_of_days[leap][month]);
}
char *read_event(fpi, p) /* Read LaTeX input and return pointer. */
FILE *fpi;
char *p;
{
int i, lenp;
char s[MAXLEN], *temp_p, *t;
temp_p = p;
lenp = strlen(p);
if ((t = malloc((unsigned) lenp)) != CNULL) {
for (i = 0; i < lenp - 1; i++)
t[i] = *temp_p++;
t[i] = '\0';
free(p);
while ((i=fscanf(fpi,"%[^\%]",s)) != 0) {
t = copy_cat(t," ");
t = copy_cat(t, s);
}
t = copy_cat(t," }");
i = fscanf(fpi,"%s",s);
} else
fprintf(stderr,"read_event: malloc error\n");
return(t);
}
main(argc, argv)
int argc;
char *argv[];
{
int year, month, day, result, prev, next, temp, cell_no;
int days_now, days_prev, days_next, days_next_next;
int day_one_now, day_one_next, day_one_prev;
int which_day, next_month, prev_month, prev_month_year, next_month_year;
int i, j;
char buffer[10], *p;
FILE *fpi, *fpo, *fopen();
struct day_info {
int day_no;
char *event;
} list[35];
if (argc == 1) {
fpi = stdin;
fpo = stdout;
}
if (argc >= 2)
if ((fpi = fopen(*++argv, "r")) == NULL) {
fprintf(stderr,"lxcal: can't open %s\n",*argv);
exit(1);
}
if (argc >= 3)
if ((fpo = fopen(*++argv, "w")) == NULL) {
fprintf(stderr,"lxcal: can't open %s\n",*argv);
exit(1);
}
result = fscanf(fpi,"%d %d", &year, &month);
/* Figure prev_month and prev_month_year, and same for next month. */
if (month == 1) {
prev_month = 12;
prev_month_year = year - 1;
} else {
prev_month = month - 1;
prev_month_year = year;
}
if (month == 12) {
next_month = 1;
next_month_year = year + 1;
} else {
next_month_year = year;
next_month = month + 1;
}
/* Figure starting week day in those months. */
day_one_now = day_of_week(year, month, 1);
day_one_next = day_of_week(next_month_year, next_month, 1);
day_one_prev = day_of_week(prev_month_year, prev_month, 1);
/* Figure number of days in those months. */
days_now = days_in_month( year, month);
days_prev = days_in_month( prev_month_year, prev_month);
days_next = days_in_month( next_month_year, next_month);
/* Determine where the sub-calendars for prev and next months go. */
temp = days_now + day_one_now;
if (day_one_now == 0 && days_now == 28) {
prev = 28;
next = 34;
} else if ( day_one_now == 0) {
prev = 33;
next = 34;
} else if (temp <= 34) {
prev = 0;
next = 34;
} else if (temp == 35) {
prev = 0;
next = 1;
} else {
prev = (temp+1)%7;
next = prev + 1;
}
/* Mark the corresponding days with negative day_no's. (For output.) */
list[prev].day_no = -2;
list[next].day_no = -1;
/* Set the dates. */
for (i = day_one_now; i < day_one_now + 35; i++) {
j = i%35;
if (i >= days_now + day_one_now)
list[j].day_no = 0; /* Mark boxes with no day numbers. */
else
list[j].day_no = i - day_one_now + 1;
if (j%7 != 0)
list[j].event = create_copy(" &\\boxit{\n }");
else
list[j].event = create_copy(" \\boxit{\n }");
}
/* Now set about arranging things for the sub-calendars. */
if (prev%7 == 0)
list[prev].event = create_copy(" ");
else
list[prev].event = create_copy(" &");
list[prev].event = copy_cat(list[prev].event,"\\boxcal{\n \\");
list[prev].event = copy_cat(list[prev].event,"begin{center}\n ");
list[prev].event = copy_cat(list[prev].event, month_name[prev_month]);
list[prev].event = copy_cat(list[prev].event, " ");
itoa(buffer, prev_month_year);
list[prev].event = copy_cat(list[prev].event,buffer);
list[prev].event = copy_cat(list[prev].event,"\n \\end{center}\n");
list[prev].event = copy_cat(list[prev].event," \\begin{tabular*}");
list[prev].event = copy_cat(list[prev].event,"{1.30in}{rrrrrrr}\n");
list[prev].event = copy_cat(list[prev].event," \\hline\n ");
list[prev].event = copy_cat(list[prev].event,"S &M &T &W &T ");
list[prev].event = copy_cat(list[prev].event,"&F &S \\\\\n");
list[prev].event = copy_cat(list[prev].event," \\hline\n");
for (i = 0; i < days_prev+day_one_prev; i++) {
if (i%7 == 0)
list[prev].event = copy_cat(list[prev].event," ");
else
list[prev].event = copy_cat(list[prev].event," &");
if (i >= day_one_prev) {
itoa(buffer,i-day_one_prev+1);
list[prev].event = copy_cat(list[prev].event,buffer);
}
if (i%7 == 6)
list[prev].event = copy_cat(list[prev].event,"\\\\\n");
}
list[prev].event = copy_cat(list[prev].event,"\n ");
list[prev].event = copy_cat(list[prev].event,"\\end{tabular*}\n }");
if (next%7 == 0)
list[next].event = create_copy(" ");
else
list[next].event = create_copy(" &");
list[next].event = copy_cat(list[next].event,"\\boxcal{\n \\");
list[next].event = copy_cat(list[next].event,"begin{center}\n ");
list[next].event = copy_cat(list[next].event, month_name[next_month]);
list[next].event = copy_cat(list[next].event, " ");
itoa(buffer,next_month_year);
list[next].event = copy_cat(list[next].event, buffer);
list[next].event = copy_cat(list[next].event,"\n \\end{center}\n");
list[next].event = copy_cat(list[next].event," \\begin{tabular*}");
list[next].event = copy_cat(list[next].event,"{1.30in}{rrrrrrr}\n");
list[next].event = copy_cat(list[next].event," \\hline\n S");
list[next].event = copy_cat(list[next].event," &M &T &W &T &F");
list[next].event = copy_cat(list[next].event," &S \\\\\n \\hline\n");
for (i = 0; i < days_next+day_one_next; i++) {
if (i%7 == 0)
list[next].event = copy_cat(list[next].event," ");
else
list[next].event = copy_cat(list[next].event," &");
if (i >= day_one_next) {
itoa(buffer,i-day_one_next+1);
list[next].event = copy_cat(list[next].event,buffer);
}
if (i%7 == 6)
list[next].event = copy_cat(list[next].event,"\\\\\n");
}
list[next].event = copy_cat(list[next].event,"\n \\");
list[next].event = copy_cat(list[next].event,"end{tabular*}\n }");
/* Read the events for the present month. */
result = fscanf(fpi,"%d",&which_day);
while (result != 0 && result != EOF) {
temp = (which_day + day_one_now +34)%35;
list[temp].event = read_event(fpi, list[temp].event);
result = fscanf(fpi,"%d",&which_day);
}
/* Just print out the LaTeX file! */
fprintf(fpo,"%s","\% This is a Calendar in LaTeX.\n");
fprintf(fpo,"%s","\% Author: B. Narasimhan\n");
fprintf(fpo,"%s","\% Dept. of Statistics\n");
fprintf(fpo,"%s","\% Florida State University\n");
fprintf(fpo,"%s","\% Tallahassee, FL 32306-3033\n");
fprintf(fpo,"%s","\% Ph. (904)-644-5852\n");
fprintf(fpo,"%s","\% E-mail: naras at stat.fsu.edu\n");
fprintf(fpo,"%s","\% Freely redistributable and modifyable. \n");
fprintf(fpo,"%s","\% Modifiers and enhancers, please:\n");
fprintf(fpo,"%s","\% 1. Make sure credit is given to all");
fprintf(fpo,"%s"," involved\n");
fprintf(fpo,"%s","\% and more importantly,\n");
fprintf(fpo,"%s","\% 2. Mail me a copy for the sake of");
fprintf(fpo,"%s"," improving my knowledge.\n" );
fprintf(fpo,"%s","\%\n");
fprintf(fpo,"%s","\\documentstyle[12pt]{article}\n");
fprintf(fpo,"%s","\%\n");
fprintf(fpo,"%s","\\newcommand{\\boxit}[1]{\\vbox to 81pt{\\");
fprintf(fpo,"%s","begin{minipage}[b]{1.30in}\n");
fprintf(fpo,"%s","\\setlength{\\parskip}{0.0in}\n");
fprintf(fpo,"%s","\\setlength{\\baselineskip}{3.0mm}\n");
fprintf(fpo,"%s","\\raggedright #1 \\end{minipage}\n");
fprintf(fpo,"%s","\\vspace{\\fill}}}\n");
fprintf(fpo,"%s","\%\n");
fprintf(fpo,"%s","\\newcommand{\\boxcal}[1]{\\vbox to 81pt{\\");
fprintf(fpo,"%s","begin{minipage}[b]{1.30in}\n");
fprintf(fpo,"%s","\\setlength{\\arrayrulewidth}{0.01mm} \n");
fprintf(fpo,"%s","\\renewcommand{\\arraystretch}{0.60} \n");
fprintf(fpo,"%s","\\trsix\n");
fprintf(fpo,"%s","\\setlength{\\baselineskip}{1.90mm} \n");
fprintf(fpo,"%s","\\setlength{\\tabcolsep}{0.6em} \n");
fprintf(fpo,"%s","#1\\end{minipage}\\vspace{\\fill}}}\n");
fprintf(fpo,"%s","\%\n");
fprintf(fpo,"%s","\\setlength{\\topskip}{0.0in}\n");
fprintf(fpo,"%s","\\setlength{\\headheight}{0.0in}\n");
fprintf(fpo,"%s","\\setlength{\\headsep}{0.0in}\n");
fprintf(fpo,"%s","\\setlength{\\footheight}{0.0in}\n");
fprintf(fpo,"%s","\\setlength{\\footskip}{0.4375in}\n");
fprintf(fpo,"%s","\\setlength{\\topmargin}{-.5625in}\n");
fprintf(fpo,"%s","\\setlength{\\oddsidemargin}{-0.65in}\n");
fprintf(fpo,"%s","\\setlength{\\textwidth}{10.3in}\n");
fprintf(fpo,"%s","\\setlength{\\textheight}{8.5in}\n");
fprintf(fpo,"%s","\\newfont{\\hb}{h-bol at 12pt}\n");
fprintf(fpo,"%s","\\newfont{\\hbbig}{h-bol at 24pt}\n");
fprintf(fpo,"%s","\\newfont{\\tbeight}{t-bol at 8pt}\n");
fprintf(fpo,"%s","\\newfont{\\treight}{t-rom at 8pt}\n");
fprintf(fpo,"%s","\\newfont{\\tbieight}{t-bolita at 8pt}\n");
fprintf(fpo,"%s","\\newfont{\\tb}{t-bol at 12pt}\n");
fprintf(fpo,"%s","\\newfont{\\trsix}{t-rom at 6pt}\n");
fprintf(fpo,"%s","\\pagestyle{empty}\n");
fprintf(fpo,"%s","\\begin{document}\n");
fprintf(fpo,"%s","\\treight\n");
fprintf(fpo,"%s","\\vspace{\\fill}\n");
fprintf(fpo,"%s","\\begin{center}\n");
fprintf(fpo,"%s"," {\\hbbig ");
fprintf(fpo,"%s ",month_name[month]);
fprintf(fpo," ");
itoa(buffer, year);
fprintf(fpo,"%s",buffer);
fprintf(fpo,"%s","}\n\\end{center}\n");
fprintf(fpo,"%s","\\begin{tabular}{|p{1.30in}|p{1.30in}|p{1.30in}");
fprintf(fpo,"%s","|p{1.30in}|p{1.30in}|p{1.30in}|p{1.30in}|}\n");
fprintf(fpo,"%s"," \\hline\n");
fprintf(fpo,"%s"," \\multicolumn{1}{|c}{\\hb Sunday}\n");
fprintf(fpo,"%s"," &\\multicolumn{1}{|c}{\\hb Monday}\n");
fprintf(fpo,"%s"," &\\multicolumn{1}{|c}{\\hb Tuesday}\n");
fprintf(fpo,"%s"," &\\multicolumn{1}{|c}{\\hb Wednesday}\n");
fprintf(fpo,"%s"," &\\multicolumn{1}{|c}{\\hb Thursday}\n");
fprintf(fpo,"%s"," &\\multicolumn{1}{|c}{\\hb Friday}\n");
fprintf(fpo,"%s"," &\\multicolumn{1}{|c|}{\\hb Saturday}\n");
fprintf(fpo,"%s"," \\\\ \n");
fprintf(fpo,"%s"," \\hline\\hline");
for (i = 0; i < 5; i++) {
for (j = 0; j < 7; j++) {
cell_no = i*7 + j;
if (cell_no%7 == 0)
fprintf(fpo," ");
else
fprintf(fpo," &");
if (list[cell_no].day_no <= 0 )
fprintf(fpo,"\n");
else
if (cell_no%7 == 6 || list[cell_no].day_no == days_now) {
fprintf(fpo,"%s","\\multicolumn{1}{|r|}{\\tb ");
itoa(buffer,list[cell_no].day_no);
fprintf(fpo,"%s}\n",buffer);
}
else {
fprintf(fpo,"%s","\\multicolumn{1}{|r}{\\tb ");
itoa(buffer,list[cell_no].day_no);
fprintf(fpo,"%s}\n",buffer);
}
}
fprintf(fpo," \\\\\n");
for (j = 0; j < 7; j++) {
cell_no = i*7 + j;
fprintf(fpo,"%s\n",list[cell_no].event);
}
fprintf(fpo,"%s"," \\\\\n \\hline\n");
}
/* Close open environments. */
fprintf(fpo,"%s","\\end{tabular}\n");
fprintf(fpo,"%s","\\vspace{\\fill}\n");
fprintf(fpo,"%s","\\end{document}\n");
/* Return heap space. */
for (i = 0; i < 35; i++)
free(list[i].event);
/* Close files. */
fclose(fpi);
fclose(fpo);
}
Sample input-----------CUT HERE----------
1989 12
4
\begin{itemize}
\item Oral Presentation. \\
\item 6:30pm Dinner date with Brokujam.
\end{itemize}
%
7
\begin{itemize}
\item Mom's Birthday. \\
\item 10:00am Dentist Appointment.
\end{itemize}
%
30
\begin{itemize}
\item Duds Anonymous meeting. \\
\item Party till death
\end{itemize}
%
More information about the Comp.lang.c
mailing list