home *** CD-ROM | disk | FTP | other *** search
/ Aminet 18 / aminetcdnumber181997.iso / Aminet / dev / gcc / ixemulsrc.lha / ixemul / utils / ixtimezone.c < prev    next >
C/C++ Source or Header  |  1996-12-11  |  8KB  |  310 lines

  1. /*
  2.  *  Written by Hans Verkuil (hans@wyst.hobby.nl)
  3.  *
  4.  *  battclock.resource patch idea was shamelessly stolen from the unixclock
  5.  *  utility written by Geert Uytterhoeven. unixclock is available on Aminet.
  6.  */
  7.  
  8. #include <stdlib.h>
  9. #include <stdio.h>
  10. #include <time.h>
  11.  
  12. #include <ixemul.h>
  13. #include <exec/memory.h>
  14. #include <clib/exec_protos.h>
  15. #include <dos/var.h>
  16.  
  17. #include <sys/time.h>
  18. #include <proto/exec.h>
  19. #include <proto/dos.h>
  20. #include <proto/battclock.h>
  21.  
  22. #define LVOReadBattClock    (-12)
  23. #define LVOWriteBattClock    (-18)
  24.  
  25. /* Flags. Currently only bit 0 is used to tell whether Daylight Saving Time
  26.    is in effect or not. However, ixtimezone only sets this flag, but it doesn't
  27.    test it. And neither does ixemul.library. In fact, the library completely
  28.    ignores the flag field. */
  29.  
  30. #define DST_ON    0x01
  31.  
  32. typedef struct
  33. {
  34.   long          offset;
  35.   unsigned char flags;
  36. } ixtime;
  37.  
  38. struct Node *BattClockBase;
  39.  
  40. char VERSION[] = "\000$VER: ixtimezone 1.0 (10.11.95)";
  41.  
  42. /* battclock.resource patch code */
  43. asm("
  44.     .text
  45.     .globl _NewReadBattClock
  46.     .globl _NewWriteBattClock
  47.     .globl _OldReadBattClock
  48.     .globl _OldWriteBattClock
  49.     .globl _GMTOffset
  50.     .globl _EndOfPatch
  51.  
  52. _NewReadBattClock:
  53.     .int    0x207a0014, 0x4e9090ba, 0x00164e75
  54.  
  55. /*    Since the GNU assembler is currently unable to properly compile
  56.     PC-relative code, I'm using the hex-code directly. As soon as the
  57.     assembler can handle PC-relative code, the line above should be
  58.     replaced by:
  59.  
  60.     move.l        _OldReadBattClock(pc),a0
  61.     jsr        (a0)
  62.     sub.l        _GMTOffset(pc),d0
  63.     rts
  64. */
  65.  
  66. _NewWriteBattClock:
  67.     .int    0xd0ba0010, 0x207a0008
  68.     .short    0x4ed0
  69.  
  70. /*    Since the GNU assembler is currently unable to properly compile
  71.     PC-relative code, I'm using the hex-code directly. As soon as the
  72.     assembler can handle PC-relative code, the line above should be
  73.     replaced by:
  74.  
  75.     add.l        _GMTOffset(pc),d0
  76.     move.l        _OldWriteBattClock(pc),a0
  77.     jmp        (a0)
  78. */
  79.  
  80. _OldReadBattClock:
  81.     .int        0
  82. _OldWriteBattClock:
  83.     .int        0
  84. _GMTOffset:
  85.     .int        0
  86. _EndOfPatch:
  87. ");
  88.  
  89. extern char NewReadBattClock;
  90. extern char NewWriteBattClock;
  91. extern char OldReadBattClock;
  92. extern char OldWriteBattClock;
  93. extern long GMTOffset; 
  94. extern long EndOfPatch;
  95. extern struct ixemul_base *ixemulbase;
  96.  
  97. static ixtime *read_ixtime(void)
  98. {
  99.   static ixtime t;
  100.   char buf[10];
  101.   
  102.   /*
  103.    * Read the GMT offset. This environment variable is 5 bytes long. The
  104.    * first 4 form a long that contains the offset in seconds and the fifth
  105.    * byte contains flags.
  106.    */
  107.   if (GetVar("IXGMTOFFSET", buf, 6, GVF_BINARY_VAR) == 5 && IoErr() == 5)
  108.   {
  109.     memcpy(&t.offset, buf, 4);
  110.     t.flags = buf[4];
  111.     return &t;
  112.   }
  113.   return NULL;
  114. }
  115.  
  116. static void create_ixtime(ixtime *t, char *pathname)
  117. {
  118.   FILE *f = fopen(pathname, "w");
  119.   
  120.   if (f)
  121.   {
  122.     fwrite(t, 5, 1, f);
  123.     fclose(f);
  124.   }
  125. }
  126.  
  127. static void write_ixtime(ixtime *t, int write_also_to_envarc)
  128. {
  129.   ix_set_gmt_offset(t->offset);
  130.   create_ixtime(t, "/ENV/IXGMTOFFSET");
  131.   if (write_also_to_envarc)
  132.     create_ixtime(t, "/ENVARC/IXGMTOFFSET");
  133. }
  134.  
  135. static void set_clock(long offset)
  136. {
  137.   struct timeval tv;
  138.   
  139.   gettimeofday(&tv, NULL);
  140.   tv.tv_sec += offset;
  141.   settimeofday(&tv, NULL);
  142. }
  143.  
  144. static void reset_clock(void)
  145. {
  146.   struct timeval tv;
  147.   
  148.   tv.tv_usec = 0;
  149.   tv.tv_sec = ReadBattClock();
  150.   tv.tv_sec += (8*365+2) * 24 * 3600 + ix_get_gmt_offset();
  151.   settimeofday(&tv, NULL);
  152. }
  153.  
  154. static long *get_function_addr(void)
  155. {
  156.   return *((long **)((char *)BattClockBase + LVOReadBattClock + 2));
  157. }
  158.  
  159. static void patch_batt_resource(long offset)
  160. {
  161.   char *mem;
  162.   long size = (long)&EndOfPatch - (long)&NewReadBattClock;
  163.   long mem_offset;
  164.   long *oldread, *oldwrite, *gmt;
  165.  
  166.   BattClockBase = (struct Node *)OpenResource("battclock.resource");
  167.   if (*(gmt = get_function_addr()) == 0x207a0014)
  168.   {
  169.     printf("battclock.resource was already patched.\n");
  170.     gmt = (long *)(((char *)&GMTOffset) + (long)((char *)gmt - &NewReadBattClock));
  171.     if (*gmt != offset)
  172.     {
  173.       *gmt = offset;
  174.       reset_clock();
  175.     }
  176.     return;  /* already patched */
  177.   }
  178.   GMTOffset = offset;
  179.   mem = AllocMem(size, MEMF_PUBLIC);
  180.   mem_offset = mem - &NewReadBattClock;
  181.   memcpy(mem, &NewReadBattClock, size);
  182.   CacheClearE(mem, size, CACRF_ClearI);  /* clear instruction cache */
  183.   oldread = (long *)(&OldReadBattClock + mem_offset);
  184.   oldwrite = (long *)(&OldWriteBattClock + mem_offset);
  185.   Disable();
  186.   *oldread = (long)SetFunction((struct Library *)BattClockBase, LVOReadBattClock, (void *)(&NewReadBattClock + mem_offset));
  187.   *oldwrite = (long)SetFunction((struct Library *)BattClockBase, LVOWriteBattClock, (void *)(&NewWriteBattClock + mem_offset));
  188.   Enable();
  189.   reset_clock();
  190.   printf("patched battclock.resource.\n");
  191. }
  192.  
  193. static void remove_patch(void)
  194. {
  195.   long *p, mem_offset, *oldread, *oldwrite;
  196.   long size = (long)&EndOfPatch - (long)&NewReadBattClock;
  197.  
  198.   BattClockBase = (struct Node *)OpenResource("battclock.resource");
  199.   if (*(p = get_function_addr()) != 0x207a0014)
  200.   {
  201.     printf("battclock.resource wasn't patched.\n");
  202.     exit(0);  /* not patched */
  203.   }
  204.   mem_offset = (char *)p - &NewReadBattClock;
  205.   oldread = (long *)(&OldReadBattClock + mem_offset);
  206.   oldwrite = (long *)(&OldWriteBattClock + mem_offset);
  207.   Disable();
  208.   SetFunction((struct Library *)BattClockBase, LVOReadBattClock, (void *)*oldread);
  209.   SetFunction((struct Library *)BattClockBase, LVOWriteBattClock, (void *)*oldwrite);
  210.   Enable();
  211.   FreeMem(p, size);
  212.   reset_clock();
  213.   printf("removed battclock.resource patch.\n");
  214.   exit(0);
  215. }
  216.  
  217. static void test(void)
  218. {
  219.   time_t t;
  220.   
  221.   time(&t);
  222.   printf("GMT:   %s", asctime(gmtime(&t)));
  223.   printf("Local: %s", asctime(localtime(&t)));
  224.   exit(0);
  225. }
  226.  
  227. static void usage(void)
  228. {
  229.   fprintf(stderr, "Usage: ixtimezone <option>
  230. Where <option> is one of:
  231.  
  232. -test        Show GMT and localtime
  233. -get-offset    Get GMT offset and patch ixemul.library
  234. -check-dst    As -get-offset, but also automatically adjust the Amiga
  235.         time if Daylight Saving Time has gone in effect (or vice
  236.         versa)
  237. -patch-resource    As -get-offset, but also patch the battclock.resource
  238. -remove-patch    Remove the battclock.resource patch\n");
  239.   exit(1);
  240. }
  241.  
  242. int main(int argc, char **argv)
  243. {
  244.   struct tm *local_tm, *gmt_tm;
  245.   int local_hms, gmt_hms;
  246.   time_t t, local_t, gmt_t;
  247.   ixtime *old, new;
  248.   int set_the_clock = 0, patch_resource = 0;
  249.   int write_to_envarc = 0;
  250.  
  251.   if (ixemulbase->ix_lib.lib_Version < 42)
  252.   {
  253.     fprintf(stderr, "ixtimezone requires ixemul.library version 42 or higher.\n");
  254.     exit(1);
  255.   }
  256.   if (argc != 2)
  257.     usage();
  258.  
  259.   if (!strcmp(argv[1], "-test"))
  260.     test();
  261.   else if (!strcmp(argv[1], "-check-dst"))
  262.     set_the_clock = 1;
  263.   else if (!strcmp(argv[1], "-patch-resource"))
  264.     patch_resource = 1;
  265.   else if (!strcmp(argv[1], "-remove-patch"))
  266.     remove_patch();
  267.   else if (strcmp(argv[1], "-get-offset"))
  268.     usage();
  269.  
  270.   /*
  271.    * Get current time, both GMT and local.  
  272.    * We don't care if these values are correct or not, we are only interested
  273.    * in the difference between the two.
  274.    */
  275.  
  276.   time(&t);
  277.   gmt_tm = gmtime(&t);
  278.   gmt_hms = gmt_tm->tm_hour * 3600 + gmt_tm->tm_min * 60 + gmt_tm->tm_sec;
  279.   local_tm = localtime(&t);
  280.   local_hms = local_tm->tm_hour * 3600 + local_tm->tm_min * 60 + local_tm->tm_sec;
  281.   new.flags = (local_tm->tm_isdst ? DST_ON : 0);
  282.   new.offset = 0;
  283.   if (gmt_hms != local_hms)
  284.   {
  285.     /* They are not the same. So compute the difference between them */
  286.  
  287.     local_tm->tm_isdst = 0;     /* don't let these values influence the result! */
  288.     local_tm->tm_zone = NULL;
  289.     local_tm->tm_gmtoff = 0;
  290.     local_t = mktime(local_tm);
  291.     gmt_tm = gmtime(&t);
  292.     gmt_tm->tm_isdst = 0;
  293.     gmt_tm->tm_zone = NULL;
  294.     gmt_tm->tm_gmtoff = 0;
  295.     gmt_t = mktime(gmt_tm);
  296.     new.offset = gmt_t - local_t;     /* obtain the difference */
  297.   }
  298.  
  299.   old = read_ixtime();
  300.   if (old == NULL || old->offset != new.offset)
  301.   {
  302.     write_to_envarc = 1;
  303.     if (set_the_clock && old)
  304.       set_clock(old->offset - new.offset);
  305.   }
  306.   write_ixtime(&new, write_to_envarc);
  307.   if (patch_resource)
  308.     patch_batt_resource(new.offset);
  309. }
  310.