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