home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d9xx / d945 / reminder.lha / Reminder / src / src.lha / CheckMain.c < prev    next >
C/C++ Source or Header  |  1993-04-19  |  14KB  |  460 lines

  1. /* ReminderCheck checks and alerts about event generated with Reminder */
  2.  
  3. /* $Id: CheckMain.c,v 1.17 1993/04/19 15:20:43 Matti_Rintala Exp $ */
  4.  
  5. #include <stdlib.h>
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include <errno.h>
  9. #include <time.h>
  10. #include <exec/types.h>
  11. #include <dos/dosextens.h>
  12. #include <utility/tagitem.h>
  13. #include <exec/libraries.h>
  14. #include <intuition/intuition.h>
  15.  
  16. #include <libraries/reqtools.h>
  17.  
  18. #ifdef __SASC
  19. #include <proto/exec.h>
  20. #include <proto/intuition.h>
  21. #include <proto/reqtools.h>
  22. #endif
  23.  
  24. #ifdef _DCC
  25. #include <clib/exec_protos.h>
  26. #include <clib/alib_protos.h>
  27. #include <clib/intuition_protos.h>
  28. #include <clib/reqtools_protos.h>
  29. #include <dos/dos.h>
  30. #include <clib/dos_protos.h>
  31. #include <workbench/startup.h>
  32.  
  33. struct Library *CxBase = NULL;    /* DICE does not auto open commodities.library */
  34. struct Library *IconBase = NULL; /* Nor icon.library */
  35.  
  36. /* DICE does not have difftime(), although its prototype is in time.h. This is
  37.    a bug in DICE, so we'll have to declare difftime() as macro */
  38. #define difftime(a,b) ((double)(a) - (double)(b))
  39.  
  40. #endif
  41.  
  42. #include "Constants.h"
  43. #include "CalcDate.h"
  44. #include "ARexx.h"
  45.  
  46. /* Responses from makealarm */
  47. #define QUIT 0
  48. #define ACK 2
  49.  
  50. char *VersionStr = "$VER: ReminderCheck 1.20";
  51.  
  52. static int makealarm(time_t *date, const char *text);
  53. static int groupalarm(time_t *date, struct EventNode *node, long int fpos);
  54. static void CloseLibraries(void);
  55.  
  56. static void putack(struct EventNode *node, time_t acked);
  57. static char *divtext(char *str, const char *text);
  58. static int makerequest(char *buffer);
  59.  
  60. char *filename;            /* Filename of the database file */
  61. FILE *infile;
  62.  
  63. struct EventNode eventnode, groupnode, *node = NULL;
  64.  
  65. BOOL noreqtools = FALSE;    /* TRUE if NOREQTOOLS tooltype is present */
  66. BOOL noarexx = FALSE;        /* TRUE if NOAREXX tooltype is present */
  67. short maxgroup = DEFAULTMAXGROUP; /* Maximum number of grouped events in one req */
  68. short groupno = 0;        /* Number of events currently grouped */
  69.  
  70. long int *groupfpos = NULL;    /* Table for record positions in grouped req */
  71. char *grouptext = NULL;        /* Text of group requester */
  72. char *groupnext = NULL;    /* Place to put next grouped event text */
  73.  
  74. short maxwidth = DEFAULTMAXWIDTH; /* Maximum width of requester line */
  75.  
  76. #ifdef _DCC
  77. static struct FileLock *oldlock = NULL;
  78.  
  79. /* DICE uses different entry point for workbench startup */
  80. int wbmain(struct WBStartup *msg) {
  81.  
  82.   /* We have to change to correct directory */
  83.   oldlock = CurrentDir(msg->sm_ArgList[0].wa_Lock);
  84.   return main(0, (char **)msg);    /* Simply call main() */
  85. }
  86. #endif
  87.  
  88. /* ReqTools library is only opened, if alerts are needed */
  89. struct ReqToolsBase *ReqToolsBase = NULL;
  90.  
  91. int main(int argc, char **argv) {
  92.  
  93.   UBYTE **ttypes;
  94.   long fpos;
  95.   time_t today, tim, alarm, acked, stamp;
  96.   double diff;
  97.   int response, interval;
  98.   size_t size;
  99.   
  100.   atexit(CloseLibraries);    /* Close libraries on exit */
  101.  
  102. #ifdef _DCC
  103.   /* With DICE we have to open commodities.library and icon.library, too */
  104.   if (!(CxBase = OpenLibrary("commodities.library", 0))) {
  105.     printf("Can't open commodities.library!\n");
  106.     exit(-1);
  107.   }
  108.   if (!(IconBase = OpenLibrary("icon.library", 0))) {
  109.     printf("Can't open icon.library!\n");
  110.     exit(-1);
  111.   }
  112. #endif
  113.  
  114.   /* Get today's date */
  115.   gettoday(&today);
  116.  
  117.   /* Set node to point to eventnode */
  118.   node = &eventnode;
  119.  
  120.   /* Parse arguments */
  121.   ttypes = ArgArrayInit(argc, argv);
  122.   /* Try to find database filename */
  123.   filename = ArgString(ttypes, FILETYPE, DEFAULTFILE);
  124.   /* And checking interval */
  125.   interval = ArgInt(ttypes, INTRVLTYPE, DEFAULTINTRVL);
  126.   /* And whether we use ReqTools.library or not */
  127.   noreqtools = (ArgString(ttypes, NOREQTOOLSTYPE, NULL) != NULL);
  128.   /* And whether we use ARexx or not */
  129.   noarexx = (ArgString(ttypes, NOAREXXTYPE, NULL) != NULL);
  130.   /* And maximum number of events in one grouped requester */
  131.   maxgroup = ArgInt(ttypes, MAXGROUPTYPE, DEFAULTMAXGROUP);
  132.   /* And maximum width of requester line */
  133.   maxwidth = ArgInt(ttypes, MAXWIDTHTYPE, DEFAULTMAXWIDTH);
  134.   if (maxwidth < 19)
  135.     maxwidth = 19;        /* Minimum is  */
  136.  
  137.   /* Try to open database */
  138.   infile = fopen(filename, "r+b");
  139.  
  140.   /* Clear up argument parsing */
  141.   ArgArrayDone();
  142.  
  143.   /* Exit if database open failed */
  144.   if (infile == NULL)
  145.     exit(0);
  146.  
  147.   /* Initialize ARexx, if permitted */
  148.   if (!noarexx)
  149.     initarexx();
  150.  
  151.   /* Allocate space for grouped requester data, if needed */
  152.   if (maxgroup != 0) {
  153.     if ((grouptext = malloc((maxgroup*(TEXTLEN+1)+19)*sizeof(char))) != NULL) {
  154.       if ((groupfpos = (long int *)calloc(maxgroup, sizeof(long int))) == NULL) {
  155.     /* If memory allocation failed, free also grouptext */
  156.     free(grouptext);
  157.     grouptext = NULL;
  158.       }
  159.     }
  160.   }
  161.  
  162.   /* Check the stamp */
  163.   if (fread(&stamp, 1, sizeof(time_t), infile) == sizeof(time_t)) {
  164.     tim = time(NULL);
  165.     diff = difftime(tim, stamp); /* Current interval in seconds */
  166.     if (diff >= (double)interval * 3600.0) {
  167.       /* Write new stamp */
  168.       fseek(infile, 0, SEEK_SET);
  169.       fwrite(&tim, 1, sizeof(time_t), infile);
  170.       fflush(infile);
  171.  
  172.       /* Go through all events in file */
  173.       while (TRUE) {
  174.     /* Read event to node */
  175.     clearerr(infile);
  176.     while (TRUE) {
  177.       fpos = ftell(infile);    /* Remember position */
  178.       size = fread(SAVEADDR(node), 1, SAVELEN(node), infile);
  179.       /* Stop looping if error or wrong number of bytes read */
  180.       if (ferror(infile) || size < SAVELEN(node))
  181.         break;
  182.       /* Also if event is not deleted */
  183.       if (node->mode != DELETEDMODE)
  184.         break;
  185.     }
  186.     /* Stop looping if end-of-file */
  187.     if (ferror(infile) || size < SAVELEN(node))
  188.       break;
  189.     
  190.     /* Get event's next alarm date */
  191.     makedate(&alarm, &today, node->day, node->month, node->year, node->wday);
  192.     /* And when it has been acknowledged last time */
  193.     makedate(&acked, &today, node->aday, node->amonth, node->ayear, 0);
  194.     
  195.     /* If acked time is the as alarm time, this event has already been acked */
  196.     if (acked == alarm)
  197.       continue;
  198.     
  199.     /* If grouping is enabled, alarm is today and event has grouping flag
  200.        set, use grouped requester, otherwise normal */
  201.     if (grouptext != NULL && alarm == today && (node->mode & GROUPEDMASK)) {
  202.       if (groupalarm(&today, node, fpos) == QUIT)
  203.         break;
  204.     }
  205.     else {
  206.       /* If difference between now and alarm time is less than 'before'
  207.          value, alarm is made */
  208.       diff = difftime(alarm, today);
  209.       if (diff < 0.0 || diff > ((double)node->before)*((double)3600*24)) {
  210.         /* If next alarm is too far away or in past, try previous alarm */
  211.         makeprevdate(&alarm, &today, node->day, node->month,
  212.              node->year, node->wday);
  213.         
  214.         /* If already acked, give up */
  215.         if (acked == alarm)
  216.           continue;
  217.         
  218.         /* If difference between alarm and now is more than 'after' value,
  219.            no alarm is made */
  220.         diff = difftime(today, alarm);
  221.         if (diff < 0.0 || diff > (double)node->after*(3600.0*24.0))
  222.           continue;
  223.       }
  224.       /* Use ARexx, if requested */
  225.       makearexx(node, &alarm);
  226.       /* Make the alarm requester */
  227.       response = makealarm(&alarm, node->text);
  228.       /* If response was 'quit', stop making alarms */
  229.       if (response == QUIT)
  230.         break;
  231.       /* If it was 'acknowledged', write the ack information to file */
  232.       if (response == ACK) {
  233.         /* If autodelete flag is set, whole event can be deleted */
  234.         if (node->autodelete)
  235.           node->mode = DELETEDMODE;
  236.         else
  237.           putack(node, alarm);
  238.       
  239.         fseek(infile, fpos, SEEK_SET);
  240.         size = fwrite(SAVEADDR(node), 1, SAVELEN(node), infile);
  241.         /* Stop if error */
  242.         if (ferror(infile) || size < SAVELEN(node))
  243.           break;
  244.         fflush(infile);
  245.       }
  246.     }
  247.       }
  248.       /* If grouped alarms are enabled, flush them */
  249.       if (grouptext != NULL) {
  250.     groupalarm(&today, NULL, 0);
  251.       }
  252.     }
  253.   }
  254.  
  255.   fclose(infile);
  256.   
  257.   exit(0);
  258. }
  259.   
  260.  
  261. /* putack puts the acknowledgement date to EventNode */
  262. static void putack(struct EventNode *node, time_t acked) {
  263.  
  264.   struct tm *tmptr;
  265.  
  266.   tmptr = localtime(&acked);    /* Change to struct */
  267.   node->aday = tmptr->tm_mday;
  268.   node->amonth = tmptr->tm_mon + 1;
  269.   node->ayear = tmptr->tm_year + 1900;
  270. }
  271.  
  272. static char buffer[TEXTLEN+19];    /* Place for requester text */
  273.  
  274. /* makealarm puts up an alarm requester */
  275. static int makealarm(time_t *date, const char *text) {
  276.  
  277.   char *str = buffer;
  278.   struct tm *d;
  279.   
  280.   /* Calculate date string */
  281.   d = localtime(date);
  282.   str += strftime(str, maxwidth, "%a %d-%b-%Y :\n", d); /* Print date */
  283.  
  284.   divtext(str, text);        /* Divide text into lines */
  285.  
  286.   return makerequest(buffer);    /* And present the requester to user */
  287. }
  288.  
  289. /* groupalarm handles grouped alarms */
  290. static int groupalarm(time_t *date, struct EventNode *node, long int fpos) {
  291.  
  292.   struct tm *d;
  293.   int i, response;
  294.   long int currfpos, size;
  295.   struct EventNode *gnode = &groupnode;
  296.  
  297.   /* If grouping not enabled, return */
  298.   if (grouptext == NULL)
  299.     return ACK;            /* Dummy return value */
  300.  
  301.   /* If node is not NULL (which means flush), add event */
  302.   if (node != NULL) {
  303.     /* If we have now the first event, calculate the date text */
  304.     if (groupno == 0) {
  305.       /* Calculate date string */
  306.       d = localtime(date);
  307.       groupnext = grouptext +
  308.     strftime(grouptext, maxwidth, "%a %d-%b-%Y :", d); /* Print date */
  309.       /* Remember the node for ARexx information */
  310.       groupnode = *node;
  311.     }
  312.     else {
  313.       /* If groupnode does not have ARexx, but new does, update groupnode */
  314.       if ((groupnode.mode & ~GROUPEDMASK) == AREXXNMODE &&
  315.       (node->mode & ~GROUPEDMASK) != AREXXNMODE)
  316.     groupnode = *node;
  317.     }
  318.     /* Add two newlines and the event text */
  319.     *groupnext++ = '\n';
  320.     *groupnext++ = '\n';
  321.     groupnext = divtext(groupnext, node->text);
  322.     /* Remember the position of the event in file */
  323.     groupfpos[groupno] = fpos;
  324.     /* There is one more event in group */
  325.     groupno++;
  326.   }
  327.  
  328.   /* If maximum number of events is reached or node is NULL, make the requester */
  329.   if (groupno == maxgroup || (node == NULL && groupno != 0)) {
  330.     /* Do the ARexx */
  331.     makearexx(&groupnode, date);
  332.  
  333.     /* Present the requester to user and get the response */
  334.     response = makerequest(grouptext);
  335.  
  336.     /* If response was 'ACK', update the acknowledgement dates of events */
  337.     if (response == ACK) {
  338.       currfpos = ftell(infile);    /* Remember current file position */
  339.  
  340.       for (i = 0; i < groupno; i++) {
  341.     clearerr(infile);
  342.     fseek(infile, groupfpos[i], SEEK_SET); /* Go to correct position */
  343.     size = fread(SAVEADDR(gnode), 1, SAVELEN(gnode), infile);
  344.     /* Stop, if error or wrong number of bytes read */
  345.     if (ferror(infile) || size < SAVELEN(gnode))
  346.       break;
  347.  
  348.     /* Delete or acknowledge event depending on mode */
  349.     if (gnode->autodelete)
  350.       gnode->mode = DELETEDMODE;
  351.     else
  352.       putack(gnode, *date);
  353.  
  354.     /* Seek back, write the new node information and flush buffers */
  355.     fseek(infile, groupfpos[i], SEEK_SET);
  356.     size = fwrite(SAVEADDR(gnode), 1, SAVELEN(gnode), infile);
  357.     /* Stop if errors */
  358.     if (ferror(infile) || size < SAVELEN(gnode))
  359.       break;
  360.     fflush(infile);
  361.       }
  362.       /* Return to old file position */
  363.       fseek(infile, currfpos, SEEK_SET);
  364.     }
  365.     groupno = 0;        /* No events in group any more */
  366.     groupnext = grouptext;    /* Same goes for group text */
  367.   }
  368.   else {
  369.     response = ACK;        /* If no requester was put up */
  370.   }
  371.  
  372.   return response;
  373. }
  374.  
  375. /* divtext divides text into lines and copies it */
  376. static char *divtext(char *str, const char *text) {
  377.  
  378.   int i;
  379.   
  380.   while (strlen(text) > maxwidth) {
  381.     /* Find first space in the end of line */
  382.     for (i = maxwidth; i > 0 && text[i] != ' '; i--);
  383.     if (i < 2)
  384.       i = maxwidth;        /* If no space found */
  385.     
  386.     strncpy(str, text, i);    /* Copy the line into buffer */
  387.     str += i;
  388.     text += i;
  389.     *str++ = '\n';        /* Add linefeed to end of line */
  390.     
  391.     while (*text != '\0' && *text == ' ')
  392.       text++;            /* Skip spaces in the beginning of new line */
  393.   }
  394.  
  395.   if (*text != '\0') {
  396.     strcpy(str, text);        /* Copy the last partial line */
  397.     str += strlen(text);    /* Update str */
  398.   }
  399.  
  400.   return str;            /* Return pointer to end of added text */
  401. }
  402.  
  403. /* makerequest puts up a given requester and returns the button pressed */
  404. static int makerequest(char *buffer) {
  405.   
  406.   /* Use ReqTools or EasyRequest depending on toolstype/cmdarg */
  407.   if (noreqtools) {
  408.     /* Structure for Intuition EasyRequest */
  409.     struct EasyStruct es = {sizeof(struct EasyStruct), 0, "Reminder",
  410.                   NULL, "Go away!|Ackn|Quit!"};
  411.     es.es_TextFormat = buffer;
  412.     
  413.     return (int)EasyRequest(NULL, &es, NULL, NULL);
  414.   }
  415.   else {
  416.     /* Open ReqTools library if not already open */
  417.     if (ReqToolsBase == NULL) {
  418.       if ((ReqToolsBase = (struct ReqToolsBase *)
  419.        OpenLibrary(REQTOOLSNAME, REQTOOLSVERSION)) == NULL)
  420.     return QUIT;        /* If cannot open, quit program */
  421.     }
  422.  
  423.     /* Make the alarm requester */
  424.     return (int)rtEZRequestTags(buffer, "_Go away!|_Ackn|_Quit!", NULL, NULL,
  425.                 RT_PubScrName, (Tag)"Workbench",
  426.                 RT_ReqPos, REQPOS_CENTERSCR,
  427.                 RT_Underscore, (Tag)'_', 
  428.                 RTEZ_Flags, EZREQF_CENTERTEXT, TAG_END);
  429.   }
  430. }
  431.  
  432. /* CloseLibraries simply closes the opened libraries */
  433. static void CloseLibraries(void) {
  434.  
  435.   if (ReqToolsBase != NULL)
  436.     CloseLibrary((struct Library *)ReqToolsBase);
  437.  
  438.   /* Release ARexx */
  439.   releasearexx();
  440.  
  441.   /* Free grouped requester data */
  442.   free(grouptext);
  443.   free(groupfpos);
  444.  
  445. #ifdef _DCC
  446.   /* With DICE we have to close commodities.library and icon.library, too */
  447.   if (CxBase != NULL)
  448.     CloseLibrary(CxBase);
  449.  
  450.   if (IconBase != NULL)
  451.     CloseLibrary(IconBase);
  452.  
  453.   /* Let's also return us to correct directory */
  454.   if (oldlock != NULL)
  455.     CurrentDir(oldlock);
  456.  
  457. #endif
  458.  
  459. }
  460.