diff --git a/ChangeLog b/ChangeLog index ba06b65..6e8f16d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,16 @@ AmiTimeKeeper Change Log +v1.12 31.01.2021 + - New options to specify the local time zone + - New ARexx commands, parameters and error variables + - New option to start without active time synchronization + - New deactivate logging option for performance saving + - More bugs in calculation of time zone transition fixed + - Sanity check on value for interval between requests + - Updated ARexx documentation and script + - Honour ARexx result flag (RXFF_RESULT) + - Various minor bug fixes and improvements + v1.11 11.01.2021 - New dedicated external control utility - New ARexx port with a diverse set of commands diff --git a/README.md b/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/TimeKeeper.guide b/TimeKeeper.guide index 2a54569..318cd66 100644 --- a/TimeKeeper.guide +++ b/TimeKeeper.guide @@ -1,5 +1,5 @@ @database TimeKeeper.guide -@$VER: TimeKeeper.guide 1.11 (11.01.2021) +@$VER: TimeKeeper.guide 1.12 (31.01.2021) @(c) 2017-2021 Carsten Sonne Larsen @author Carsten Sonne Larsen @@ -26,46 +26,50 @@ @rem (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF @rem THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -@Width 74 +@Width 67 @Node Main "TimeKeeper.guide" @Next "Configuring the client" @Index "Index" +@Smartwrap -AmiTimeKeeper is a small program which keeps the time right on your -machine. No installation is required. It is operated as a commodity -and is controlled by AmigaOS. An AmiTCP compatible TCP/IP stack is -required for AmiTimeKeeper to work. +@{par} +AmiTimeKeeper is a small program which keeps the time right on +your machine. No installation is required. It is operated as a +commodity and is controlled by AmigaOS. An AmiTCP compatible +TCP/IP stack is required for AmiTimeKeeper to work. +@{par} Several settings determine the behaviour. The settings are passed -either directly from the command line or by using the tool types of -an icon. Preferences can also be saved between reboots. A utility -for small tasks like starting and stopping is also included. -Comprehensive control is possible using ARexx. +either directly from the command line or by using the tool types +of an icon. Preferences can also be saved between reboots. +A utility for small tasks like starting and stopping is also +included. Comprehensive control is possible using ARexx. +@{par} The default server address is pool.ntp.org. To find another server -try visiting support.ntp.org or one of the other sites listing time -servers. Use a fixed pool server by prefixing the domain, for example, -1.pool.ntp.org. +try visiting support.ntp.org or one of the other sites listing +time servers. Use a fixed pool server by prefixing the domain, +for example, 1.pool.ntp.org. -The local time zone needs to be configured using Workbench preferences -or a similar tool. In AmigaOS 3.1 and 3.9 this is available through -the Locale Preferences Editor. Daylight saving time is only supported -through the POSIX TZ environment variable. +@{par} +The time zone needs to be configured using Workbench preferences +or a similar tool. In Amiga OS this is available through the +Locale Preferences Editor. The local time zone and daylight saving +time are optionally specified manually or using a POSIX TZ +variable style. -This version (1.11) has two known issues: - - Settings window in AROS is broken (and disabled in this version) - - Setting Popkey is not supported in ARexx - -@{"Configuring the client" Link "Configuring the client"} -@{"Using tooltypes of an icon" Link "Using tooltypes of an icon"} -@{"Saving preferences" Link "Saving preferences"} -@{"ARexx interface" Link "ARexx interface"} -@{"The POSIX TZ environment variable" Link "The POSIX TZ environment variable"} -@{"Change Log / Change History" Link "Change Log"} -@{"Software License" Link "Software License"} -@{"Credits and donations" Link "Credits and donations"} -@{"Contacting the author" Link "Contacting the author"} +@{par} +@{"Configuring the client" Link "Configuring the client"}@{line} +@{"Using tooltypes of an icon" Link "Using tooltypes of an icon"}@{line} +@{"Local time zone" Link "Local time zone"}@{line} +@{"Saving preferences" Link "Saving preferences"}@{line} +@{"ARexx interface" Link "ARexx interface"}@{line} +@{"POSIX TZ environment variable" Link "POSIX TZ environment variable"}@{line} +@{"Change Log / Change History" Link "Change Log"}@{line} +@{"Software License" Link "Software License"}@{line} +@{"Credits and donations" Link "Credits and donations"}@{line} +@{"Contacting the author" Link "Contacting the author"}@{line} @EndNode @@ -103,53 +107,101 @@ is the NTP server port different from 123. @{b}Interval between requests@{ub} The Amiga hardware clock will slowly drift away from true time. The -drift is determined by several factors and is small, but the drift is not -insignificant. Request are repeatedly sent to the NTP server in order to -keep the Amiga hardware clock as close to true time as possible. +drift is determined by several factors and is small, but the drift +is not insignificant. Request are repeatedly sent to the NTP server +in order to keep the Amiga hardware clock as close to true time as +possible. The requests are sent in certain intervals defined by the interval parameter. The interval should be specified using milliseconds. One -thousand (1.000) milliseconds are equal to one second. +thousand (1.000) milliseconds are equal to one second. The max value +is 172.800.000 equal to 48 hours. @{b}Adjustment threshold@{ub} -Due to the accuracy of NTP servers and the nature of Amiga CPUs it does -not make sense to adjust the Amiga clock on every response from the NTP -server. Processing the response could take longer time than anticipated. -The amount of accepted inaccuracy is set using the threshold parameter. -The threshold should be specified using microsecond. One million -microseconds (1.000.000) are equal to one second. +Due to the accuracy of NTP servers and the nature of Amiga CPUs +it does not make sense to adjust the Amiga clock on every response +from the NTP server. Processing the response could take longer time +than anticipated. The amount of accepted inaccuracy is set using the +threshold parameter. The threshold should be specified using micro- +seconds. One million microseconds (1.000.000) are equal to one +second. + +@{b}Start with synchronization active@{ub} + +Maybe some related settings or the network connection are not ready +from the beginning. The commodity can then be started in a preparation +mode. Activation is possible from within AmigaOS, through the control +utility @{i}TimeCtrl@{ui}, or through the ARexx interface. @{b}Read only@{ub} It is possible to send request to the NTP server without setting the -Amiga hardware clock. This option can be handy when debugging time zone -setup and other time related parameters. +Amiga hardware clock. This option can be handy when debugging time +zone setup and other time related parameters. @{b}Expert mode@{ub} -Advanced settings are hidden from the settings window when expert mode -is not enabled. The advanced settings are server port, interval between -requests, adjustment threshold, connection timeout, verbosity level and -commodity priority. +Advanced settings are hidden from the settings window when expert +mode is not enabled. The advanced settings are server port, interval +between requests, adjustment threshold, connection timeout, verbosity +level and commodity priority. -@{b}Connection timeout@{ub} +@{b}Connection time out@{ub} -In some cases, a connection to the NTP server cannot be established or -the NTP server simply does respond. To avoid an infinitive wait for a -response the connection will instead make a time out. The timeout -interval should be specified using milliseconds. One thousand (1.000) -milliseconds are equal to one second. +In some cases, a connection to the NTP server cannot be established +or the NTP server simply does respond. To avoid an infinitive wait +for a response the connection will instead make a time out. The +timeout interval should be specified using milliseconds. One thousand +(1.000) milliseconds are equal to one second. The max value is half +of the value of interval between requests. @{b}Logging@{ub} -It is possible view log messages using the supplied tool TimeLogger. +It is possible view log messages using the supplied tool @{i}TimeLogger@{ui}. +The logger facility can be turned off with the NOLOG switch. + +@{b}Automatically synchronization@{ub} + +Sometimes it is feasible to start without synchronization. The clock +is synchronized in the background, only when synchronization is +active. By default synchronization is not active. + +@{b}Time zone display@{ub} + +Different options for displaying the current time zone exists. The +most common is a combination of time zone name and time zone offset +from GMT. The ARexx documentation describes the time zone display +methods in details. + +@{b}Time zone@{ub} + +If the local time zone is expressed as a POSIX TZ compatible string, +an optional setting is available. + +@{b}Time zone value@{ub} + +The most simple way to expressed the local time zone is in hours +and minutes from Greenwich Mean Time (GMT) or UTC. One hour ahead +is equal to 100. One hour behind is equal to -100. Two and a half +hour ahead is equal to 230. + +@{b}Time zone abbreviation@{ub} + +If no name is given for the local time zone, the time zone is shown +as hours and minutes from Greenwich Mean Time (GMT). It is possible +to specify a time zone abbreviation for better readability. + +@{b}Daylight saving time@{ub} + +Sometimes is feasible to force a standard one hour ahead daylight +saving time (DST). Use this option if needed. @{b}Caveats@{ub} -TimeKeeper does not start as a background process. When started in the -startup-sequence it will by default block execution. Use the run command -to avoid this behaviour: +TimeKeeper does not start as a background process. When started in +the startup-sequence it will by default block execution. Use the run +command to avoid this behaviour: > RUN >NIL: TimeKeeper @EndNode @@ -158,18 +210,18 @@ to avoid this behaviour: @Toc "Main" @Index "Index" @Prev "Configuring the client" -@Next "Saving preferences" +@Next "Local time zone" -Settings can be changes by setting tooltypes in the TimeKeeper icon. -Add the following line to the icon tooltypes in order to set the server -to an European pool server: +Settings can be changes by setting tooltypes in the TimeKeeper +icon. Add the following line to the icon tooltypes in order to +set the server to an European pool server: SERVER=europe.pool.ntp.org -The available tooltypes are the same as the available command line -parameters. +The available tooltypes are the same as the available command +line parameters. @{b}Parameter@{ub} | @{b}Short description@{ub} -------------------------------------------------- +----------------------------------------------------------- SERVER | NTP server Address PORT | NTP server Port THRESHOLD | Adjustment threshold @@ -180,6 +232,13 @@ CX_PRIORITY | Commodity priority READONLY | Do not set clock EXPERT | Show advanced options TIMEOUT | Connection Timeout +ACTIVE | Automatically start time synchronization +NOLOG | deactivate logger facility +TZD | Time zone display mode i settings window +TZ | Optional TZ POSIX string +TZVALUE | Optional time zone value +TZNAME | Optional time zone abbreviation +TZDST | Optionally force day light saving time @{b}Caveats@{ub} @@ -187,50 +246,90 @@ Settings can be overridden and are set in the following order: 1. Preference file 2. Icon tooltypes / CLI parameters -CLI parameters and icon tooltypes has highest precedence and will -always override settings from the preference file. +CLI parameters and icon tooltypes has highest precedence and +will always override settings from the preference file. Settings from icon tooltypes are used when starting from Workbench. Settings from CLI are used only when starting from shell. -When using WBStartup folder or a similar system, remember to add the -mandatory tooltype DONOTWAIT. Otherwise the startup execution will be -blocked. +When using WBStartup folder or a similar system, remember to add +the mandatory tool type DONOTWAIT. Otherwise the startup execution +will be blocked. + +@EndNode + +@Node "Local time zone" "TimeKeeper.guide/Local time zone" +@Toc "Main" +@Index "Index" +@Prev "Using tooltypes of an icon" +@Next "Saving preferences" + +@{b}Selecting time zone@{ub} + +The local time zone is selected from 5 different options, in +the following order: +1. TZVALUE setting +2. TZ setting +3. Global TZ environment variable +4. Global TZONE environment variable +5. Workbench Locale Preferences + +@{b}TZVALUE setting@{ub} +The local time zone value setting is available from CLI, icon +tool types and ARexx. The value is specified in hours and minutes +from GMT or UTC. One hour ahead is equal to 100. One hour +behind is equal to -100. + +@{b}TZ setting@{ub} +The TZ setting is available from CLI, icon tool types and ARexx. + +@{b}Global TZ environment variable@{ub} +The Amiga DOS global environment variable TZ containing a POSIX +TZ compliant string. + +@{b}Global TZONE environment variable@{ub} +The Amiga DOS environment global variable TZONE containing a +POSIX TZ compliant string. + +@{b}Workbench Locale Preferences@{ub} +Amiga OS / Workbench Locale Preferences Editor @EndNode @Node "Saving preferences" "TimeKeeper.guide/Saving preferences" @Toc "Main" @Index "Index" -@Prev "Using tooltypes of an icon" +@Prev "Local time zone" @Next "ARexx interface" -Preferences from the setting window are saved in the file -ENV:timekeeper.prefs +Preferences are saved in the file +ENV:AmiTimeKeeper/timekeeper.prefs Preferences are also persisted between reboots in the file -ENVARC:timekeeper.prefs +ENVARC:AmiTimeKeeper/timekeeper.prefs Depending on the setup the content of timekeeper.prefs could be: CX_POPUP=NO CX_POPKEY=lshift control t -CX_PRIORITY=25 +CX_PRIORITY=5 THRESHOLD=1000000 SERVER=de.pool.ntp.org PORT=123 -TIMEOUT=5000 +TIMEOUT=1000 INTERVAL=17500 READONLY=NO EXPERT=NO +ACTIVE=YES +NOLOG=YES The preference file should not be edited under normal circumstances. @{b}Caveats@{ub} Not only visible settings in the settings window are saved to the -preference file. All setting are saved includes default settings and -settings from CLI and icon tooltypes. +preference file. All setting are saved includes default settings +and settings from CLI and icon tooltypes. @EndNode @@ -238,7 +337,7 @@ settings from CLI and icon tooltypes. @Toc "Main" @Index "Index" @Prev "Saving preferences" -@Next "The POSIX TZ environment variable" +@Next "POSIX TZ environment variable" TimeKeeper provides an ARexx port with a diverse set of commands. The ARexx address is 'TIMEKEEPER.1'. @@ -251,17 +350,77 @@ HELP | Get a list of ARexx commands and a short description STATUS | Get current synchronization status TIMEZONE | Get current time zone LASTSYNC | Get time of last synchronization operation -LASTAD | Get time of last clock adjustment +LASTADJ | Get time of last clock adjustment GET | Get a runtime configuration value SET | Set a runtime configuration value +NOW | Get current time and date SHOW | Show settings window HIDE | Hide settings window START | Start the time synchronization process STOP | Stop the time synchronization process SHUTDOWN | Shutdown TimeKeeper +LOGTRANS | Emit time zone transition map to log ----------------------------------------------------------------- -@{b}Examples@{ub} +@{b}Parameters@{ub} +STATUS accepts NUMBER as parameter. Result is instead formatted +as binary value. + +TIMEZONE accepts a code as parameter. The code determines how +the result is formatted: + 1 Seconds ahead of UTC (+[s]) + 2 Minutes ahead of UTC (+[m]) + 3 TZ style long format (UTC-[hh]:[mm]) + 4 TZ style short format (-[hh]:[mm]) + 5 ISO 8601 style (+[hh][mm]) + 6 ISO 8601 style (+[hh]:[mm]) + 7 ISO 8601 short style (+[h]) + 8 Common identifier (GMT+[hh]:[mm]) + 9 Common identifier, offset with name in parens + 10 Common identifier, name with offset in parens + 11 Standard time zone + 12 DST time zone + +NOW, LASTSYNC and LASTADJ accepts DOS, ASCII, DATE, TIME, RFC850, +RFC1123, RFC2822, RFC3339 or ISO8601 as first parameter. DOS is +assumed if parameter is not specified. +DOS AmigaDOS style +ASCII C ASCII style +DATE ARexx formatted as sorted date +TIME Time in seconds since midnight + +LOCAL and UTC are accepted as second parameter. LOCAL is assumed +if second parameter is not specified. RFC850 and RFC1123 are always +GMT. + +GET accepts SERVER, PORT, THRESHOLD, INTERVAL, CX_PRIORITY, +CX_POPKEY, CX_POPUP, READONLY, EXPERT, TIMEOUT, ACTIVE, NOLOG, +TZD, TZ, TZNAME, TZVALUE and TZDST as parameter. CX_POPUP, +READONLY, EXPERT, ACTIVE and NOLOG accepts NUMBER as parameter. + +SET accepts the same parameters as GET, except CX_POPKEY, and an +addition value parameter. + +ID, VERSION, HELP, SHOW, HIDE, START, STOP, SHUTDOWN and LOGTRANS +does not accept parameters. + +@{b}Error handling@{ub} + +The local variables TIMEKEEPER.ERRORCODE and TIMEKEEPER.ERRORTEXT +are set on each call too ARexx. + +There is a number of possible error codes. + 0 No errors +10 Unknown ARexx command +11 Unknown parameter +12 Parameter is missing +13 Invalid parameter value +18 Clock has not been adjusted +19 No responses from NTP server +20 Synchronization is already active +21 Synchronization is already deactivated + +@{b}ARexx examples@{ub} Stop the the time synchronization process with: @{"Address TIMEKEEPER.1 stop" rxs "Address TIMEKEEPER.1 stop"} @@ -276,13 +435,15 @@ The arexx folder contains a set of sample scripts. @{b}TimeCtrl@{ub} Controlling TimeKeeper without using ARexx is done through the -control utility TimeCtrl. The following commands are available. +control utility @{i}TimeCtrl@{ui}. The following commands are available. @{b}Command@{ub} | @{b}Description@{ub} ----------------------------------------------------------------- ID | Get application identifier VERSION | Get application version number STATUS | Get current synchronization status +TIME | Get current time and date +ZONE | Get current time zone START | Start the time synchronization process STOP | Stop the time synchronization process SHOW | Show settings window @@ -292,23 +453,24 @@ SHUTDOWN | Shutdown TimeKeeper @EndNode -@Node "The POSIX TZ environment variable" "TimeKeeper.guide/The POSIX TZ environment variable" +@Node "POSIX TZ environment variable" "TimeKeeper.guide/POSIX TZ environment variable" @Toc "Main" @Index "Index" @Prev "ARexx interface" @Next "Change Log" -This is a list of time zone information for locations around the globe. -Information is represented in the POSIX TZ environment variable format. -The list originates from the IANA time zone database version 2019b. +This is a list of time zone information for locations around the +globe. Information is represented in the POSIX TZ environment +variable format. The list originates from the IANA time zone +database version 2019b. -A formal definition of the POSIX TZ environment variable is available -to the public from The GNU C Library Reference Manual and The Open Group -Library. +A formal definition of the POSIX TZ environment variable is +available to the public from The GNU C Library Reference Manual +and The Open Group Library. -The POSIX TZ environment variable is described in dept by for example -The Open Group Base Specifications Issue 7, 2018 edition and The GNU C -Library Reference Manual. +The POSIX TZ environment variable is described in dept by for +example The Open Group Base Specifications Issue 7, 2018 edition +and The GNU C Library Reference Manual. Set the TZ environment variable to Central Europe Time: > SETENV TZ CET-1 @@ -753,6 +915,17 @@ Pacific/Wallis TZ -12 @Prev "Saving preferences" @Next "Software License" +@{b}v1.12 31.01.2021@{ub} + - New options to specify the local time zone + - New ARexx commands, parameters and error variables + - New option to start without active time synchronization + - New deactivate logging option for performance saving + - More bugs in calculation of time zone transition fixed + - Sanity check on value for interval between requests + - Updated ARexx documentation and script + - Honour ARexx result flag (RXFF_RESULT) + - Various minor bug fixes and improvements + @{b}v1.11 11.01.2021@{ub} - New dedicated external control utility - New ARexx port with a diverse set of commands @@ -833,6 +1006,11 @@ Pacific/Wallis TZ -12 @Toc "Main" @Index "Index" +@{b}Bug reports@{ub} + +Please report bugs and other issues by sending a mail or using: +https://ranger.innolan.net/rainlance/AmiTimeKeeper + @{b}Simplified BSD License / 2-clause BSD license@{ub} Copyright (c) 2001, 02 Motoyuki Kasahara @@ -869,17 +1047,13 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. @Toc "Main" @Index "Index" -A custom version has been integrated into the Icaros Desktop system. -The author encourages all developers with an interest to make their own -build. - The following people have contributed with bug reports, comments, suggestions and donations: * aPEX from a1k.org +* Greg Donner * Paolo Besser * Knut Hansen -* Greg Donner * Thomas Blatt * Bartosz Makara * Benny Damsgaard @@ -911,52 +1085,136 @@ January 2021 @{b}A@{ub} -@{"ARexx interface" Link "ARexx interface"} +@{"Adjustment threshold" Link "Configuring the client" 40 } +@{"ARexx interface" Link "ARexx interface" 1 } +@{"Automatically synchronization" Link "Configuring the client" 84 } @{b}C@{ub} -@{"Configution" Link "Configuring the client"} -@{"CLI" Link "Using tooltypes of an icon"} +@{"Configution" Link "Configuring the client" 1 } +@{"Connection time out" Link "Configuring the client" 70 } +@{"CLI" Link "Using tooltypes of an icon" 7 } + +@{b}D@{ub} + +@{"Daylight saving time" Link "Configuring the client" 113 } + +@{b}E@{ub} + +@{"Environment variable" Link "Local time zone" 20 } +@{"Expert mode" Link "Configuring the client" 63 } + +@{b}I@{ub} + +@{"Interval between requests" Link "Configuring the client" 27 } +@{"ISO8601" Link "ARexx interface" 44 } + +@{b}K@{ub} + +@{"Keywords" Link "ARexx" 1 } + @{"ACTIVE - Automatically synchronization" Link "Configuring the client" 84 } + @{"ASCII" Link "ARexx interface" 44 } + @{"CX_POPKEY" Link "Using tooltypes of an icon" 17 } + @{"CX_POPUP" Link "Using tooltypes of an icon" 16 } + @{"CX_PRIORITY" Link "Using tooltypes of an icon" 18 } + @{"DATE" Link "ARexx interface" 44 } + @{"DONOTWAIT" Link "Using tooltypes of an icon" 42 } + @{"DOS" Link "ARexx interface" 44 } + @{"DST - Daylight saving time" Link "Configuring the client" 113 } + @{"EXPERT - Expert mode" Link "Configuring the client" 63 } + @{"INTERVAL - Interval between requests" Link "Configuring the client" 27 } + @{"ISO8601" Link "ARexx interface" 44 } + @{"LOCAL" Link "ARexx interface" 52 } + @{"NOLOG - Logging" Link "Configuring the client" 79 } + @{"NUMBER" Link "ARexx interface" 56 } + @{"POPUP" Link "Configuring the client"} + @{"PORT - Server port" Link "Configuring the client" 22 } + @{"READONLY" Link "Configuring the client" 57 } + @{"RFC850" Link "ARexx interface" 44 } + @{"RFC1123" Link "ARexx interface" 44 } + @{"RFC2822" Link "ARexx interface" 44 } + @{"RFC3339" Link "ARexx interface" 44 } + @{"SERVER - Server address" Link "Configuring the client" 1 } + @{"THRESHOLD - Adjustment threshold" Link "Configuring the client" 40 } + @{"TIME" Link "ARexx interface" 44 } + @{"TIMEOUT - Connection time out" Link "Configuring the client" 70 } + @{"TIMEZONE - Time zone" Link "Configuring the client" 97 } + @{"TIMEZONEABBREVIATION - Time zone abbreviation" Link "Configuring the client" 104 } + @{"TIMEZONEDISPLAY - Time zone display" Link "Configuring the client" 90 } + @{"TIMEZONEVALUE - Time zone value" Link "Configuring the client" 109 } + @{"TZ - Time zone" Link "Configuring the client" 97 } + @{"TZD - Time zone display" Link "Configuring the client" 90 } + @{"TZDST - Daylight saving time" Link "Configuring the client" 113 } + @{"TZNAME - Time zone abbreviation" Link "Configuring the client" 104 } + @{"TZVALUE - Time zone value" Link "Configuring the client" 109 } + @{"UTC" Link "ARexx interface" 52 } @{b}L@{ub} -@{"Logging" Link "Configuring the client"} +@{"Logging" Link "Configuring the client" 79 } @{b}N@{ub} -@{"Network Time Foundation" Link "Configuring the client"} +@{"Network Time Foundation" Link "Configuring the client" 4 } @{b}P@{ub} -@{"Pool servers" Link "Configuring the client"} -@{"Posix" Link "The POSIX TZ environment variable"} -@{"Preferences" Link "Saving preferences"} +@{"Pool servers" Link "Configuring the client" 9 } +@{"Popkey" Link "Using tooltypes of an icon" 17 } +@{"Popup" Link "Using tooltypes of an icon" 16 } +@{"Posix" Link "POSIX TZ environment variable" 1 } +@{"Preferences" Link "Saving preferences" 1 } +@{"Priority" Link "Using tooltypes of an icon" 18 } + +@{b}R@{ub} + +@{"Read only" Link "Configuring the client" 57 } +@{"RFC850" Link "ARexx interface" 44 } +@{"RFC1123" Link "ARexx interface" 44 } +@{"RFC2822" Link "ARexx interface" 44 } +@{"RFC3339" Link "ARexx interface" 44 } @{b}S@{ub} -@{"Settings" Link "Configuring the client"} - @{"Connection timeout" Link "Configuring the client"} - @{"Expert mode " Link "Configuring the client"} - @{"Interval" Link "Configuring the client"} - @{"Logging" Link "Configuring the client"} - @{"Popkey" Link "Configuring the client"} - @{"Popup" Link "Configuring the client"} - @{"Priority" Link "Configuring the client"} - @{"Read only" Link "Configuring the client"} - @{"Server address" Link "Configuring the client"} - @{"Server port" Link "Configuring the client"} - @{"Threshold" Link "Configuring the client"} -@{"Standard time" Link "The POSIX TZ environment variable"} -@{"Summer time" Link "The POSIX TZ environment variable"} +@{"Server address" Link "Configuring the client" 1 } +@{"Server port" Link "Configuring the client" 22 } +@{"Settings" Link "Configuring the client" 1 } + @{"Automatically synchronization" Link "Configuring the client" 84 } + @{"Connection time out" Link "Configuring the client" 70} + @{"Daylight saving time" Link "Configuring the client" 113 } + @{"Expert mode " Link "Configuring the client" 63 } + @{"Interval between requests" Link "Configuring the client" 27 } + @{"Logging" Link "Configuring the client" 79 } + @{"Popkey" Link "Using tooltypes of an icon" 17 } + @{"Popup" Link "Using tooltypes of an icon" 16 } + @{"Priority" Link "Using tooltypes of an icon" 18 } + @{"Read only" Link "Configuring the client" 57 } + @{"Server address" Link "Configuring the client" 1 } + @{"Server port" Link "Configuring the client" 22 } + @{"Threshold" Link "Configuring the client" 40 } + @{"Time zone" Link "Configuring the client" 97 } + @{"Time zone abbreviation" Link "Configuring the client" 104 } + @{"Time zone display" Link "Configuring the client" 90 } + @{"Time zone value" Link "Configuring the client" 109 } +@{"Standard time" Link "Configuring the client" 113 } +@{"Startup Sequence" Link "Configuring the client" 120 } +@{"Summer time" Link "Configuring the client" 113 } @{b}T@{ub} -@{"TimeCtrl" Link "ARexx interface"} -@{"TimeLogger" Link "Configuring the client"} -@{"Time zone" Link "Main"} - @{"Getting" Link "ARexx interface"} - @{"Setting" Link "Main"} -@{"Tool types" Link "Using tooltypes of an icon"} -@{"TZ" Link "The POSIX TZ environment variable"} +@{"Threshold" Link "Configuring the client" 40 } +@{"TimeCtrl" Link "ARexx interface" 95 } +@{"TimeLogger" Link "Configuring the client" 81 } +@{"Time zone" Link "Local time zone" 1 } + @{"Getting" Link "ARexx interface" 29 } + @{"Setting" Link "Local time zone" 1 } +@{"Time zone abbreviation" Link "Configuring the client" 104 } +@{"Time zone display" Link "Configuring the client" 90 } +@{"Time zone value" Link "Local time zone" 109 } +@{"Tool types" Link "Using tooltypes of an icon" 1 } +@{"TZ" Link "Local time zone" 18 } + @{"Content" Link "POSIX TZ environment variable" 7 } + @{"Setting" Link "Local time zone" 1 } +@{"TZONE" Link "Local time zone" 25 } @EndNode diff --git a/TimeKeeper.html b/TimeKeeper.html index 088b798..ef699a1 100644 --- a/TimeKeeper.html +++ b/TimeKeeper.html @@ -9,36 +9,39 @@

Contents Index Help Prev Next

 
-AmiTimeKeeper is a small program which keeps the time right on your
-machine. No installation is required. It is operated as a commodity
-and is controlled by AmigaOS. An AmiTCP compatible TCP/IP stack is
-required for AmiTimeKeeper to work.
+
+AmiTimeKeeper is a small program which keeps the time right on
+your machine. No installation is required. It is operated as a
+commodity and is controlled by AmigaOS. An AmiTCP compatible
+TCP/IP stack is required for AmiTimeKeeper to work.
+
 
 Several settings determine the behaviour. The settings are passed
-either directly from the command line or by using the tool types of
-an icon. Preferences can also be saved between reboots. A utility
-for small tasks like starting and stopping is also included.
-Comprehensive control is possible using ARexx.
+either directly from the command line or by using the tool types
+of an icon. Preferences can also be saved between reboots.
+A utility for small tasks like starting and stopping is also
+included. Comprehensive control is possible using ARexx.
+
 
 The default server address is pool.ntp.org. To find another server
-try visiting support.ntp.org or one of the other sites listing time
-servers. Use a fixed pool server by prefixing the domain, for example,
-1.pool.ntp.org.
+try visiting support.ntp.org or one of the other sites listing
+time servers. Use a fixed pool server by prefixing the domain,
+for example, 1.pool.ntp.org.
 
-The local time zone needs to be configured using Workbench preferences
-or a similar tool. In AmigaOS 3.1 and 3.9 this is available through
-the Locale Preferences Editor. Daylight saving time is only supported
-through the POSIX TZ environment variable.
 
-This version (1.11) has two known issues:
-  - Settings window in AROS is broken (and disabled in this version)
-  - Setting Popkey is not supported in ARexx
+The time zone needs to be configured using Workbench preferences
+or a similar tool. In Amiga OS this is available through the
+Locale Preferences Editor. The local time zone and daylight saving
+time are optionally specified manually or using a POSIX TZ
+variable style.
+
 
 Configuring the client
 Using tooltypes of an icon
+Local time zone
 Saving preferences
 ARexx interface
-The POSIX TZ environment variable
+POSIX TZ environment variable
 Change Log / Change History
 Software License
 Credits and donations
@@ -80,53 +83,101 @@ is the NTP server port different from 123.
 Interval between requests
 
 The Amiga hardware clock will slowly drift away from true time. The
-drift is determined by several factors and is small, but the drift is not
-insignificant. Request are repeatedly sent to the NTP server in order to
-keep the Amiga hardware clock as close to true time as possible.
+drift is determined by several factors and is small, but the drift
+is not insignificant. Request are repeatedly sent to the NTP server
+in order to keep the Amiga hardware clock as close to true time as
+possible.
 
 The requests are sent in certain intervals defined by the interval
 parameter. The interval should be specified using milliseconds. One
-thousand (1.000) milliseconds are equal to one second.
+thousand (1.000) milliseconds are equal to one second. The max value
+is 172.800.000 equal to 48 hours.
 
 Adjustment threshold
 
-Due to the accuracy of NTP servers and the nature of Amiga CPUs it does
-not make sense to adjust the Amiga clock on every response from the NTP
-server. Processing the response could take longer time than anticipated.
-The amount of accepted inaccuracy is set using the threshold parameter.
-The threshold should be specified using microsecond. One million
-microseconds (1.000.000) are equal to one second.
+Due to the accuracy of NTP servers and the nature of Amiga CPUs
+it does not make sense to adjust the Amiga clock on every response
+from the NTP server. Processing the response could take longer time
+than anticipated. The amount of accepted inaccuracy is set using the
+threshold parameter. The threshold should be specified using micro-
+seconds. One million microseconds (1.000.000) are equal to one
+second.
+
+Start with synchronization active
+
+Maybe some related settings or the network connection are not ready
+from the beginning. The commodity can then be started in a preparation
+mode. Activation is possible from within AmigaOS, through the control
+utility TimeCtrl, or through the ARexx interface.
 
 Read only
 
 It is possible to send request to the NTP server without setting the
-Amiga hardware clock. This option can be handy when debugging time zone
-setup and other time related parameters.
+Amiga hardware clock. This option can be handy when debugging time
+zone setup and other time related parameters.
 
 Expert mode
 
-Advanced settings are hidden from the settings window when expert mode
-is not enabled. The advanced settings are server port, interval between
-requests, adjustment threshold, connection timeout, verbosity level and
-commodity priority.
+Advanced settings are hidden from the settings window when expert
+mode is not enabled. The advanced settings are server port, interval
+between requests, adjustment threshold, connection timeout, verbosity
+level and commodity priority.
 
-Connection timeout
+Connection time out
 
-In some cases, a connection to the NTP server cannot be established or
-the NTP server simply does respond. To avoid an infinitive wait for a
-response the connection will instead make a time out. The timeout
-interval should be specified using milliseconds. One thousand (1.000)
-milliseconds are equal to one second.
+In some cases, a connection to the NTP server cannot be established
+or the NTP server simply does respond. To avoid an infinitive wait
+for a response the connection will instead make a time out. The
+timeout interval should be specified using milliseconds. One thousand
+(1.000) milliseconds are equal to one second. The max value is half
+of the value of interval between requests.
 
 Logging
 
-It is possible view log messages using the supplied tool TimeLogger.
+It is possible view log messages using the supplied tool TimeLogger.
+The logger facility can be turned off with the NOLOG switch.
+
+Automatically synchronization
+
+Sometimes it is feasible to start without synchronization. The clock
+is synchronized in the background, only when synchronization is
+active. By default synchronization is not active.
+
+Time zone display
+
+Different options for displaying the current time zone exists. The
+most common is a combination of time zone name and time zone offset
+from GMT. The ARexx documentation describes the time zone display
+methods in details.
+
+Time zone
+
+If the local time zone is expressed as a POSIX TZ compatible string,
+an optional setting is available.
+
+Time zone value
+
+The most simple way to expressed the local time zone is in hours
+and minutes from Greenwich Mean Time (GMT) or UTC. One hour ahead
+is equal to 100. One hour behind is equal to -100. Two and a half
+hour ahead is equal to 230.
+
+Time zone abbreviation
+
+If no name is given for the local time zone, the time zone is shown
+as hours and minutes from Greenwich Mean Time (GMT). It is possible
+to specify a time zone abbreviation for better readability.
+
+Daylight saving time
+
+Sometimes is feasible to force a standard one hour ahead daylight
+saving time (DST). Use this option if needed.
 
 Caveats
 
-TimeKeeper does not start as a background process. When started in the
-startup-sequence it will by default block execution. Use the run command
-to avoid this behaviour:
+TimeKeeper does not start as a background process. When started in
+the startup-sequence it will by default block execution. Use the run
+command to avoid this behaviour:
 > RUN >NIL: TimeKeeper
 
 
@@ -134,19 +185,19 @@ to avoid this behaviour:

TimeKeeper.guide/Using tooltypes of an icon

-

Contents Index Help Prev Next

+

Contents Index Help Prev Next

 
-Settings can be changes by setting tooltypes in the TimeKeeper icon.
-Add the following line to the icon tooltypes in order to set the server
-to an European pool server:
+Settings can be changes by setting tooltypes in the TimeKeeper
+icon. Add the following line to the icon tooltypes in order to
+set the server to an European pool server:
 SERVER=europe.pool.ntp.org
 
-The available tooltypes are the same as the available command line
-parameters.
+The available tooltypes are the same as the available command
+line parameters.
 
 Parameter   | Short description
--------------------------------------------------
+-----------------------------------------------------------
 SERVER      | NTP server Address
 PORT        | NTP server Port
 THRESHOLD   | Adjustment threshold
@@ -157,6 +208,13 @@ CX_PRIORITY | Commodity priority
 READONLY    | Do not set clock
 EXPERT      | Show advanced options
 TIMEOUT     | Connection Timeout
+ACTIVE      | Automatically start time synchronization
+NOLOG       | deactivate logger facility
+TZD         | Time zone display mode i settings window
+TZ          | Optional TZ POSIX string
+TZVALUE     | Optional time zone value
+TZNAME      | Optional time zone abbreviation
+TZDST       | Optionally force day light saving time
 
 Caveats
 
@@ -164,57 +222,97 @@ Settings can be overridden and are set in the following order:
 1. Preference file
 2. Icon tooltypes / CLI parameters
 
-CLI parameters and icon tooltypes has highest precedence and will
-always override settings from the preference file.
+CLI parameters and icon tooltypes has highest precedence and
+will always override settings from the preference file.
 
 Settings from icon tooltypes are used when starting from Workbench.
 Settings from CLI are used only when starting from shell.
 
-When using WBStartup folder or a similar system, remember to add the
-mandatory tooltype DONOTWAIT. Otherwise the startup execution will be
-blocked.
+When using WBStartup folder or a similar system, remember to add
+the mandatory tool type DONOTWAIT. Otherwise the startup execution
+will be blocked.
+
+
+
+
+
+

TimeKeeper.guide/Local time zone

+

Contents Index Help Prev Next

+
+
+Selecting time zone
+
+The local time zone is selected from 5 different options, in
+the following order:
+1. TZVALUE setting
+2. TZ setting
+3. Global TZ environment variable
+4. Global TZONE environment variable
+5. Workbench Locale Preferences
+
+TZVALUE setting
+The local time zone value setting is available from CLI, icon
+tool types and ARexx. The value is specified in hours and minutes
+from GMT or UTC. One hour ahead is equal to 100. One hour
+behind is equal to -100.
+
+TZ setting
+The TZ setting is available from CLI, icon tool types and ARexx.
+
+Global TZ environment variable
+The Amiga DOS global environment variable TZ containing a POSIX
+TZ compliant string.
+
+Global TZONE environment variable
+The Amiga DOS environment global variable TZONE containing a
+POSIX TZ compliant string.
+
+Workbench Locale Preferences
+Amiga OS / Workbench Locale Preferences Editor
 
 

TimeKeeper.guide/Saving preferences

-

Contents Index Help Prev Next

+

Contents Index Help Prev Next

 
-Preferences from the setting window are saved in the file
-ENV:timekeeper.prefs
+Preferences are saved in the file
+ENV:AmiTimeKeeper/timekeeper.prefs
 
 Preferences are also persisted between reboots in the file
-ENVARC:timekeeper.prefs
+ENVARC:AmiTimeKeeper/timekeeper.prefs
 
 Depending on the setup the content of timekeeper.prefs could be:
 
 CX_POPUP=NO
 CX_POPKEY=lshift control t
-CX_PRIORITY=25
+CX_PRIORITY=5
 THRESHOLD=1000000
 SERVER=de.pool.ntp.org
 PORT=123
-TIMEOUT=5000
+TIMEOUT=1000
 INTERVAL=17500
 READONLY=NO
 EXPERT=NO
+ACTIVE=YES
+NOLOG=YES
 
 The preference file should not be edited under normal circumstances.
 
 Caveats
 
 Not only visible settings in the settings window are saved to the
-preference file. All setting are saved includes default settings and
-settings from CLI and icon tooltypes.
+preference file. All setting are saved includes default settings
+and settings from CLI and icon tooltypes.
 
 

TimeKeeper.guide/ARexx interface

-

Contents Index Help Prev Next

+

Contents Index Help Prev Next

 
 TimeKeeper provides an ARexx port with a diverse set of commands.
@@ -228,17 +326,77 @@ HELP       | Get a list of ARexx commands and a short description
 STATUS     | Get current synchronization status
 TIMEZONE   | Get current time zone
 LASTSYNC   | Get time of last synchronization operation
-LASTAD     | Get time of last clock adjustment
+LASTADJ    | Get time of last clock adjustment
 GET        | Get a runtime configuration value
 SET        | Set a runtime configuration value
+NOW        | Get current time and date
 SHOW       | Show settings window
 HIDE       | Hide settings window
 START      | Start the time synchronization process
 STOP       | Stop the time synchronization process
 SHUTDOWN   | Shutdown TimeKeeper
+LOGTRANS   | Emit time zone transition map to log
 -----------------------------------------------------------------
 
-Examples
+Parameters
+STATUS accepts NUMBER as parameter. Result is instead formatted
+as binary value.
+
+TIMEZONE accepts a code as parameter. The code determines how
+the result is formatted:
+  1  Seconds ahead of UTC (+[s])
+  2  Minutes ahead of UTC (+[m])
+  3  TZ style long format (UTC-[hh]:[mm])
+  4  TZ style short format (-[hh]:[mm])
+  5  ISO 8601 style (+[hh][mm])
+  6  ISO 8601 style (+[hh]:[mm])
+  7  ISO 8601 short style (+[h])
+  8  Common identifier (GMT+[hh]:[mm])
+  9  Common identifier, offset with name in parens
+ 10  Common identifier, name with offset in parens
+ 11  Standard time zone
+ 12  DST time zone
+
+NOW, LASTSYNC and LASTADJ accepts DOS, ASCII, DATE, TIME, RFC850,
+RFC1123, RFC2822, RFC3339 or ISO8601 as first parameter. DOS is
+assumed if parameter is not specified.
+DOS    AmigaDOS style
+ASCII  C ASCII style
+DATE   ARexx formatted as sorted date
+TIME   Time in seconds since midnight
+
+LOCAL and UTC are accepted as second parameter. LOCAL is assumed
+if second parameter is not specified. RFC850 and RFC1123 are always
+GMT.
+
+GET accepts SERVER, PORT, THRESHOLD, INTERVAL, CX_PRIORITY,
+CX_POPKEY, CX_POPUP, READONLY, EXPERT, TIMEOUT, ACTIVE, NOLOG,
+TZD, TZ, TZNAME, TZVALUE and TZDST as parameter. CX_POPUP,
+READONLY, EXPERT, ACTIVE and NOLOG accepts NUMBER as parameter.
+
+SET accepts the same parameters as GET, except CX_POPKEY, and an
+addition value parameter.
+
+ID, VERSION, HELP, SHOW, HIDE, START, STOP, SHUTDOWN and LOGTRANS
+does not accept parameters.
+
+Error handling
+
+The local variables TIMEKEEPER.ERRORCODE and TIMEKEEPER.ERRORTEXT
+are set on each call too ARexx.
+
+There is a number of possible error codes.
+ 0  No errors
+10  Unknown ARexx command
+11  Unknown parameter
+12  Parameter is missing
+13  Invalid parameter value
+18  Clock has not been adjusted
+19  No responses from NTP server
+20  Synchronization is already active
+21  Synchronization is already deactivated
+
+ARexx examples
 Stop the the time synchronization process with:
 Address TIMEKEEPER.1 stop
 
@@ -253,13 +411,15 @@ The arexx folder contains a set of sample scripts.
 TimeCtrl
 
 Controlling TimeKeeper without using ARexx is done through the
-control utility TimeCtrl. The following commands are available.
+control utility TimeCtrl. The following commands are available.
 
 Command    | Description
 -----------------------------------------------------------------
 ID         | Get application identifier
 VERSION    | Get application version number
 STATUS     | Get current synchronization status
+TIME       | Get current time and date
+ZONE       | Get current time zone
 START      | Start the time synchronization process
 STOP       | Stop the time synchronization process
 SHOW       | Show settings window
@@ -269,23 +429,24 @@ SHUTDOWN   | Shutdown TimeKeeper
 
 
-
+

-

TimeKeeper.guide/The POSIX TZ environment variable

+

TimeKeeper.guide/POSIX TZ environment variable

Contents Index Help Prev Next

 
-This is a list of time zone information for locations around the globe.
-Information is represented in the POSIX TZ environment variable format.
-The list originates from the IANA time zone database version 2019b.
+This is a list of time zone information for locations around the
+globe. Information is represented in the POSIX TZ environment
+variable format. The list originates from the IANA time zone
+database version 2019b.
 
-A formal definition of the POSIX TZ environment variable is available
-to the public from The GNU C Library Reference Manual and The Open Group
-Library.
+A formal definition of the POSIX TZ environment variable is
+available to the public from The GNU C Library Reference Manual
+and The Open Group Library.
 
-The POSIX TZ environment variable is described in dept by for example
-The Open Group Base Specifications Issue 7, 2018 edition and The GNU C
-Library Reference Manual.
+The POSIX TZ environment variable is described in dept by for
+example The Open Group Base Specifications Issue 7, 2018 edition
+and The GNU C Library Reference Manual.
 
 Set the TZ environment variable to Central Europe Time:
 > SETENV TZ CET-1
@@ -730,6 +891,17 @@ Pacific/Wallis TZ <GMT+12>-12
 

Contents Index Help Prev Next

 
+v1.12 31.01.2021
+  - New options to specify the local time zone
+  - New ARexx commands, parameters and error variables
+  - New option to start without active time synchronization
+  - New deactivate logging option for performance saving
+  - More bugs in calculation of time zone transition fixed
+  - Sanity check on value for interval between requests
+  - Updated ARexx documentation and script
+  - Honour ARexx result flag (RXFF_RESULT)
+  - Various minor bug fixes and improvements
+
 v1.11 11.01.2021
   - New dedicated external control utility
   - New ARexx port with a diverse set of commands
@@ -810,6 +982,11 @@ Pacific/Wallis TZ <GMT+12>-12
 

Contents Index Help Prev Next

 
+Bug reports
+
+Please report bugs and other issues by sending a mail or using:
+https://ranger.innolan.net/rainlance/AmiTimeKeeper
+
 Simplified BSD License / 2-clause BSD license
 
 Copyright (c) 2001, 02  Motoyuki Kasahara
@@ -846,17 +1023,13 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 

Contents Index Help Prev Next

 
-A custom version has been integrated into the Icaros Desktop system.
-The author encourages all developers with an interest to make their own
-build.
-
 The following people have contributed with bug reports, comments,
 suggestions and donations:
 
 * aPEX from a1k.org
+* Greg Donner
 * Paolo Besser
 * Knut Hansen
-* Greg Donner
 * Thomas Blatt
 * Bartosz Makara
 * Benny Damsgaard
@@ -892,13 +1065,70 @@ January 2021
 
 A
 
+Adjustment threshold
 ARexx interface
+Automatically synchronization
 
 C
 
 Configution
+Connection time out
 CLI
 
+D
+
+Daylight saving time
+
+E
+
+Environment variable
+Expert mode
+
+I
+
+Interval between requests
+ISO8601
+
+K
+
+Keywords
+  ACTIVE - Automatically synchronization
+  ASCII
+  CX_POPKEY
+  CX_POPUP
+  CX_PRIORITY
+  DATE
+  DONOTWAIT
+  DOS
+  DST - Daylight saving time
+  EXPERT - Expert mode
+  INTERVAL - Interval between requests
+  ISO8601
+  LOCAL
+  NOLOG - Logging
+  NUMBER
+  POPUP
+  PORT - Server port
+  READONLY
+  RFC850
+  RFC1123
+  RFC2822
+  RFC3339
+  SERVER - Server address
+  THRESHOLD - Adjustment threshold
+  TIME
+  TIMEOUT - Connection time out
+  TIMEZONE - Time zone
+  TIMEZONEABBREVIATION - Time zone abbreviation
+  TIMEZONEDISPLAY - Time zone display
+  TIMEZONEVALUE - Time zone value
+  TZ - Time zone
+  TZD - Time zone display
+  TZDST - Daylight saving time
+  TZNAME - Time zone abbreviation
+  TZVALUE - Time zone value
+  UTC
+
 L
 
 Logging
@@ -910,35 +1140,62 @@ January 2021
 P
 
 Pool servers
-Posix
+Popkey
+Popup
+Posix
 Preferences
+Priority
+
+R
+
+Read only
+RFC850
+RFC1123
+RFC2822
+RFC3339
 
 S
 
+Server address
+Server port
 Settings
-   Connection timeout
+   Automatically synchronization
+   Connection time out
+   Daylight saving time
    Expert mode 
-   Interval
+   Interval between requests
    Logging
-   Popkey
-   Popup
-   Priority
+   Popkey
+   Popup
+   Priority
    Read only
    Server address
    Server port
    Threshold
-Standard time
-Summer time
+   Time zone
+   Time zone abbreviation
+   Time zone display
+   Time zone value
+Standard time
+Startup Sequence
+Summer time
 
 T
 
+Threshold
 TimeCtrl
 TimeLogger
-Time zone
+Time zone
   Getting
-  Setting
+  Setting
+Time zone abbreviation
+Time zone display
+Time zone value
 Tool types
-TZ
+TZ
+  Content
+  Setting
+TZONE
 
 
diff --git a/TimeKeeper.readme b/TimeKeeper.readme index 87c636c..b21a032 100644 --- a/TimeKeeper.readme +++ b/TimeKeeper.readme @@ -2,7 +2,7 @@ Short: Keep your time right Author: Carsten Larsen (carsten.larsen@mail.com) Uploader: Carsten Larsen (carsten.larsen@mail.com) Type: util/cdity -Version: 1.11 +Version: 1.12 Architecture: m68k-amigaos AmiTimeKeeper is a small program which keeps the time right on your @@ -21,29 +21,25 @@ try visiting support.ntp.org or one of the other sites listing time servers. Use a fixed pool server by prefixing the domain, for example, 1.pool.ntp.org. -The local time zone needs to be configured using Workbench preferences -or a similar tool. In AmigaOS 3.1 and 3.9 this is available through -the Locale Preferences Editor. Daylight saving time is only supported -through the POSIX TZ environment variable. +The time zone needs to be configured using Workbench preferences or +a similar tool. In Amiga OS this is available through the Locale +Preferences Editor. The local time zone and daylight saving time are +optionally specified manually or using a POSIX TZ variable style. The Icaros Desktop system is distributed with a custom build version of AmiTimeKeeper. The author encourages everyone with an interest to build their own version. -Version 1.11 has two known issues: - - Settings window in AROS is broken (and disabled in this version) - - Setting Popkey is not supported in ARexx +Please report bugs and other issues by sending a mail or using: +https://ranger.innolan.net/rainlance/AmiTimeKeeper -Changes in v1.11, 11.01.2021 - - New dedicated external control utility - - New ARexx port with a diverse set of commands - - Rewritten logger facility with more verbose output - - Bug preventing commodity to shutdown fixed - - Bug in CLI SWITCH parameters reporting wrong values fixed - - Support AmigaOS 1.3 (v34) but without settings window - - Time zone information displayed using GMT instead of UTC - - Only watch current env variable for time zone changes - - Improved validation of application settings - - Several dead and duplicated code blocks removed +v1.12 31.01.2021 + - New options to specify the local time zone + - New ARexx commands, parameters and error variables + - New option to start without active time synchronization + - New deactivate logging option for performance saving + - More bugs in calculation of time zone transition fixed + - Sanity check on value for interval between requests + - Updated ARexx documentation and script + - Honour ARexx result flag (RXFF_RESULT) - Various minor bug fixes and improvements - - Documentation in HTML format diff --git a/amiga.c b/amiga.c deleted file mode 100644 index 5bfd163..0000000 --- a/amiga.c +++ /dev/null @@ -1,743 +0,0 @@ -/* -** This file is in the public domain. -*/ - -#include "amiga.h" -#include "private.h" -#include -#include -#include -#include - -/* Amiga convert functions */ -void Amiga2Tm(ULONG seconds, struct tm *tm); -ULONG Tm2Amiga(struct tm *tm); -ULONG Unix2Amiga(time_t t); -time_t Amiga2Unix(ULONG t); -void Tm2DateStamp(struct tm *tm, struct DateStamp *date); -void DateStamp2Tm(struct DateStamp *date, struct tm *tm); -void Unix2DateStamp(time_t *time, struct DateStamp *date); -void DateStamp2Unix(struct DateStamp *date, time_t *time); -void Tm2Date(struct tm *tm, struct ClockData *date); -void Date2Tm(struct ClockData *date, struct tm *tm); -void Date2Unix(struct ClockData *date, time_t *time); -void Unix2Date(time_t *time, struct ClockData *date); - -static struct timerequest *TimeRequest; -static struct MsgPort *TimerPort; - -const struct lc_time_T C_time_locale = { - {"Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}, - {"January", "February", "March", "April", "May", "June", - "July", "August", "September", "October", "November", "December"}, - {"Sun", "Mon", "Tue", "Wed", - "Thu", "Fri", "Sat"}, - {"Sunday", "Monday", "Tuesday", "Wednesday", - "Thursday", "Friday", "Saturday"}, - - /* X_fmt */ - "%H:%M:%S", - - /* - ** x_fmt - ** C99 requires this format. - ** Using just numbers (as here) makes Quakers happier; - ** it's also compatible with SVR4. - */ - "%m/%d/%y", - - /* - ** c_fmt - ** C99 requires this format. - ** Previously this code used "%D %X", but we now conform to C99. - ** Note that - ** "%a %b %d %H:%M:%S %Y" - ** is used by Solaris 2.3. - */ - "%a %b %e %T %Y", - - /* t_fmt_ampm */ - "TODO", - - /* am */ - "AM", - - /* pm */ - "PM", - - /* am_pm */ - { "TODOAM", "TODOPM"}, - - /* date_fmt */ - "%a %b %e %H:%M:%S %Z %Y"}; - -static int -CreateTimer() -{ - int error; - - TimerPort = CreateMsgPort(); - if (TimerPort == NULL) - { - return 1; - } - - TimeRequest = (struct timerequest *)CreateIORequest( - TimerPort, - sizeof(struct timerequest)); - - if (TimeRequest == NULL) - { - DeleteMsgPort(TimerPort); - return 2; - } - - error = OpenDevice( - (STRPTR)TIMERNAME, - UNIT_MICROHZ, - (struct IORequest *)TimeRequest, - 0); - - if (error != 0) - { - DeleteIORequest((struct IORequest *)TimeRequest); - DeleteMsgPort(TimerPort); - return 3; - } - - TimerBase = (struct Device *)TimeRequest->tr_node.io_Device; - return 0; -} - -static void -DeleteTimer() -{ - TimerBase = NULL; - - if (TimeRequest != NULL) - { - if (!CheckIO((struct IORequest *)TimeRequest)) - { - WaitIO((struct IORequest *)TimeRequest); - } - - CloseDevice((struct IORequest *)TimeRequest); - DeleteIORequest((struct IORequest *)TimeRequest); - - TimeRequest = NULL; - } - - if (TimerPort != NULL) - { - DeleteMsgPort(TimerPort); - TimerPort = NULL; - } -} - -int gmtoffset(time_t locale_time) -{ - struct tm tm; - time_t time = locale_time; - localtime_r(&time, &tm); - return tm.tm_gmtoff; -} - -int gettimeofday(struct timeval *tv, void *tz) -{ - long zone; - - if (tv == NULL) - { - return -1; - } - - if (TimerSemaphore != NULL) - { - ObtainSemaphore(TimerSemaphore); - } - - if (CreateTimer() != 0) - { - if (TimerSemaphore != NULL) - { - ReleaseSemaphore(TimerSemaphore); - } - return -1; - } - - zone = gmtoffset(tv->tv_secs); - - TimeRequest->tr_node.io_Command = TR_GETSYSTIME; - DoIO((struct IORequest *)TimeRequest); - tv->tv_secs = (long)TimeRequest->tr_time.tv_secs + EPOCH_OFFSET + zone; - tv->tv_micro = TimeRequest->tr_time.tv_micro; - - DeleteTimer(); - - if (TimerSemaphore != NULL) - { - ReleaseSemaphore(TimerSemaphore); - } - - return 0; -} - -int settimeofday(const struct timeval *tv, void *tz) -{ - long zone; - - if (tv == NULL) - { - return -1; - } - - if (TimerSemaphore != NULL) - { - ObtainSemaphore(TimerSemaphore); - } - - if (CreateTimer() != 0) - { - if (TimerSemaphore != NULL) - { - ReleaseSemaphore(TimerSemaphore); - } - return -1; - } - - zone = gmtoffset(tv->tv_secs); - - TimeRequest->tr_node.io_Command = TR_SETSYSTIME; - TimeRequest->tr_time.tv_secs = (long)tv->tv_secs - EPOCH_OFFSET - zone; - TimeRequest->tr_time.tv_micro = tv->tv_micro; - DoIO((struct IORequest *)TimeRequest); - - DeleteTimer(); - - if (TimerSemaphore != NULL) - { - ReleaseSemaphore(TimerSemaphore); - } - - return 0; -} - -int getsystime(struct timeval *tv) -{ - if (tv == NULL) - { - return -1; - } - - if (TimerSemaphore != NULL) - { - ObtainSemaphore(TimerSemaphore); - } - - if (CreateTimer() != 0) - { - if (TimerSemaphore != NULL) - { - ReleaseSemaphore(TimerSemaphore); - } - - tv->tv_secs = 0; - tv->tv_micro = 0; - return -1; - } - -#ifdef AROS - TimeRequest->tr_node.io_Command = TR_GETSYSTIME; - DoIO((struct IORequest *)TimeRequest); - tv->tv_secs = TimeRequest->tr_time.tv_secs; - tv->tv_micro = 0; -#else - { - struct timeval tv1; - GetSysTime(&tv1); - *tv = tv1; - } -#endif - - DeleteTimer(); - - if (TimerSemaphore != NULL) - { - ReleaseSemaphore(TimerSemaphore); - } - - return 0; -} - -int setsystime(struct timeval *tv) -{ - if (tv == NULL) - { - return -1; - } - - if (TimerSemaphore != NULL) - { - ObtainSemaphore(TimerSemaphore); - } - - if (CreateTimer() != 0) - { - if (TimerSemaphore != NULL) - { - ReleaseSemaphore(TimerSemaphore); - } - return -1; - } - - TimeRequest->tr_node.io_Command = TR_SETSYSTIME; - TimeRequest->tr_time.tv_secs = (long)tv->tv_secs; - TimeRequest->tr_time.tv_micro = tv->tv_micro; - DoIO((struct IORequest *)TimeRequest); - - DeleteTimer(); - - if (TimerSemaphore != NULL) - { - ReleaseSemaphore(TimerSemaphore); - } - - return 0; -} - -#if defined(__AROS__) -int *__stdc_geterrnoptr(void) -{ - return &errno; -} -#endif - -int daylight_c(void) -{ - return daylight; -} - -long Timezone_c(void) -{ - return Timezone; -} - -long altzone_c(void) -{ - return altzone; -} - -char **tzname_c(void) -{ - return (char **)tzname; -} - -/* -struct lc_time_T *_current_locale() -{ - return (struct lc_time_T *)&C_time_locale; -} - -size_t -_strftime(const Timezone_t sp, char *const s, const size_t maxsize, - const char *const format, const struct tm *const t, struct lc_time_T *loc); - -size_t -strftime_lz(const Timezone_t sp, char *const s, const size_t maxsize, - const char *const format, const struct tm *const t, locale_t locale) -{ - size_t res; - struct lc_time_T *l = openlc(locale); - - res = _strftime(sp, s, maxsize, format, t, l); - - closelc(l); - return res; -} -*/ - -struct lc_time_T *openlc(locale_t locale) -{ - int i, j; - struct lc_time_T *l = (struct lc_time_T *)AllocMem( - sizeof(struct lc_time_T), - MEMF_ANY | MEMF_CLEAR); - - struct Locale *loc = locale; - - if (!loc) - { - loc = OpenLocale(NULL); - } - - l->X_fmt = (const char *)loc->loc_TimeFormat; - l->x_fmt = (const char *)loc->loc_ShortDateFormat; - l->c_fmt = (const char *)loc->loc_DateTimeFormat; - l->am = (const char *)GetLocaleStr(loc, AM_STR); - l->pm = (const char *)GetLocaleStr(loc, PM_STR); - l->date_fmt = "%a %b %e %H:%M:%S %Z %Y"; - - j = 0; - for (i = 1; i < 8; i++) - { - l->weekday[j++] = (const char *)GetLocaleStr(loc, i); - } - - j = 0; - for (i = 8; i < 15; i++) - { - l->wday[j++] = (const char *)GetLocaleStr(loc, i); - } - - j = 0; - for (i = 15; i < 27; i++) - { - l->month[j++] = (const char *)GetLocaleStr(loc, i); - } - - j = 0; - for (i = 27; i < 39; i++) - { - l->mon[j++] = (const char *)GetLocaleStr(loc, i); - } - - l->locale = loc; - - return l; -} - -void closelc(struct lc_time_T *lc) -{ - if (lc->locale) - { - CloseLocale(lc->locale); - } - - FreeMem(lc, sizeof(struct lc_time_T)); -} - -/* Similar to intuition.library CurrentTime */ -void CurrentTimeGmt(ULONG *seconds, ULONG *micros) -{ - ULONG sec, mic; - CurrentTime(&sec, &mic); - *seconds = sec + (daylight ? altzone : Timezone); - *micros = mic; -} - -struct tm *AmigaLocalTime(ULONG *seconds, struct tm *tm) -{ - time_t time; - if (tm == NULL) - { - return NULL; - } - - if (seconds == NULL) - { - /* Amiga Epoch time */ - Date2Tm(NULL, tm); - return tm; - } - - time = Amiga2Unix(*seconds); - localtime_r(&time, tm); - return tm; -} - -struct tm *AmigaLocalTimeZ(ULONG *seconds, struct tm *tm, const Timezone_t tz) -{ - time_t time; - if (tm == NULL) - { - return NULL; - } - - if (seconds == NULL) - { - /* Amiga Epoch time */ - Date2Tm(NULL, tm); - return tm; - } - - time = Amiga2Unix(*seconds); - localtime_rz(tz, &time, tm); - return tm; -} - -/* Similar to locale.library FormatDate */ -void FormatDateTm(char *const s, const char *format, struct tm *tm, - char *buffer, long maxsize) -{ - strftime(s, maxsize, format, tm); -} - -/* Similar to locale.library ParseDate */ -int ParseDateTm(struct Locale *locale, const char *format, struct tm *tm, - const char *string) -{ - // TODO - return 0; -} - -/* Similar to utility.library Amiga2Date. - Fills in a tm structure with the date and time calculated - from a ULONG containing the number of seconds from 01-Jan-1978 - to the date. */ -void Amiga2Tm(ULONG seconds, struct tm *tm) -{ - struct tm tmp; - time_t t; - if (tm == NULL) - { - return; - } - - t = seconds + EPOCH_OFFSET; - localtime_r(&t, &tmp); - t = seconds + EPOCH_OFFSET - tmp.tm_gmtoff; - localtime_r(&t, tm); -} - -/* Similar to utility.library Date2Amiga. - Calculates the number of seconds from 01-Jan-1978 to the date - specified in the tm structure. */ -ULONG Tm2Amiga(struct tm *tm) -{ - time_t time; - if (tm == NULL) - { - /* Amiga Epoch time */ - return 0; - } - - time = timelocal(tm); - return time - EPOCH_OFFSET; -} - -/* Convert between Unix Time Epoch and Amiga Time Epoch */ -ULONG Unix2Amiga(time_t t) -{ - return t - EPOCH_OFFSET; -} - -time_t Amiga2Unix(ULONG t) -{ - return t + EPOCH_OFFSET; -} - -/* Convert to and from DateStamp */ -void Tm2DateStamp(struct tm *tm, struct DateStamp *date) -{ - time_t time; - struct tm tmp = *tm; - - if (date == NULL) - { - return; - } - - if (tm == NULL) - { - /* UNIX Epoch time */ - time = 0; - } - else - { - time = mktime(&tmp); - } - - Unix2DateStamp(&time, date); -} - -void DateStamp2Tm(struct DateStamp *date, struct tm *tm) -{ - time_t time; - if (tm == NULL) - { - return; - } - - if (date == NULL) - { - Date2Tm(NULL, tm); - return; - } - - DateStamp2Unix(date, &time); - gmtime_r(&time, tm); -} - -void Unix2DateStamp(time_t *time, struct DateStamp *date) -{ - long t, d, m, q; - if (date == NULL) - { - return; - } - - if (time == NULL) - { - /* Amiga Epoch time */ - date->ds_Days = 0; - date->ds_Minute = 0; - date->ds_Tick = 0; - return; - } - - t = *time - EPOCH_OFFSET; - d = t / SECONDSPERDAY; - m = (t - SECONDSPERDAY * d) / 60; - q = (t - SECONDSPERDAY * d - m * 60) * 60; // TODO: Ticks = 50 - - date->ds_Days = d; - date->ds_Minute = m; - date->ds_Tick = q; -} - -void DateStamp2Unix(struct DateStamp *date, time_t *time) -{ - if (time == NULL) - { - return; - } - - if (date == NULL) - { - /* Amiga Epoch time */ - *time = EPOCH_OFFSET; - return; - } - - *time = date->ds_Days * SECONDSPERDAY + - date->ds_Minute * 60 + - date->ds_Tick / 60 + - EPOCH_OFFSET; -} - -/* Convert from tm to ClockData */ -void Tm2Date(struct tm *tm, struct ClockData *date) -{ - if (date == NULL) - { - return; - } - - if (tm == NULL) - { - /* UNIX Epoch time */ - date->sec = 0; - date->min = 0; - date->hour = 0; - date->mday = 1; - date->month = 1; - date->year = EPOCH_YEAR; - date->wday = EPOCH_WDAY; - return; - } - - date->sec = (UWORD)tm->tm_sec; - date->min = (UWORD)tm->tm_min; - date->hour = (UWORD)tm->tm_hour; - date->mday = (UWORD)tm->tm_mday; - date->month = (UWORD)tm->tm_mon + 1; - date->year = (UWORD)tm->tm_year + TM_YEAR_BASE + EPOCH_OFFSET_YEAR; - date->wday = (UWORD)tm->tm_wday; - - /* tm allow seconds in interval [0-61] */ - if (date->sec > 59) - { - date->sec = 59; - } -} - -/* Convert from ClockData to tm */ -void Date2Tm(struct ClockData *date, struct tm *tm) -{ - if (tm == NULL) - { - return; - } - - if (date == NULL) - { - /* Amiga Epoch time */ - tm->tm_sec = 0; - tm->tm_min = 0; - tm->tm_hour = 0; - tm->tm_mday = 1; - tm->tm_mon = 0; - tm->tm_year = EPOCH_YEAR + EPOCH_OFFSET_YEAR - TM_YEAR_BASE; - mktime(tm); - return; - } - - tm->tm_sec = (int)date->sec; - tm->tm_min = (int)date->min; - tm->tm_hour = (int)date->hour; - tm->tm_mday = (int)date->mday; - tm->tm_mon = (int)date->month - 1; - tm->tm_year = (int)date->year - TM_YEAR_BASE - EPOCH_OFFSET_YEAR; - tm->tm_wday = (int)date->wday; - mktime(tm); -} - -void Date2Unix(struct ClockData *date, time_t *time) -{ - struct tm tm; - if (time == NULL) - { - return; - } - - if (date == NULL) - { - /* UNIX Epoch time */ - *time = 0; - return; - } - - Date2Tm(date, &tm); - mktime(&tm); -} - -void Unix2Date(time_t *time, struct ClockData *date) -{ - struct tm tm; - time_t t; - if (date == NULL) - { - return; - } - - if (time == NULL) - { - /* UNIX Epoch time */ - Tm2Date(NULL, date); - return; - } - - t = *time; - gmtime_r(&t, &tm); - Tm2Date(&tm, date); -} - -#undef time -#undef time_t - -time_t -time(time_t *x) -{ - time_t tloc; - ULONG sec, mic; - CurrentTime(&sec, &mic); - tloc = (time_t)(sec); - - if (x) - { - *x = tloc; - } - - return tloc; -} diff --git a/amiga.h b/amiga.h deleted file mode 100644 index c6bcb99..0000000 --- a/amiga.h +++ /dev/null @@ -1,157 +0,0 @@ -/* -** This file is in the public domain. -*/ - -#ifndef AMIGA_H -#define AMIGA_H - -/* Used by localtime.c but not by amiga */ -#define O_CLOEXEC 0 -#define _DIAGASSERT(x) -#define __aconst - -/* Used by amiga.c */ -#define SECONDSPERDAY 86400 - -/* - * The following macro is used to remove const cast-away warnings - * from gcc -Wcast-qual; it should be used with caution because it - * can hide valid errors; in particular most valid uses are in - * situations where the API requires it, not to cast away string - * constants. We don't use *intptr_t on purpose here and we are - * explicit about unsigned long so that we don't have additional - * dependencies. - */ -#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) - -#define NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU 1 -#define HAVE_STRFTIME_L 0 - -/* Thread safety */ -#define rwlock_wrlock(x) -#define rwlock_unlock(x) - -#define TIME_MAX ULONG_MAX - -#include -#include -#include "mem.h" - -#define TZLIB_NAME "tz.library" -#define VERSION_NO 7 -#define VERSION_TEXT "7" -#define REVISION_NO 00 -#define REVISION_TEXT "00" -#define DATE_TEXT "10.01.2018" -#define VERS TZLIB_NAME " " VERSION_TEXT "." REVISION_TEXT -#define VSTRING TZLIB_NAME " " VERSION_TEXT "." REVISION_TEXT \ - "(" DATE_TEXT ")\r\n" -#define VERSTAG "\0$VER: " TZLIB_NAME " " VERSION_TEXT "." REVISION_TEXT \ - "(" DATE_TEXT ")" - -#define TZVARIABLE1 "TZ" -#define TZVARIABLE2 "Timezone.prefs" -#define TZVARIABLE TZVARIABLE1 -#define ENVSIZE 128 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef REG -#define REG(reg, arg) arg __asm(#reg) -#endif - -#ifndef UNUSED -#define UNUSED __attribute__((unused)) -#endif - -typedef struct Locale *locale_t; - -struct lc_time_T -{ - const char *mon[12]; - const char *month[12]; - const char *wday[7]; - const char *weekday[7]; - const char *X_fmt; - const char *x_fmt; - const char *c_fmt; - const char *t_fmt_ampm; // TODO - const char *am; - const char *pm; - const char *am_pm[2]; // TODO - const char *date_fmt; - locale_t locale; -}; - -#undef ctime_rz -#define ctime_rz tz_ctime_rz -#undef tzgetgmtoff -#define tzgetgmtoff tz_tzgetgmtoff -#undef timelocal_z -#define timelocal_z tz_timelocal_z -#undef tzgetname -#define tzgetname tz_tzgetname -#undef offtime_r -#define offtime_r tz_offtime_r -#undef tzgetlocation -#define tzgetlocation tz_tzgetlocation -#undef snprintf -#define snprintf SNPrintf - -/* amiga.c tz functions */ -int daylight_c(void); -long Timezone_c(void); -long altzone_c(void); -char **tzname_c(void); -int getsystime(struct timeval *tv); -int setsystime(struct timeval *tv); -int gettimeofday(struct timeval *tv, void *tz); -int settimeofday(const struct timeval *tv, void *tz); - -/* amiga.c internals */ -struct lc_time_T *openlc(locale_t locale); -void closelc(struct lc_time_T *lc); -extern const struct lc_time_T C_time_locale; -extern long Timezone; -extern long altzone; -extern int daylight; -extern struct SignalSemaphore *TimerSemaphore; - -/* string.c */ -void bcopy(const void *src, void *dest, size_t len); // -void *memmove(void *dest, const void *src, size_t len); // -size_t strlen(const char *string); // -char *strcpy(char *dest, const char *src); // -char *strcat(char *dest, const char *src); // -int strcmp(const char *s1, const char *s2); // -char *strchr(const char *s, int c); // -int strncmp(const char *s1, const char *s2, size_t n); // -char *getenv(const char *name); // -int isdigit(char c); -int isspace(char c); -int isalpha(char c); -int toupper(char c); -int strncasecmp(const char *_s1, const char *_s2, size_t n); -int SNPrintf(char *str, size_t size, const char *format, ...); - -#if defined(__GNUC__) && (__GNUC__ < 3) -void *memcpy(void *dst, const void *src, unsigned long len); -#else -void *memcpy(void *dst, const void *src, size_t len); -#endif -void *memset(void *str, int c, size_t n); - -extern struct ExecBase *SysBase; -extern int errno; - -#endif diff --git a/arexx.c b/arexx.c index ac0c653..22ef3a0 100644 --- a/arexx.c +++ b/arexx.c @@ -38,6 +38,8 @@ #include "tz.h" #include +#include +#include #include #include @@ -46,64 +48,66 @@ static char *arexxHelpText = NULL; -typedef void (*ArexxCommand)(struct RexxMsg *, char **); +typedef void (*ARexxCommand)(struct RexxMsg *, char **); -struct ArexxCommandDef +struct ARexxCommandDef { + /* Unique numeric command identifier */ const int Identifier; + /* Command name as exposed in ARexx interface */ const char *Name; + /* Short description of command purpose */ const char *Description; - ArexxCommand Command; + /* Function pointer to action code */ + ARexxCommand Command; + /* True if command only return a result. False is command is an action */ + bool ResultCommand; + /* Number of parameters / arguments */ int Parameters; }; -struct ArexxParameterDef +struct ARexxParameterDef { const int Identifier; const char *Keyword; }; -static void ArexxIdentifierCommand(struct RexxMsg *, char **); -static void ArexxVersionCommand(struct RexxMsg *, char **); -static void ArexxHelpCommand(struct RexxMsg *, char **); -static void ArexxStatusCommand(struct RexxMsg *, char **); -static void ArexxTimezoneCommand(struct RexxMsg *msg, char **); -static void ArexxLastSyncCommand(struct RexxMsg *msg, char **); -static void ArexxLastAdjustCommand(struct RexxMsg *msg, char **); -static void ArexxGetValueCommand(struct RexxMsg *, char **); -static void ArexxSetValueCommand(struct RexxMsg *, char **); -static void ArexxShowCommand(struct RexxMsg *, char **); -static void ArexxHideCommand(struct RexxMsg *, char **); -static void ArexxStartCommand(struct RexxMsg *, char **); -static void ArexxStopCommand(struct RexxMsg *, char **); -static void ArexxShutdownCommand(struct RexxMsg *, char **); +static void ARexxIdentifierCommand(struct RexxMsg *, char **); +static void ARexxVersionCommand(struct RexxMsg *, char **); +static void ARexxHelpCommand(struct RexxMsg *, char **); +static void ARexxStatusCommand(struct RexxMsg *, char **); +static void ARexxTimezoneCommand(struct RexxMsg *msg, char **); +static void ARexxLastSyncCommand(struct RexxMsg *msg, char **); +static void ARexxLastAdjustCommand(struct RexxMsg *msg, char **); +static void ARexxGetValueCommand(struct RexxMsg *, char **); +static void ARexxSetValueCommand(struct RexxMsg *, char **); +static void ARexxNowCommand(struct RexxMsg *, char **); +static void ARexxShowCommand(struct RexxMsg *, char **); +static void ARexxHideCommand(struct RexxMsg *, char **); +static void ARexxStartCommand(struct RexxMsg *, char **); +static void ARexxStopCommand(struct RexxMsg *, char **); +static void ARexxShutdownCommand(struct RexxMsg *, char **); +static void ARexxLogTransitionsCommand(struct RexxMsg *, char **); -#define AREXX_COMMAND_OK 0 -#define AREXX_COMMAND_ERROR 1 -#define AREXX_COMMAND_UNKNOWN 10 -#define AREXX_COMMAND_PARM_UNKNOWN 11 -#define AREXX_COMMAND_PARM_INVALID 12 -#define AREXX_COMMAND_SHUTDOWN 99 +#define AREXX_COMMAND_COUNT 16 +static struct ARexxCommandDef arexxCommands[] = { + {1, "ID", "Get application identifier", ARexxIdentifierCommand, true, 0}, + {2, "VERSION", "Get application version number", ARexxVersionCommand, true, 0}, + {3, "HELP", "Get a list of ARexx commands and a short description", ARexxHelpCommand, true, 0}, + {4, "STATUS", "Get current synchronization status", ARexxStatusCommand, true, 1}, + {5, "TIMEZONE", "Get current time zone", ARexxTimezoneCommand, true, 1}, + {6, "LASTSYNC", "Get time of last synchronization operation", ARexxLastSyncCommand, true, 2}, + {7, "LASTADJ", "Get time of last clock adjustment", ARexxLastAdjustCommand, true, 2}, + {8, "GET", "Get a runtime configuration value", ARexxGetValueCommand, true, 1}, + {9, "SET", "Set a runtime configuration value", ARexxSetValueCommand, false, 2}, + {10, "NOW", "Get current time and date", ARexxNowCommand, false, 2}, + {11, "SHOW", "Show settings window", ARexxShowCommand, false, 0}, + {12, "HIDE", "Hide settings window", ARexxHideCommand, false, 0}, + {13, "START", "Start the time synchronization process", ARexxStartCommand, false, 0}, + {14, "STOP", "Stop the time synchronization process", ARexxStopCommand, false, 0}, + {15, "SHUTDOWN", "Shutdown " APP_SHORT_NAME, ARexxShutdownCommand, false, 0}, + {16, "LOGTRANS", "Emit time zone transition map to log", ARexxLogTransitionsCommand, false, 0}}; -static struct ArexxCommandDef arexxCommands[] = { - {1, "ID", "Get application identifier", ArexxIdentifierCommand, 0}, - {2, "VERSION", "Get application version number", ArexxVersionCommand}, - {3, "HELP", "Get a list of ARexx commands and a short description", ArexxHelpCommand, 0}, - {4, "STATUS", "Get current synchronization status", ArexxStatusCommand, 0}, - {5, "TIMEZONE", "Get current time zone", ArexxTimezoneCommand, 0}, - {6, "LASTSYNC", "Get time of last synchronization operation", ArexxLastSyncCommand, 0}, - {7, "LASTADJ", "Get time of last clock adjustment", ArexxLastAdjustCommand, 0}, - {8, "GET", "Get a runtime configuration value", ArexxGetValueCommand, 1}, - {9, "SET", "Set a runtime configuration value", ArexxSetValueCommand, 2}, - {10, "SHOW", "Show settings window", ArexxShowCommand, 0}, - {11, "HIDE", "Hide settings window", ArexxHideCommand, 0}, - {12, "START", "Start the time synchronization process", ArexxStartCommand, 0}, - {13, "STOP", "Stop the time synchronization process", ArexxStopCommand, 0}, - {14, "SHUTDOWN", "Shutdown " APP_SHORT_NAME, ArexxShutdownCommand, 0}}; - -#define AREXX_COMMAND_COUNT 14 - -#define AREXX_PARAMETER_UNKNOWN 0 #define AREXX_PARAMETER_SERVER 1 #define AREXX_PARAMETER_PORT 2 #define AREXX_PARAMETER_THRESHOLD 3 @@ -114,8 +118,21 @@ static struct ArexxCommandDef arexxCommands[] = { #define AREXX_PARAMETER_READONLY 8 #define AREXX_PARAMETER_EXPERT 9 #define AREXX_PARAMETER_TIMEOUT 10 +#define AREXX_PARAMETER_ACTIVE 11 +#define AREXX_PARAMETER_NOLOG 12 +#define AREXX_PARAMETER_TZ 13 +#define AREXX_PARAMETER_TZD 14 +#define AREXX_PARAMETER_TZNAME 15 +#define AREXX_PARAMETER_TZVALUE 16 +#define AREXX_PARAMETER_TZDST 17 +#define AREXX_PARAMETER_LAST 17 -static struct ArexxParameterDef arexxParameters[] = { +#define AREXX_PARAMETER_UNKNOWN 90 +#define AREXX_PARAMETER_MISSING 91 +#define AREXX_PARAMETER_SHUTDOWN 92 + +#define AREXX_PARAMETER_COUNT AREXX_PARAMETER_LAST +static struct ARexxParameterDef arexxParameters[] = { {AREXX_PARAMETER_SERVER, KEYWORD_SERVER}, {AREXX_PARAMETER_PORT, KEYWORD_PORT}, {AREXX_PARAMETER_THRESHOLD, KEYWORD_THRESHOLD}, @@ -125,12 +142,43 @@ static struct ArexxParameterDef arexxParameters[] = { {AREXX_PARAMETER_POPUP, KEYWORD_POPUP}, {AREXX_PARAMETER_READONLY, KEYWORD_READONLY}, {AREXX_PARAMETER_EXPERT, KEYWORD_EXPERT}, - {AREXX_PARAMETER_TIMEOUT, KEYWORD_TIMEOUT}}; - -#define AREXX_PARAMETER_COUNT 10 + {AREXX_PARAMETER_TIMEOUT, KEYWORD_TIMEOUT}, + {AREXX_PARAMETER_ACTIVE, KEYWORD_ACTIVE}, + {AREXX_PARAMETER_NOLOG, KEYWORD_NOLOG}, + {AREXX_PARAMETER_TZ, KEYWORD_TZ}, + {AREXX_PARAMETER_TZD, KEYWORD_TZD}, + {AREXX_PARAMETER_TZNAME, KEYWORD_TZNAME}, + {AREXX_PARAMETER_TZVALUE, KEYWORD_TZVALUE}, + {AREXX_PARAMETER_TZDST, KEYWORD_TZDST}}; #define AREXX_MAX_ARGS_COUNT 8 +struct ARexxErrorDef +{ + const long Code; + const char **Text; +}; + +#define AREXX_ERROR_UNKNOWN_CMD 10 /* Unknown ARexx command */ +#define AREXX_ERROR_UNKNOWN_PARM 11 /* Unknown parameter */ +#define AREXX_ERROR_PARM_MISS 12 /* Parameter is missing */ +#define AREXX_ERROR_PARM_INV 13 /* Invalid parameter value */ +#define AREXX_ERROR_CLOCK_INV 18 /* Clock has not been adjusted */ +#define AREXX_ERROR_SYNC_INV 19 /* No responses from NTP server */ +#define AREXX_ERROR_SYNC_ON 20 /* Synchronization is already active */ +#define AREXX_ERROR_SYNC_OFF 21 /*Synchronization is already deactivated */ + +#define AREXX_ERROR_COUNT 8 +static struct ARexxErrorDef arexxErrors[] = { + {AREXX_ERROR_UNKNOWN_CMD, &TextARexxCmdUnknown}, + {AREXX_ERROR_UNKNOWN_PARM, &TextARexxParmUnknown}, + {AREXX_ERROR_PARM_MISS, &TextARexxParmMiss}, + {AREXX_ERROR_PARM_INV, &TextARexxParmInvalid}, + {AREXX_ERROR_CLOCK_INV, &TextNoClockAdjust}, + {AREXX_ERROR_SYNC_INV, &TextNoResponses}, + {AREXX_ERROR_SYNC_ON, &TextSyncAlreadyOn}, + {AREXX_ERROR_SYNC_OFF, &TextSyncAlreadyOff}}; + static void SplitAndTerminate(char *string, char **res) { char *p = string; @@ -164,7 +212,7 @@ static void SplitAndTerminate(char *string, char **res) } } -static void BuildArexxHelpText(void) +static void BuildARexxHelpText(void) { char *p; int i, line, len = 0; @@ -172,8 +220,9 @@ static void BuildArexxHelpText(void) if (arexxHelpText != NULL) return; - len += 26; // Header - len += 71 * 2; // Spacer + len += 26; // Header + len += 71 * 2; // Spacer + len += StrLen(AREXX_ERROR_VAR_CODE) + StrLen(AREXX_ERROR_VAR_TEXT) + 32; // Footer for (i = 0; i < AREXX_COMMAND_COUNT; i++) { @@ -201,9 +250,15 @@ static void BuildArexxHelpText(void) } p = AppendChar(p, '-', 70); + p = AppendChar(p, '\n', 1); + + p = AppendText(p, "Error status is set in "); + p = AppendText(p, AREXX_ERROR_VAR_CODE); + p = AppendText(p, " and "); + p = AppendText(p, AREXX_ERROR_VAR_TEXT); } -void CleanupArexx(void) +void CleanupARexx(void) { if (arexxHelpText != NULL) { @@ -212,75 +267,209 @@ void CleanupArexx(void) } } -static void ArexxIdentifierCommand(struct RexxMsg *msg, char **parms) +static void SetARexxVar(struct RexxMsg *msg, const char *var, const char *text) { - msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)APP_TITLE, (LONG)StrLen(APP_TITLE)); -} - -static void ArexxVersionCommand(struct RexxMsg *msg, char **parms) -{ - msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)APP_VERSION, (LONG)StrLen(APP_VERSION)); -} - -static void ArexxHelpCommand(struct RexxMsg *msg, char **parms) -{ - BuildArexxHelpText(); - msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)arexxHelpText, (LONG)StrLen(arexxHelpText)); -} - -static void ArexxStatusCommand(struct RexxMsg *msg, char **parms) -{ - if (SynchronizerRunning) - msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)TextSyncOn, (LONG)StrLen(TextSyncOn)); + LONG error = SetRexxVar(msg, (CONST_STRPTR)var, (CONST_STRPTR)text, (LONG)StrLen(text)); + if (error != 0) + { + LogWarn(TextARexxVarError, (long)error); + LogWarn(TextARexxVarValue, var, text); + } else - msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)TextSyncOff, (LONG)StrLen(TextSyncOff)); + { + LogTrace(TextARexxVarValue, var, text); + } } -static void ArexxTimezoneCommand(struct RexxMsg *msg, char **parms) +static void ARexxErrorClear(struct RexxMsg *msg) { - char Timezone[TIMEZONE_TEXT_LEN]; - GetTimezoneText(AppLocale, Timezone, UTC_NO_PARENS); - msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)Timezone, (LONG)StrLen(Timezone)); + SetARexxVar(msg, AREXX_ERROR_VAR_CODE, "0"); + SetARexxVar(msg, AREXX_ERROR_VAR_TEXT, ""); } -static void ArexxLastSyncCommand(struct RexxMsg *msg, char **parms) +static void ARexxError(struct RexxMsg *msg, long code) +{ + char errorCode[MAXLONGCHARSIZE]; + LongToStr(code, errorCode); + SetARexxVar(msg, AREXX_ERROR_VAR_CODE, errorCode); + + int i; + for (i = 0; i < AREXX_ERROR_COUNT; i++) + { + if (arexxErrors[i].Code == code) + { + const char *text = *(arexxErrors[i].Text); + SetARexxVar(msg, AREXX_ERROR_VAR_TEXT, text); + break; + } + } +} + +static void ARexxResult(struct RexxMsg *msg, const char *text) +{ + if (msg->rm_Action & RXFF_RESULT) + { + msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)text, (LONG)StrLen(text)); + } + + LogTrace(TextARexxResult, text); + ARexxErrorClear(msg); +} + +static void ARexxResultLong(struct RexxMsg *msg, long value) +{ + char text[MAXLONGCHARSIZE]; + SNPrintf(text, MAXLONGCHARSIZE, "%ld", value); + ARexxResult(msg, text); +} + +static void ARexxResultBool(struct RexxMsg *msg, char *parm, long value) +{ + char text[MAXBOOLCHARSIZE]; + if (Stricmp((STRPTR)parm, (STRPTR)KEYWORD_NUMBER) == 0) + BoolToStrNum(value, text); + else + BoolToStr(value, text); + ARexxResult(msg, text); +} + +static void ARexxTimeResult(struct RexxMsg *msg, char **parms, struct timeval *utv, long error) +{ + if (utv->tv_secs == 0 && utv->tv_micro == 0) + { + ARexxError(msg, error); + return; + } + + bool utc; + STRPTR format = (STRPTR)*parms++; + if (Stricmp((STRPTR)*parms, (STRPTR)KEYWORD_UTC) == 0) + { + utc = true; + } + else if (*parms == NULL || Stricmp((STRPTR)*parms, (STRPTR)KEYWORD_LOCAL) == 0) + { + utc = false; + } + else + { + ARexxError(msg, AREXX_ERROR_PARM_INV); + return; + } + + char result[DATETIME_TEXT_LEN + 1]; + if (format == NULL || Stricmp(format, (STRPTR)KEYWORD_DOS) == 0) + Utc2DOS(utv, result, utc); + else if (Stricmp(format, (STRPTR)KEYWORD_ASCII) == 0) + Utc2ASCII(utv, result, utc); + else if (Stricmp(format, (STRPTR)KEYWORD_DATE) == 0) + Utc2ARexxDate(utv, result, utc); + else if (Stricmp(format, (STRPTR)KEYWORD_TIME) == 0) + Utc2ARexxTime(utv, result, utc); + else if (Stricmp(format, (STRPTR)KEYWORD_RFC850) == 0) + Utc2RFC850(utv, result); + else if (Stricmp(format, (STRPTR)KEYWORD_RFC1123) == 0) + Utc2RFC1123(utv, result); + else if (Stricmp(format, (STRPTR)KEYWORD_RFC2822) == 0) + Utc2RFC2822(utv, result, utc); + else if (Stricmp(format, (STRPTR)KEYWORD_RFC3339) == 0) + Utc2RFC3339(utv, result, utc); + else if (Stricmp(format, (STRPTR)KEYWORD_ISO8601) == 0) + Utc2ISO8601(utv, result, utc); + else + { + ARexxError(msg, AREXX_ERROR_PARM_INV); + return; + } + + ARexxResult(msg, result); +} + +static void ARexxIdentifierCommand(struct RexxMsg *msg, char **parms) +{ + ARexxResult(msg, APP_TITLE); +} + +static void ARexxVersionCommand(struct RexxMsg *msg, char **parms) +{ + ARexxResult(msg, APP_VERSION); +} + +static void ARexxHelpCommand(struct RexxMsg *msg, char **parms) +{ + BuildARexxHelpText(); + ARexxResult(msg, arexxHelpText); +} + +static void ARexxStatusCommand(struct RexxMsg *msg, char **parms) +{ + if (Stricmp((STRPTR)*parms, (STRPTR)KEYWORD_NUMBER) == 0) + { + char buf[4]; + BoolToStrNum(SynchronizerRunning, buf); + ARexxResult(msg, buf); + } + else if (*parms == NULL) + { + const char *result = SynchronizerRunning ? TextSyncOn : TextSyncOff; + ARexxResult(msg, result); + } + else + { + ARexxError(msg, AREXX_ERROR_PARM_INV); + } +} + +static void ARexxTimezoneCommand(struct RexxMsg *msg, char **parms) +{ + long val; + if (parms == NULL) + { + ARexxError(msg, AREXX_ERROR_PARM_MISS); + } + else if (!TryParseLong(*parms, &val)) + { + ARexxError(msg, AREXX_ERROR_PARM_INV); + } + else if (val < 1 || val > 12) + { + ARexxError(msg, AREXX_ERROR_PARM_INV); + } + else + { + char buf[TIMEZONE_TEXT_LEN]; + GetTimezoneText(buf, val); + ARexxResult(msg, buf); + } +} + +static void ARexxNowCommand(struct RexxMsg *msg, char **parms) +{ + struct timeval tv; + GetTimeOfDay(&tv); + ARexxTimeResult(msg, parms, &tv, AREXX_ERROR_SYNC_INV); +} + +static void ARexxLastSyncCommand(struct RexxMsg *msg, char **parms) { struct timeval tv = LastSync; - if (tv.tv_sec == 0 && tv.tv_micro == 0) - { - msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)TextNoResponses, (LONG)StrLen(TextNoResponses)); - } - else - { - char result[64]; - Utc2Str(&tv, result); - msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)result, (LONG)StrLen(result)); - } + ARexxTimeResult(msg, parms, &tv, AREXX_ERROR_SYNC_INV); } -static void ArexxLastAdjustCommand(struct RexxMsg *msg, char **parms) +static void ARexxLastAdjustCommand(struct RexxMsg *msg, char **parms) { struct timeval tv = LastAdjust; - if (tv.tv_sec == 0 && tv.tv_micro == 0) - { - msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)TextNoClockAdjust, (LONG)StrLen(TextNoClockAdjust)); - } - else - { - char result[64]; - Utc2Str(&tv, result); - msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)result, (LONG)StrLen(result)); - } + ARexxTimeResult(msg, parms, &tv, AREXX_ERROR_CLOCK_INV); } -static int ArexxFindParameter(char **parms) +static int ARexxFindParameter(char **parms) { int i; char *parm = parms[0]; if (parm == NULL || *parm == '\0') { - return AREXX_PARAMETER_UNKNOWN; + return AREXX_PARAMETER_MISSING; } for (i = 0; i < AREXX_PARAMETER_COUNT; i++) @@ -294,68 +483,84 @@ static int ArexxFindParameter(char **parms) return AREXX_PARAMETER_UNKNOWN; } -static void ArexxGetValueCommand(struct RexxMsg *msg, char **parms) +static void ARexxGetValueCommand(struct RexxMsg *msg, char **parms) { - char buf[MAXLONGLONGCHARSIZE]; - char *res; - int param = ArexxFindParameter(parms); - + int param = ARexxFindParameter(parms++); switch (param) { case AREXX_PARAMETER_UNKNOWN: - msg->rm_Result1 = AREXX_COMMAND_PARM_UNKNOWN; + ARexxError(msg, AREXX_ERROR_UNKNOWN_PARM); + break; + case AREXX_PARAMETER_MISSING: + ARexxError(msg, AREXX_ERROR_PARM_MISS); break; case AREXX_PARAMETER_SERVER: - res = Settings->DestinationAddress; + ARexxResult(msg, Settings->DestinationAddress); break; case AREXX_PARAMETER_PORT: - res = Settings->DestinationPort; - break; - case AREXX_PARAMETER_THRESHOLD: - LongLongToStr(Settings->Threshold, buf); - res = buf; + ARexxResult(msg, Settings->DestinationPort); break; case AREXX_PARAMETER_INTERVAL: - LongToStr(Settings->Interval, buf); - res = buf; + ARexxResultLong(msg, Settings->Interval); break; case AREXX_PARAMETER_PRIORITY: - LongToStr(Settings->Priority, buf); - res = buf; + ARexxResultLong(msg, Settings->Priority); break; case AREXX_PARAMETER_POPKEY: - res = Settings->PopKey; + ARexxResult(msg, Settings->PopKey); break; case AREXX_PARAMETER_POPUP: - BoolToStr(Settings->Popup, buf); - res = buf; + ARexxResultBool(msg, *parms, Settings->Popup); break; case AREXX_PARAMETER_READONLY: - BoolToStr(Settings->Readonly, buf); - res = buf; + ARexxResultBool(msg, *parms, Settings->Readonly); break; case AREXX_PARAMETER_EXPERT: - BoolToStr(Settings->Expert, buf); - res = buf; + ARexxResultBool(msg, *parms, Settings->Expert); break; case AREXX_PARAMETER_TIMEOUT: - LongToStr(Settings->Timeout, buf); - res = buf; + ARexxResultLong(msg, Settings->Timeout); break; - default: - msg->rm_Result1 = AREXX_COMMAND_PARM_UNKNOWN; + case AREXX_PARAMETER_ACTIVE: + ARexxResultBool(msg, *parms, SynchronizerRunning); break; - } - - if (msg->rm_Result1 != AREXX_COMMAND_PARM_UNKNOWN) + case AREXX_PARAMETER_NOLOG: + ARexxResultBool(msg, *parms, Settings->NoLog); + break; + case AREXX_PARAMETER_TZ: + ARexxResult(msg, Settings->TZ); + break; + case AREXX_PARAMETER_TZD: + ARexxResultLong(msg, Settings->TimeZoneDisplay); + break; + case AREXX_PARAMETER_TZNAME: + ARexxResult(msg, Settings->TimeZoneName); + break; + case AREXX_PARAMETER_TZVALUE: + if (Settings->TimeZoneValue != TZVALUE_DEF) + ARexxResultLong(msg, Settings->TimeZoneValue); + else + ARexxResult(msg, ""); + break; + case AREXX_PARAMETER_TZDST: + ARexxResultLong(msg, Settings->TimeZoneDst); + break; + case AREXX_PARAMETER_THRESHOLD: { - msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)res, (LONG)StrLen(res)); + char buf[MAXLONGLONGCHARSIZE]; + LongLongToStr(Settings->Threshold, buf); + ARexxResult(msg, buf); + } + break; + default: + ARexxError(msg, AREXX_ERROR_UNKNOWN_PARM); + break; } } -static void ArexxSetValueCommand(struct RexxMsg *msg, char **parms) +static void ARexxSetValueCommand(struct RexxMsg *msg, char **parms) { - int param = ArexxFindParameter(parms++); + int param = ARexxFindParameter(parms++); char *valueString = *parms; long long longlongValue; long longValue; @@ -364,47 +569,68 @@ static void ArexxSetValueCommand(struct RexxMsg *msg, char **parms) switch (param) { case AREXX_PARAMETER_UNKNOWN: - msg->rm_Result1 = AREXX_COMMAND_PARM_UNKNOWN; + ARexxError(msg, AREXX_ERROR_UNKNOWN_PARM); + break; + case AREXX_PARAMETER_MISSING: + ARexxError(msg, AREXX_ERROR_PARM_MISS); break; case AREXX_PARAMETER_SERVER: SetServer(valueString, APPLY_VALUE); + ARexxErrorClear(msg); break; case AREXX_PARAMETER_PORT: SetPort(valueString, APPLY_VALUE); + ARexxErrorClear(msg); break; case AREXX_PARAMETER_THRESHOLD: if (TryParseLongLong(valueString, &longlongValue)) + { SetThreshold(valueString, APPLY_VALUE); + ARexxErrorClear(msg); + } else - msg->rm_Result1 = AREXX_COMMAND_PARM_INVALID; + ARexxError(msg, AREXX_ERROR_PARM_INV); break; case AREXX_PARAMETER_INTERVAL: if (TryParseLong(valueString, &longValue)) + { SetInterval(longValue, APPLY_VALUE); + ARexxErrorClear(msg); + } else - msg->rm_Result1 = AREXX_COMMAND_PARM_INVALID; + ARexxError(msg, AREXX_ERROR_PARM_INV); break; case AREXX_PARAMETER_PRIORITY: if (TryParseLong(valueString, &longValue)) + { SetPriority(valueString, APPLY_VALUE); + ARexxErrorClear(msg); + } else - msg->rm_Result1 = AREXX_COMMAND_PARM_INVALID; + ARexxError(msg, AREXX_ERROR_PARM_INV); break; case AREXX_PARAMETER_POPKEY: // TODO: Remove CX filter object and add new filter (Settings->PopKey) // http://amigadev.elowar.com/read/ADCD_2.1/Libraries_Manual_guide/node0587.html + ARexxError(msg, AREXX_ERROR_UNKNOWN_PARM); break; case AREXX_PARAMETER_POPUP: if (TryParseBoolean(valueString, &boolValue)) + { Settings->Popup = boolValue; + ARexxErrorClear(msg); + } else - msg->rm_Result1 = AREXX_COMMAND_PARM_INVALID; + ARexxError(msg, AREXX_ERROR_PARM_INV); break; case AREXX_PARAMETER_READONLY: if (TryParseBoolean(valueString, &boolValue)) + { Settings->Readonly = boolValue; + ARexxErrorClear(msg); + } else - msg->rm_Result1 = AREXX_COMMAND_PARM_INVALID; + ARexxError(msg, AREXX_ERROR_PARM_INV); break; case AREXX_PARAMETER_EXPERT: if (TryParseBoolean(valueString, &boolValue)) @@ -412,80 +638,138 @@ static void ArexxSetValueCommand(struct RexxMsg *msg, char **parms) Settings->Expert = boolValue; SendWindowMessage(ATK_SHUTDOWN); ShowSettingWindow(); + ARexxErrorClear(msg); } else - msg->rm_Result1 = AREXX_COMMAND_PARM_INVALID; + ARexxError(msg, AREXX_ERROR_PARM_INV); break; case AREXX_PARAMETER_TIMEOUT: if (TryParseLong(valueString, &longValue)) + { SetTimeout(longValue, APPLY_VALUE); + ARexxErrorClear(msg); + } else - msg->rm_Result1 = AREXX_COMMAND_PARM_INVALID; + ARexxError(msg, AREXX_ERROR_PARM_INV); + break; + case AREXX_PARAMETER_ACTIVE: + if (TryParseBoolean(valueString, &boolValue)) + { + if (boolValue) + ARexxStartCommand(msg, NULL); + else + ARexxStopCommand(msg, NULL); + } + else + ARexxError(msg, AREXX_ERROR_PARM_INV); + break; + case AREXX_PARAMETER_NOLOG: + if (TryParseBoolean(valueString, &boolValue)) + { + Settings->NoLog = boolValue; + ARexxErrorClear(msg); + } + else + ARexxError(msg, AREXX_ERROR_PARM_INV); + case AREXX_PARAMETER_TZ: + SetTimeZoneSetting(SettingKeys->TZ, &Settings->TZ, valueString, APPLY_VALUE); + break; + case AREXX_PARAMETER_TZD: + SetTimeZoneSettingLong(SettingKeys->TimeZoneDisplay, &Settings->TimeZoneDisplay, valueString, APPLY_VALUE); + break; + case AREXX_PARAMETER_TZNAME: + SetTimeZoneSetting(SettingKeys->TimeZoneName, &Settings->TimeZoneName, valueString, APPLY_VALUE); + break; + case AREXX_PARAMETER_TZVALUE: + SetTimeZoneSettingLong(SettingKeys->TimeZoneValue, &Settings->TimeZoneValue, valueString, APPLY_VALUE); + break; + case AREXX_PARAMETER_TZDST: + SetTimeZoneSettingLong(SettingKeys->TimeZoneDst, &Settings->TimeZoneDst, valueString, APPLY_VALUE); break; default: - msg->rm_Result1 = AREXX_COMMAND_PARM_UNKNOWN; + ARexxError(msg, AREXX_ERROR_UNKNOWN_PARM); break; } } -static void ArexxShowCommand(struct RexxMsg *msg, char **parms) +static void ARexxShowCommand(struct RexxMsg *msg, char **parms) { ShowSettingWindow(); + ARexxErrorClear(msg); } -static void ArexxHideCommand(struct RexxMsg *msg, char **parms) +static void ARexxHideCommand(struct RexxMsg *msg, char **parms) { SendWindowMessage(ATK_SHUTDOWN); + ARexxErrorClear(msg); } -static void ArexxStartCommand(struct RexxMsg *msg, char **parms) +static void ARexxStartCommand(struct RexxMsg *msg, char **parms) { if (!SynchronizerRunning) { Activate(); + ARexxErrorClear(msg); } else { - // TODO: Set error text - msg->rm_Result1 = AREXX_COMMAND_ERROR; + ARexxError(msg, AREXX_ERROR_SYNC_ON); } } -static void ArexxStopCommand(struct RexxMsg *msg, char **parms) +static void ARexxStopCommand(struct RexxMsg *msg, char **parms) { if (SynchronizerRunning) { Deactivate(); + ARexxErrorClear(msg); } else { - // TODO: Set error text - msg->rm_Result1 = AREXX_COMMAND_ERROR; + ARexxError(msg, AREXX_ERROR_SYNC_OFF); } } -static void ArexxShutdownCommand(struct RexxMsg *msg, char **parms) +static void ARexxShutdownCommand(struct RexxMsg *msg, char **parms) { - LogWarn("Requesting shutdown"); - msg->rm_Result1 = AREXX_COMMAND_SHUTDOWN; + LogWarn(TextARexxShutdown); + msg->rm_Result1 = AREXX_PARAMETER_SHUTDOWN; + ARexxErrorClear(msg); } -static bool HandleArexxMessage(struct RexxMsg *msg) +static void ARexxLogTransitionsCommand(struct RexxMsg *msg, char **parms) +{ + LogTransitionMap(); + ARexxErrorClear(msg); +} + +static void ExecuteARexxCommand(struct RexxMsg *msg, struct ARexxCommandDef *cmd, char **parms) +{ + LogNotice(TextARexxReceived, cmd->Name); + + bool execute = !cmd->ResultCommand || (cmd->ResultCommand && (msg->rm_Action & RXFF_RESULT)); + if (execute) + { + if (parms == NULL) + LogTrace(TextARexxExecute1, cmd->Name); + else + LogTrace(TextARexxExecute2, cmd->Name, *parms); + cmd->Command(msg, parms); + } +} + +static bool HandleARexxMessage(struct RexxMsg *msg) { int i; bool found = false; bool cont = true; - msg->rm_Result1 = AREXX_COMMAND_UNKNOWN; - msg->rm_Result2 = 0; - for (i = 0; i < AREXX_COMMAND_COUNT; i++) { - if (arexxCommands[i].Parameters == 0 && Stricmp((STRPTR)msg->rm_Args[0], (STRPTR)arexxCommands[i].Name) == 0) + struct ARexxCommandDef *cmd = &arexxCommands[i]; + if (cmd->Parameters == 0 && Stricmp((STRPTR)msg->rm_Args[0], (STRPTR)cmd->Name) == 0) { - LogNotice("Received %s", arexxCommands[i].Name); - msg->rm_Result1 = AREXX_COMMAND_OK; - arexxCommands[i].Command(msg, NULL); + ExecuteARexxCommand(msg, cmd, NULL); found = true; break; } @@ -497,7 +781,7 @@ static bool HandleArexxMessage(struct RexxMsg *msg) char *buf = AllocStringSafe(StrLen(msg->rm_Args[0]) + 1); StrCopy(buf, msg->rm_Args[0]); - for (i = 1; i < AREXX_MAX_ARGS_COUNT; i++) + for (i = 0; i < AREXX_MAX_ARGS_COUNT; i++) { args[i] = NULL; } @@ -505,11 +789,10 @@ static bool HandleArexxMessage(struct RexxMsg *msg) SplitAndTerminate(buf, args); for (i = 0; i < AREXX_COMMAND_COUNT; i++) { - if (arexxCommands[i].Parameters > 0 && Stricmp((STRPTR)buf, (STRPTR)arexxCommands[i].Name) == 0) + struct ARexxCommandDef *cmd = &arexxCommands[i]; + if (cmd->Parameters > 0 && Stricmp((STRPTR)buf, (STRPTR)cmd->Name) == 0) { - LogNotice("Received %s", arexxCommands[i].Name); - msg->rm_Result1 = AREXX_COMMAND_OK; - arexxCommands[i].Command(msg, args); + ExecuteARexxCommand(msg, cmd, args); found = true; break; } @@ -518,9 +801,12 @@ static bool HandleArexxMessage(struct RexxMsg *msg) FreeMemSafe(buf); } - if (msg->rm_Result1 == AREXX_COMMAND_SHUTDOWN) + if (!found) + { + ARexxError(msg, AREXX_ERROR_UNKNOWN_CMD); + } + else if (msg->rm_Result1 == AREXX_PARAMETER_SHUTDOWN) { - msg->rm_Result1 = AREXX_COMMAND_OK; cont = false; } @@ -534,7 +820,11 @@ bool HandleARexxMessages(void) while ((msg = (struct RexxMsg *)GetNewMessage(MSGPORT_AREXX))) { - cont &= HandleArexxMessage(msg); + if (CheckRexxMsg(msg)) + { + cont &= HandleARexxMessage(msg); + msg->rm_Result1 = RC_OK; + } ReplyMsg((struct Message *)msg); } diff --git a/arexx/TZCentralAustralia.rx b/arexx/TZCentralAustralia.rx new file mode 100644 index 0000000..73b533e --- /dev/null +++ b/arexx/TZCentralAustralia.rx @@ -0,0 +1,3 @@ +/* Set TZ to Australian Central time zone */ +SAY ('Changing to Australian Central time zone ...') +Address TIMEKEEPER.1 'SET TZ ACST-9' diff --git a/arexx/TZCentralEurope.rx b/arexx/TZCentralEurope.rx new file mode 100644 index 0000000..3d60771 --- /dev/null +++ b/arexx/TZCentralEurope.rx @@ -0,0 +1,3 @@ +/* Set TZ to Central European time zone */ +SAY ('Changing to Central European time zone ...') +Address TIMEKEEPER.1 'SET TZ CET-1CEST,M3.5.0,M10.5.0/3' diff --git a/arexx/TZEastAustralia.rx b/arexx/TZEastAustralia.rx new file mode 100644 index 0000000..c488cfb --- /dev/null +++ b/arexx/TZEastAustralia.rx @@ -0,0 +1,3 @@ +/* Set TZ to Australian Eastern time zone */ +SAY ('Changing to Australian Eastern time zone ...') +Address TIMEKEEPER.1 'SET TZ AEST-10AEDT,M10.1.0,M4.1.0/3' diff --git a/arexx/TZNorthEastAmerica.rx b/arexx/TZNorthEastAmerica.rx new file mode 100644 index 0000000..1b6aa6e --- /dev/null +++ b/arexx/TZNorthEastAmerica.rx @@ -0,0 +1,3 @@ +/* Set TZ to North East American time zone */ +SAY ('Changing to North East American time zone ...') +Address TIMEKEEPER.1 'SET TZ EST5EDT,M3.2.0,M11.1.0' diff --git a/arexx/TZTest.rx b/arexx/TZTest.rx new file mode 100644 index 0000000..4157190 --- /dev/null +++ b/arexx/TZTest.rx @@ -0,0 +1,23 @@ +/* Test time zone settings */ +SAY ('Testing time zone values ...') + +Address TIMEKEEPER.1 SET TZ +Address TIMEKEEPER.1'SET TZ CET-1' +Address TIMEKEEPER.1'SET TZ CET-1CEST' +Address TIMEKEEPER.1'SET TZ -3' + +Address TIMEKEEPER.1 'SET TZ CST6CDT,M3.2.0,M11.1.0' +Address TIMEKEEPER.1 'SET TZ MST7MDT,M3.2.0,M11.1.0' +Address TIMEKEEPER.1 'SET TZ AEST-10AEDT,M10.1.0,M4.1.0/3' +Address TIMEKEEPER.1 'SET TZ NZST-12NZDT,M9.5.0,M4.1.0/3' +Address TIMEKEEPER.1'SET TZ GMT0' + +Address TIMEKEEPER.1 SET TZVALUE 10000 +Address TIMEKEEPER.1 'SET TZVALUE -10000' +Address TIMEKEEPER.1 SET TZVALUE 200 +Address TIMEKEEPER.1 'SET TZVALUE -200' + +Address TIMEKEEPER.1 'SET TZDST -1' +Address TIMEKEEPER.1 SET TZDST 0 +Address TIMEKEEPER.1 SET TZDST 1 +Address TIMEKEEPER.1 'SET TZDST -1' diff --git a/arexx/getactive.rx b/arexx/getactive.rx new file mode 100644 index 0000000..6d3013f --- /dev/null +++ b/arexx/getactive.rx @@ -0,0 +1,4 @@ +/* Show TimeKeeper synchronization status */ +Options RESULTS +Address TIMEKEEPER.1 'GET ACTIVE'; SAY ('ACTIVE (YES/NO):' RESULT); +Address TIMEKEEPER.1 'GET ACTIVE NUMBER'; SAY ('ACTIVE (0/1):' RESULT); diff --git a/arexx/getserver.rx b/arexx/getserver.rx new file mode 100644 index 0000000..06f203e --- /dev/null +++ b/arexx/getserver.rx @@ -0,0 +1,5 @@ +/* Show the NTP server address in use */ +Options RESULTS +Address TIMEKEEPER.1 'GET SERVER'; +IF TIMEKEEPER.ERRORCODE == 0 THEN SAY (RESULT) +ELSE SAY (TIMEKEEPER.ERRORTEXT) diff --git a/arexx/help.rx b/arexx/help.rx index 580036c..e0e3c8c 100644 --- a/arexx/help.rx +++ b/arexx/help.rx @@ -1,4 +1,4 @@ /* Show the ARexx commands supported by TimeKeeper */ Options RESULTS -Address TIMEKEEPER.1 'help' +Address TIMEKEEPER.1 HELP SAY (RESULT) diff --git a/arexx/hide.rx b/arexx/hide.rx new file mode 100644 index 0000000..e9b3525 --- /dev/null +++ b/arexx/hide.rx @@ -0,0 +1,2 @@ +/* Hide TimeKeeper settings window */ +Address TIMEKEEPER.1 HIDE diff --git a/arexx/lastsync.rx b/arexx/lastsync.rx index 811383f..89ecc01 100644 --- a/arexx/lastsync.rx +++ b/arexx/lastsync.rx @@ -1,3 +1,19 @@ /* Show when the last response from a NTP server was received */ Options RESULTS -Address TIMEKEEPER.1 'LastSync'; SAY (RESULT) +Address TIMEKEEPER.1 'LastSync' +IF TIMEKEEPER.ERRORCODE == 0 THEN SAY (RESULT) +ELSE SAY (TIMEKEEPER.ERRORTEXT) + +Address TIMEKEEPER.1 LASTSYNC RFC3339 +IF TIMEKEEPER.ERRORCODE == 0 THEN SAY ('Time and date in RFC 3339 format:' RESULT) +ELSE SAY (TIMEKEEPER.ERRORTEXT) + +Address TIMEKEEPER.1 LASTSYNC DATE +IF TIMEKEEPER.ERRORCODE == 0 THEN SAY ('Sorted date (in the form YYYYMMDD):' RESULT) +ELSE SAY (TIMEKEEPER.ERRORTEXT) + +Address TIMEKEEPER.1 LASTSYNC TIME +IF TIMEKEEPER.ERRORCODE == 0 THEN SAY ('Time in seconds since midnight:' RESULT) +ELSE SAY (TIMEKEEPER.ERRORTEXT) + + diff --git a/arexx/settings.rx b/arexx/settings.rx index 828a55c..23c32a2 100644 --- a/arexx/settings.rx +++ b/arexx/settings.rx @@ -9,6 +9,13 @@ Address TIMEKEEPER.1 'get threshold'; SAY ('THRESHOLD =' RESULT 'microseconds') Address TIMEKEEPER.1 'get ReadOnly'; SAY ('READONLY =' RESULT) Address TIMEKEEPER.1 'get Expert'; SAY ('EXPERT =' RESULT) Address TIMEKEEPER.1 'get timeout'; SAY ('TIMEOUT =' RESULT 'milliseconds') -Address TIMEKEEPER.1 'get CX_POPUP'; SAY ('POPUP =' RESULT) -Address TIMEKEEPER.1 'get CX_POPKEY'; SAY ('POPKEY =' RESULT) -Address TIMEKEEPER.1 'get CX_PRIORITY'; SAY ('PRIORITY =' RESULT) +Address TIMEKEEPER.1 GET CX_POPUP; SAY ('POPUP =' RESULT) +Address TIMEKEEPER.1 GET CX_POPKEY; SAY ('POPKEY =' RESULT) +Address TIMEKEEPER.1 GET CX_PRIORITY; SAY ('PRIORITY =' RESULT) +Address TIMEKEEPER.1 GET ACTIVE; SAY ('ACTIVE =' RESULT) +Address TIMEKEEPER.1 GET NOLOG; SAY ('NOLOG =' RESULT) +Address TIMEKEEPER.1 GET TZD; SAY ('TZD =' RESULT) +Address TIMEKEEPER.1 GET TZ; SAY ('TZ =' RESULT) +Address TIMEKEEPER.1 GET TZNAME; SAY ('TZNAME =' RESULT) +Address TIMEKEEPER.1 GET TZVALUE; SAY ('TZVALUE =' RESULT) +Address TIMEKEEPER.1 GET TZDST; SAY ('TZDST =' RESULT) diff --git a/arexx/start.rx b/arexx/start.rx index 6110105..7df6342 100644 --- a/arexx/start.rx +++ b/arexx/start.rx @@ -1,3 +1,4 @@ /* Start TimeKeeper synchronization */ SAY ('Starting TimeKeeper synchronization ...') -Address TIMEKEEPER.1 'start' +Address TIMEKEEPER.1 START +IF TIMEKEEPER.ERRORCODE ~= 0 THEN SAY (TIMEKEEPER.ERRORTEXT) diff --git a/arexx/status.rx b/arexx/status.rx index 4efc939..8bce7ee 100644 --- a/arexx/status.rx +++ b/arexx/status.rx @@ -1,5 +1,12 @@ /* Show TimeKeeper synchronization status */ Options RESULTS + Address TIMEKEEPER.1 'status'; SAY (RESULT) -Address TIMEKEEPER.1 'LastSync'; SAY ('Last response from NTP server was:' RESULT) -Address TIMEKEEPER.1 'LastAdjust'; SAY ('Last clock adjustment was:' RESULT) + +Address TIMEKEEPER.1 'LastSync' +IF TIMEKEEPER.ERRORCODE == 0 THEN SAY ('Last response from NTP server was:' RESULT) +ELSE SAY ('LastSync:' TIMEKEEPER.ERRORTEXT) + +Address TIMEKEEPER.1 'LastAdj' +IF TIMEKEEPER.ERRORCODE == 0 THEN SAY ('Last clock adjustment was:' RESULT) +ELSE SAY ('LastAdj:' TIMEKEEPER.ERRORTEXT) diff --git a/arexx/stop.rx b/arexx/stop.rx index ece85ef..2fd9fc0 100644 --- a/arexx/stop.rx +++ b/arexx/stop.rx @@ -1,3 +1,4 @@ /* Stop TimeKeeper synchronization */ SAY ('Stopping TimeKeeper synchronization ...') -Address TIMEKEEPER.1 'stop' +Address TIMEKEEPER.1 STOP +IF TIMEKEEPER.ERRORCODE ~= 0 THEN SAY (TIMEKEEPER.ERRORTEXT) diff --git a/arexx/time.rx b/arexx/time.rx new file mode 100644 index 0000000..887928a --- /dev/null +++ b/arexx/time.rx @@ -0,0 +1,19 @@ +/* Show current date and time */ +Options RESULTS + +Address TIMEKEEPER.1 NOW ASCII LOCAL; SAY ('ASCII style:' RESULT) +Address TIMEKEEPER.1 NOW ASCII UTC; SAY ('ASCII style (UTC):' RESULT) +Address TIMEKEEPER.1 NOW DOS LOCAL; SAY ('DOS style:' RESULT) +Address TIMEKEEPER.1 NOW DOS UTC; SAY ('DOS style (UTC):' RESULT) +Address TIMEKEEPER.1 NOW RFC850; SAY ('RFC850:' RESULT) +Address TIMEKEEPER.1 NOW RFC1123; SAY ('RFC1123:' RESULT) +Address TIMEKEEPER.1 NOW RFC2822 LOCAL; SAY ('RFC2822:' RESULT) +Address TIMEKEEPER.1 NOW RFC2822 UTC; SAY ('RFC2822:' RESULT) +Address TIMEKEEPER.1 NOW RFC3339 LOCAL; SAY ('RFC3339:' RESULT) +Address TIMEKEEPER.1 NOW RFC3339 UTC; SAY ('RFC3339:' RESULT) +Address TIMEKEEPER.1 NOW ISO8601 LOCAL; SAY ('ISO8601:' RESULT) +Address TIMEKEEPER.1 NOW ISO8601 UTC; SAY ('ISO8601:' RESULT) +Address TIMEKEEPER.1 NOW DATE LOCAL; SAY ('ARexx compatible sorted date:' RESULT) +Address TIMEKEEPER.1 NOW DATE UTC; SAY ('ARexx compatible sorted date (UTC):' RESULT) +Address TIMEKEEPER.1 NOW TIME LOCAL; SAY ('ARexx compatible time in seconds since midnight:' RESULT) +Address TIMEKEEPER.1 NOW TIME UTC; SAY ('ARexx compatible time in seconds since midnight (UTC):' RESULT) diff --git a/arexx/timezone.rx b/arexx/timezone.rx index ce32cc2..89adc59 100644 --- a/arexx/timezone.rx +++ b/arexx/timezone.rx @@ -1,3 +1,14 @@ /* Show current timezone in TimeKeeper */ Options RESULTS -Address TIMEKEEPER.1 'timezone'; SAY (RESULT) +Address TIMEKEEPER.1 TIMEZONE 1; SAY (' 1:' RESULT) +Address TIMEKEEPER.1 TIMEZONE 2; SAY (' 2:' RESULT) +Address TIMEKEEPER.1 TIMEZONE 3; SAY (' 3:' RESULT) +Address TIMEKEEPER.1 TIMEZONE 4; SAY (' 4:' RESULT) +Address TIMEKEEPER.1 TIMEZONE 5; SAY (' 5:' RESULT) +Address TIMEKEEPER.1 TIMEZONE 6; SAY (' 6:' RESULT) +Address TIMEKEEPER.1 TIMEZONE 7; SAY (' 7:' RESULT) +Address TIMEKEEPER.1 TIMEZONE 8; SAY (' 8:' RESULT) +Address TIMEKEEPER.1 TIMEZONE 9; SAY (' 9:' RESULT) +Address TIMEKEEPER.1 TIMEZONE 10; SAY ('10:' RESULT) +Address TIMEKEEPER.1 TIMEZONE 11; SAY ('11:' RESULT) +Address TIMEKEEPER.1 TIMEZONE 12; SAY ('12:' RESULT) diff --git a/arexx/transition.rx b/arexx/transition.rx new file mode 100644 index 0000000..496fcc3 --- /dev/null +++ b/arexx/transition.rx @@ -0,0 +1,3 @@ +/* Show time zone transition map in log */ +SAY ('Writing time zone transition map to log') +Address TIMEKEEPER.1 LOGTRANS diff --git a/broker.c b/broker.c index cce8512..3b4c360 100644 --- a/broker.c +++ b/broker.c @@ -118,7 +118,7 @@ void StartBroker(void) LogError("System problems (CBERR_SYSERR). Could not allocate broker object"); break; case CBERR_DUP: - LogWarn(APP_SHORT_NAME " is already running"); + LogWarn(Text2P, APP_SHORT_NAME, TextAlreadyRunning); break; default: LogError("Could not allocate broker object (error code: %ld)", error); @@ -186,7 +186,12 @@ void StartBroker(void) #endif ActivateNotifyPort(Broker.NotifyPort); - StartSynchronizer(); + + if (Settings->Active) + { + ActivateCxObj(Broker.Object, 1); + StartSynchronizer(); + } MsgLoop(); @@ -207,14 +212,14 @@ void Deactivate(void) { if (SynchronizerRunning) { - LogNotice("ACTIVE changed: 1 -> 0"); + LogNotice(KEYWORD_ACTIVE " changed: 1 -> 0"); LogWarn("Disabling time synchronization"); SendMessageWait(MSGPORT_SYNCER, ATK_DISABLE); ActivateCxObj(Broker.Object, 0); } else { - LogError("Synchronization is already deactivated"); + LogError(TextSyncAlreadyOff); } } @@ -222,30 +227,26 @@ void Activate(void) { if (!SynchronizerRunning) { - LogNotice("ACTIVE changed: 0 -> 1"); + LogNotice(KEYWORD_ACTIVE " changed: 0 -> 1"); LogWarn("Enabling time synchronization"); ActivateCxObj(Broker.Object, 1); StartSynchronizer(); } else { - LogError("Synchronization is already active"); + LogError(TextSyncAlreadyOn); } } static void ChangeTimezone(void) { - // TODO: Make thread safe - CleanupTimezone(); InitTimezone(); SendWindowMessage(ATK_TZ_CHANGED); } static void ChangeTimezoneDelayedProc(void) { - // TODO: Make thread safe Delay(15 * 50); - ReopenLocale(); ChangeTimezone(); SendWindowMessage(ATK_TZ_CHANGED); } @@ -382,9 +383,7 @@ static void MsgLoop(void) BrokerRunning = true; LogTrace("Loop started"); - ActivateCxObj(Broker.Object, 1); - - if (Settings->Popup) + if (Settings->Popup || Settings->PopupOnStart) { ShowSettingWindow(); } @@ -486,7 +485,6 @@ static void MsgLoop(void) { ShowSettingWindow(); } - ReopenLocale(); ChangeTimezone(); reopenWindow = false; } diff --git a/compiler.h b/compiler.h index ba21e5c..f8796f1 100644 --- a/compiler.h +++ b/compiler.h @@ -58,7 +58,8 @@ typedef int bool; #define StrCopy(dest, src) strcpy((char *)dest, (const char *)src) #endif -#define MAXLONGCHARSIZE 12 -#define MAXLONGLONGCHARSIZE 22 +#define MAXLONGCHARSIZE 13 +#define MAXLONGLONGCHARSIZE 23 +#define MAXBOOLCHARSIZE 4 #endif diff --git a/config.h b/config.h index ff556ab..8c04295 100644 --- a/config.h +++ b/config.h @@ -160,8 +160,8 @@ int poll(struct pollfd *, nfds_t, int); #define APP_LONG_NAME "Amiga Time Keeper" #endif -#define APP_VERSION "1.11" -#define APP_DATE_VERSION "1.11 (11.01.2021)" +#define APP_VERSION "1.12" +#define APP_DATE_VERSION "1.12 (31.01.2021)" #define APP_ID APP_SHORT_NAME " " APP_DATE_VERSION #define APP_TITLE APP_LONG_NAME " " APP_DATE_VERSION #define APP_TITLE_VERSION APP_LONG_NAME " " APP_VERSION @@ -169,7 +169,7 @@ int poll(struct pollfd *, nfds_t, int); #include "log.h" #include "string.h" -#define KEYWORD_COUNT 10 +#define KEYWORD_COUNT 18 #define KEYWORD_SERVER "SERVER" #define KEYWORD_PORT "PORT" #define KEYWORD_THRESHOLD "THRESHOLD" @@ -180,6 +180,35 @@ int poll(struct pollfd *, nfds_t, int); #define KEYWORD_READONLY "READONLY" #define KEYWORD_EXPERT "EXPERT" #define KEYWORD_TIMEOUT "TIMEOUT" +#define KEYWORD_ACTIVE "ACTIVE" +#define KEYWORD_NOLOG "NOLOG" +#define KEYWORD_TZ "TZ" +#define KEYWORD_TZD "TZD" +#define KEYWORD_TZNAME "TZNAME" +#define KEYWORD_TZVALUE "TZVALUE" +#define KEYWORD_TZDST "TZDST" +#define KEYWORD_POPUP2 "POPUP" + +#define KEYWORD_FROM "FROM" + +#define KEYWORD_TZ_ALIAS "TIMEZONE" +#define KEYWORD_TZD_ALIAS "TIMEZONEDISPLAY" +#define KEYWORD_TZNAME_ALIAS "TIMEZONEABBREVIATION" +#define KEYWORD_TZVALUE_ALIAS "TIMEZONEVALUE" +#define KEYWORD_TZDST_ALIAS "DST" + +#define KEYWORD_NUMBER "NUMBER" +#define KEYWORD_DATE "DATE" +#define KEYWORD_TIME "TIME" +#define KEYWORD_DOS "DOS" +#define KEYWORD_ASCII "ASCII" +#define KEYWORD_RFC850 "RFC850" +#define KEYWORD_RFC1123 "RFC1123" +#define KEYWORD_RFC2822 "RFC2822" +#define KEYWORD_RFC3339 "RFC3339" +#define KEYWORD_ISO8601 "ISO8601" +#define KEYWORD_LOCAL "LOCAL" +#define KEYWORD_UTC "UTC" #define SERVER_DEF "pool.ntp.org" #define PORT_DEF "123" @@ -187,27 +216,73 @@ int poll(struct pollfd *, nfds_t, int); #define THRESHOLD_DEF 1000000LL #define INTERVAL_MIN 500 #define INTERVAL_DEF 17500 +#define INTERVAL_MAX 2 * 24 * 60 * 60 * 1000 #define PRIORITY_MIN -128 #define PRIORITY_DEF 25 #define PRIORITY_MAX 127 #define READONLY_DEF 0 #define EXPERT_DEF 0 +#define ACTIVE_DEF 0 +#define NOLOG_DEF 0 #define TIMEOUT_MIN 100 #define TIMEOUT_DEF 5000 #define TIMEOUT_MAX 30000 #define POPKEY_DEF "lshift control t" #define POPUP_DEF 0 +#define TZ_DEF "" +#define TZD_MIN 1 +#define TZD_DEF 9 +#define TZD_MAX 10 +#define TZNAME_DEF "" +#define TZVALUE_MIN -2400 +#define TZVALUE_DEF 16800 +#define TZVALUE_MAX 2400 +#define TZDST_DEF -1 -#define KEYWORD_TEMPLATE_1 KEYWORD_READONLY "/S," KEYWORD_EXPERT "/S," KEYWORD_SERVER "/K," KEYWORD_PORT "/K," KEYWORD_TIMEOUT "/N/K," -#define KEYWORD_TEMPLATE_2 KEYWORD_THRESHOLD "/K," KEYWORD_INTERVAL "/N/K," -#define KEYWORD_TEMPLATE_3 KEYWORD_PRIORITY "/N/K," KEYWORD_POPKEY "/K," KEYWORD_POPUP "/K," -#define KEYWORD_TEMPLATE KEYWORD_TEMPLATE_1 KEYWORD_TEMPLATE_2 KEYWORD_TEMPLATE_3 +#define KWD_TMPL_01 KEYWORD_READONLY "/S," +#define KWD_TMPL_02 KEYWORD_EXPERT "/S," +#define KWD_TMPL_03 KEYWORD_ACTIVE "/S," +#define KWD_TMPL_04 KEYWORD_NOLOG "/S," +#define KWD_TMPL_05 KEYWORD_SERVER "/K," +#define KWD_TMPL_06 KEYWORD_PORT "/K," +#define KWD_TMPL_07 KEYWORD_TIMEOUT "/N/K," +#define KWD_TMPL_08 KEYWORD_THRESHOLD "/K," +#define KWD_TMPL_09 KEYWORD_INTERVAL "/N/K," +#define KWD_TMPL_10 KEYWORD_PRIORITY "/N/K," +#define KWD_TMPL_11 KEYWORD_POPKEY "/K," +#define KWD_TMPL_12 KEYWORD_POPUP "/K," +#define KWD_TMPL_13 KEYWORD_TZ_ALIAS "=" KEYWORD_TZ "/K," +#define KWD_TMPL_14 KEYWORD_TZD_ALIAS "=" KEYWORD_TZD "/N/K," +#define KWD_TMPL_15 KEYWORD_TZNAME_ALIAS "=" KEYWORD_TZNAME "/K," +#define KWD_TMPL_16 KEYWORD_TZVALUE_ALIAS "=" KEYWORD_TZVALUE "/N/K," +#define KWD_TMPL_17 KEYWORD_TZDST_ALIAS "=" KEYWORD_TZDST "/N/K," +#define KWD_TMPL_18 KEYWORD_POPUP2 "/S" + +#define KWD_TMPL \ + KWD_TMPL_01 \ + KWD_TMPL_02 \ + KWD_TMPL_03 \ + KWD_TMPL_04 \ + KWD_TMPL_05 \ + KWD_TMPL_06 \ + KWD_TMPL_07 \ + KWD_TMPL_08 \ + KWD_TMPL_09 \ + KWD_TMPL_10 \ + KWD_TMPL_11 \ + KWD_TMPL_12 \ + KWD_TMPL_13 \ + KWD_TMPL_14 \ + KWD_TMPL_15 \ + KWD_TMPL_16 \ + KWD_TMPL_17 \ + KWD_TMPL_18 struct ControlMesage; /* arexx.c */ bool HandleARexxMessages(void); -void CleanupArexx(void); +void CleanupARexx(void); /* broker.c */ void StartBroker(void); @@ -222,6 +297,5 @@ bool HandleControlMessages(void); /* libraries.c */ int OpenLibraries(void); void CloseLibraries(void); -void ReopenLocale(void); #endif diff --git a/conv.c b/conv.c index f8be98f..55cc1a1 100644 --- a/conv.c +++ b/conv.c @@ -109,6 +109,25 @@ int BoolToStr(bool value, char *string) return count; } +/* + * Convert a boolean to a null terminated numeral string. + */ +int BoolToStrNum(bool value, char *string) +{ + char *s = string; + + if (s == NULL) + return 0; + + if (value) + *s++ = '1'; + else + *s++ = '0'; + + *s = '\0'; + return 1; +} + /* * Convert a long to a null terminated string. */ diff --git a/conv.h b/conv.h index 3442e19..a009849 100644 --- a/conv.h +++ b/conv.h @@ -33,6 +33,7 @@ bool TryParseBoolean(char *, bool *); bool TryParseLong(char *, long *); bool TryParseLongLong(char *, long long *); int BoolToStr(bool, char *); +int BoolToStrNum(bool, char *); int LongToStr(signed long, char *); int LongLongToStr(signed long long, char *); int StrToLongLong(char *, unsigned long long *); diff --git a/ctrl.c b/ctrl.c index 7666326..3eb5b2b 100644 --- a/ctrl.c +++ b/ctrl.c @@ -37,9 +37,9 @@ #include "message.h" #include "string.h" -const char *vers = "\0$VER: TimeControl 1.11 (11.01.2021)"; +const char *vers = "\0$VER: TimeControl 1.12 (31.01.2021)"; -#define ARGSFORMAT "ID/S,VERSION/S,STATUS/S,START/S,STOP/S,SHOW/S,HIDE/S,SHUTDOWN/S" +#define ARGSFORMAT "ID/S,VERSION/S,STATUS/S,TIME/S,ZONE/S,START/S,STOP/S,SHOW/S,HIDE/S,SHUTDOWN/S" struct RDArgs *rdargs = NULL; struct MsgPort *replyPort = NULL; @@ -50,6 +50,8 @@ struct progargs long id; long version; long status; + long time; + long timezone; long start; long stop; long show; @@ -62,6 +64,8 @@ void InitializeArgs(struct progargs *args) args->id = FALSE; args->version = FALSE; args->status = FALSE; + args->time = FALSE; + args->timezone = FALSE; args->start = FALSE; args->stop = FALSE; args->show = FALSE; @@ -107,7 +111,7 @@ void SendControlMessage(struct ControlMesage *message) if (message->Code == ATK_CTRL_UNKNOWN) { - Printf((STRPTR) APP_SHORT_NAME " could not process control command.\n"); + Printf((STRPTR)APP_SHORT_NAME " could not process control command.\n"); } else if (*message->Result != '\0') { @@ -195,6 +199,18 @@ void ProcessArgs(struct progargs *args) processed = TRUE; } + if (args->time) + { + SendSimpleMessage(ATK_TIME); + processed = TRUE; + } + + if (args->timezone) + { + SendSimpleMessage(ATK_TIMEZONE); + processed = TRUE; + } + if (args->show) { SendSimpleMessage(ATK_SHOW); @@ -262,7 +278,7 @@ int main(int argc, char **argv) port = FindPort((CONST_STRPTR)CONTROL_PORT_NAME); if (port == NULL) { - Printf((STRPTR) APP_SHORT_NAME " is not running.\n"); + Printf((STRPTR)APP_SHORT_NAME " is not running.\n"); Cleanup(); exit(1); } diff --git a/ctrlmsg.c b/ctrlmsg.c index 439af33..fc66f75 100644 --- a/ctrlmsg.c +++ b/ctrlmsg.c @@ -26,9 +26,12 @@ #include "config.h" #include "message.h" +#include "global.h" +#include "locale.h" #include "text.h" #include "sync.h" #include "win.h" +#include "mem.h" #include "logmod.h" #define MODULENAME "Control" @@ -49,6 +52,22 @@ static bool HandleControlMessage(struct ControlMesage *msg) StrCopy(msg->Result, APP_VERSION); msg->Code = ATK_CTRL_OK; break; + case ATK_TIME: + LogDebug("Time request"); + char *time = GetTimeText(LOCAL_TIME_NOW); + StrCopy(msg->Result, time); + FreeMemSafe(time); + msg->Code = ATK_CTRL_OK; + break; + case ATK_TIMEZONE: + LogDebug("Timezone request"); + { + char buf[TIMEZONE_TEXT_LEN]; + GetTimezoneText(buf, TZD_SECONDS_AHEAD); + StrCopy(msg->Result, buf); + } + msg->Code = ATK_CTRL_OK; + break; case ATK_STATUS: LogDebug("Status request"); if (SynchronizerRunning) diff --git a/global.c b/global.c index efbdea4..f3800b6 100644 --- a/global.c +++ b/global.c @@ -32,6 +32,5 @@ #include -struct AppLocale *AppLocale; struct PosixTimezone *Timezone; struct PosixTransitionTime *NextTransition; diff --git a/global.h b/global.h index e6dce14..fd9b331 100644 --- a/global.h +++ b/global.h @@ -30,7 +30,6 @@ #include #include "locale.h" -extern struct AppLocale *AppLocale; extern struct PosixTimezone *Timezone; extern struct PosixTransitionTime *NextTransition; extern struct Library *BattClockBase; diff --git a/icons/AmiTimeKeeper_1.12.info b/icons/AmiTimeKeeper_1.12.info new file mode 100644 index 0000000..a5ec624 Binary files /dev/null and b/icons/AmiTimeKeeper_1.12.info differ diff --git a/icons/TimeKeeper.guide.info b/icons/TimeKeeper.guide.info new file mode 100644 index 0000000..89dadd8 Binary files /dev/null and b/icons/TimeKeeper.guide.info differ diff --git a/icons/TimeKeeper.info b/icons/TimeKeeper.info new file mode 100644 index 0000000..5918394 Binary files /dev/null and b/icons/TimeKeeper.info differ diff --git a/icons/TimeKeeper.readme.info b/icons/TimeKeeper.readme.info new file mode 100644 index 0000000..7d481e8 Binary files /dev/null and b/icons/TimeKeeper.readme.info differ diff --git a/library.c b/library.c index 85a1111..6602350 100644 --- a/library.c +++ b/library.c @@ -27,6 +27,7 @@ #include "config.h" #include "global.h" #include "timer.h" +#include "text.h" #include "ptz.h" #include "mem.h" @@ -85,9 +86,6 @@ #define LIBREV 34L #endif -#ifndef CURRENTLOCALE -#define CURRENTLOCALE "Current Locale Info" -#endif #ifndef NOAREXX #define NOAREXX "No ARexx library found" #endif @@ -110,54 +108,25 @@ struct Device *TimerDevice = NULL; static void OpenLibrarySuccess(char *name, char *ident) { - if (ident == NULL) - { - LogDebug("Opened %s", name); - } - else - { - LogDebug("Opened %s", ident); - } -} - -static void OpenLibraryError(char *name, long version) -{ - char message[64]; - SNPrintf(message, 63, "Cannot open %s %ld.0", name, version); - Printf((STRPTR)message); - LogError(message); + LogDebug(Text2P, TextOpened, (ident == NULL ? name : ident)); } static void ClosingLibrary(char *name, char *ident) { - if (ident == NULL) - { - LogDebug("Closing %s", name); - } - else - { - LogDebug("Closing %s", ident); - } + LogDebug(Text2P, TextClosing, (ident == NULL ? name : ident)); } -static void OpenResourceSuccess(char *name) +static void OpenLibraryError(char *name, long version) { - LogDebug("Opened %s", name); -} - -static void OpenResourceError(char *name) -{ - LogError("Cannot open %s", name); -} - -static void ClosingResource(char *name) -{ - LogDebug("Closing %s", name); + char message[128]; + SNPrintf(message, 127, "%s %s %ld.0", TextNoOpen, name, version); + Printf((STRPTR)message); + LogError(message); } int OpenLibraries(void) { - LogInfo("Starting up"); + LogInfo(Text2P, TextStarting, "up"); // DOS Library if (!(DOSBase2 = (struct DosLibrary *)OpenLibrary((STRPTR)DOSNAME, LIBREV))) @@ -234,27 +203,19 @@ int OpenLibraries(void) LogWarn(NOAREXX); } - // Locale - if (!(AppLocale = OpenAppLocale(NULL))) - { - OpenResourceError(CURRENTLOCALE); - return LIB_ERROR; - } - OpenResourceSuccess((char *)AppLocale->AmigaLocale->loc_LocaleName); - // Timer Device if (!(TimerDevice = OpenTimerBase())) { - OpenResourceError(TIMERNAME); + LogError(Text2P, TextNoOpen, TIMERNAME); return LIB_ERROR; } - OpenResourceSuccess(TIMERNAME); + LogDebug(Text2P, TextOpened, TIMERNAME); // RTC Clock BattClockBase = OpenResource((STRPTR)BATTCLOCKNAME); if (BattClockBase != NULL) { - OpenResourceSuccess(BATTCLOCKNAME); + LogDebug(Text2P, TextOpened, BATTCLOCKNAME); } else { @@ -265,7 +226,7 @@ int OpenLibraries(void) ScreenNotifyBase = OpenLibrary((STRPTR)SCREENNOTIFY_NAME, SCREENNOTIFY_VERSION); if (ScreenNotifyBase != NULL) { - LogNotice("Found %s", SCREENNOTIFY_NAME); + LogNotice(Text2P, TextFound, SCREENNOTIFY_NAME); } #endif @@ -278,18 +239,11 @@ void CloseLibraries(void) { if (TimerDevice != NULL) { - ClosingResource(TIMERNAME); + LogDebug(Text2P, TextClosing, TIMERNAME); CloseTimerBase(); TimerDevice = NULL; } - if (AppLocale != NULL) - { - ClosingResource((char *)AppLocale->AmigaLocale->loc_LocaleName); - CloseAppLocale(AppLocale); - AppLocale = NULL; - } - #if defined(SCREENNOTIFY) if (ScreenNotifyBase != NULL) { @@ -362,24 +316,3 @@ void CloseLibraries(void) DOSBase2 = NULL; } } - -void ReopenLocale(void) -{ - struct AppLocale *locale, *temp; - - if (!(locale = OpenAppLocale(NULL))) - { - OpenResourceError(CURRENTLOCALE); - } - else - { - temp = AppLocale; - AppLocale = locale; - - if (temp != NULL) - { - ClosingResource((char *)temp->AmigaLocale->loc_LocaleName); - CloseAppLocale(temp); - } - } -} diff --git a/locale.c b/locale.c index 7a5955b..8999172 100644 --- a/locale.c +++ b/locale.c @@ -26,130 +26,130 @@ #include "locale.h" #include "global.h" -#include "string.h" +#include "setting.h" +#include "conv.h" +#include "text.h" #include "ptz.h" #include "mem.h" +#include "tz.h" +#include #include #include #include -struct AppLocale *OpenAppLocale(struct Locale *locale) +#include + +#define SECONDSPERDAY 86400 + +static const char *WeekdayShort[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; +static const char *WeekdayLong[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}; +static const char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + +static void GetTimezoneCommon(char *text, struct PosixLocalTimezone *tz, long opt) { - int i, j; - struct AppLocale *l = AllocStructSafe(struct AppLocale); - struct Locale *loc = locale; - - if (!loc) + if (tz->abbreviation[0] == 'G' && tz->abbreviation[1] == 'M' && tz->abbreviation[2] == 'T') { - loc = OpenLocale(NULL); + if (tz->offset.minutes == 0) + SNPrintf(text, TIMEZONE_TEXT_LEN, "GMT%s%d", TimezoneSignChar(tz), tz->offset.hours); + else + SNPrintf(text, TIMEZONE_TEXT_LEN, "GMT%s%d:%02d", + TimezoneSignChar(tz), tz->offset.hours, tz->offset.minutes); } - - if (loc == NULL) + else if (opt == TZD_ZONE_IN_PARENS) { - return NULL; + if (tz->offset.minutes == 0) + SNPrintf(text, TIMEZONE_TEXT_LEN, "%s (GMT%s%d)", + tz->abbreviation, TimezoneSignChar(tz), tz->offset.hours); + else + SNPrintf(text, TIMEZONE_TEXT_LEN, "%s (GMT%s%d:%02d)", + tz->abbreviation, TimezoneSignChar(tz), tz->offset.hours, tz->offset.minutes); } - - l->X_fmt = (const char *)loc->loc_TimeFormat; - l->x_fmt = (const char *)loc->loc_ShortDateFormat; - l->c_fmt = (const char *)loc->loc_DateTimeFormat; - l->am = (const char *)GetLocaleStr(loc, AM_STR); - l->pm = (const char *)GetLocaleStr(loc, PM_STR); - l->date_fmt = "%a %b %e %H:%M:%S %Z %Y"; - - j = 0; - for (i = 1; i < 8; i++) + else if (opt == TZD_OFFSET_IN_PARENS) { - l->weekday[j++] = (const char *)GetLocaleStr(loc, i); - } - - j = 0; - for (i = 8; i < 15; i++) - { - l->wday[j++] = (const char *)GetLocaleStr(loc, i); - } - - j = 0; - for (i = 15; i < 27; i++) - { - l->month[j++] = (const char *)GetLocaleStr(loc, i); - } - - j = 0; - for (i = 27; i < 39; i++) - { - l->mon[j++] = (const char *)GetLocaleStr(loc, i); - } - - l->AmigaLocale = loc; - return l; -} - -void CloseAppLocale(struct AppLocale *lc) -{ - if (lc->AmigaLocale) - { - CloseLocale(lc->AmigaLocale); - } - - FreeMemSafe(lc); -} - -void GetTimezoneText(struct AppLocale *loc, char *text, long opt) -{ - if (loc == NULL || text == NULL) - { - return; - } - - if (Timezone == NULL) - { - long hoursOffset = (loc->AmigaLocale->loc_GMTOffset / -60L); - long minutsOffset = (loc->AmigaLocale->loc_GMTOffset % -60L); - SNPrintf(text, TIMEZONE_TEXT_LEN, "%s%s%02ld:%02ld", - (opt == UTC_NO_PARENS) ? "UTC" : "GMT", - (loc->AmigaLocale->loc_GMTOffset < 0L) ? "+" : "", - hoursOffset, - minutsOffset); - } - else if (opt == ZONE_IN_PARENS) - { - SNPrintf(text, TIMEZONE_TEXT_LEN, "GMT%s%02d:%02d (%s)", - PosixTimezoneSignChar(&Timezone->current), - Timezone->current.offset.hours, - Timezone->current.offset.minutes, - Timezone->current.abbreviation); - } - else if (opt == OFFSET_IN_PARENS) - { - SNPrintf(text, TIMEZONE_TEXT_LEN, "%s (GMT%s%02d:%02d)", - Timezone->current.abbreviation, - PosixTimezoneSignChar(&Timezone->current), - Timezone->current.offset.hours, - Timezone->current.offset.minutes); - } - else if (opt == UTC_NO_PARENS) - { - SNPrintf(text, TIMEZONE_TEXT_LEN, "UTC%s%02d:%02d", - PosixTimezoneSignChar(&Timezone->current), - Timezone->current.offset.hours, - Timezone->current.offset.minutes); + if (tz->offset.minutes == 0) + SNPrintf(text, TIMEZONE_TEXT_LEN, "GMT%s%d (%s)", + TimezoneSignChar(tz), tz->offset.hours, tz->abbreviation); + else + SNPrintf(text, TIMEZONE_TEXT_LEN, "GMT%s%d:%02d (%s)", + TimezoneSignChar(tz), tz->offset.hours, tz->offset.minutes, tz->abbreviation); } } -char *GetTimeText(struct AppLocale *loc, ULONG time) +void GetTimezoneText(char *text, long opt) { - char *out; + long offset = -GetUtcOffsetValue(); + char *tzSign = (offset <= 0 ? "+" : "-"); + + switch (opt) + { + case TZD_SECONDS_AHEAD: + SNPrintf(text, TIMEZONE_TEXT_LEN, "%ld", offset); + break; + case TZD_MINUTES_AHEAD: + SNPrintf(text, TIMEZONE_TEXT_LEN, "%ld", offset / 60); + break; + case TZD_TZ_LONG: + if (Timezone->current.offset.minutes == 0) + SNPrintf(text, TIMEZONE_TEXT_LEN, "UTC%s%d", tzSign, Timezone->current.offset.hours); + else + SNPrintf(text, TIMEZONE_TEXT_LEN, "UTC%s%d:%02d", + tzSign, Timezone->current.offset.hours, Timezone->current.offset.minutes); + break; + case TZD_TZ_SHORT: + { + if (Timezone->current.offset.minutes == 0) + SNPrintf(text, TIMEZONE_TEXT_LEN, "%s%d", tzSign, Timezone->current.offset.hours); + else + SNPrintf(text, TIMEZONE_TEXT_LEN, "%s%d:%02d", + tzSign, Timezone->current.offset.hours, Timezone->current.offset.minutes); + } + break; + case TZD_ISO8601_NO_COLON: + SNPrintf(text, TIMEZONE_TEXT_LEN, "%s%02d%02d", TimezoneSignChar(&Timezone->current), + Timezone->current.offset.hours, Timezone->current.offset.minutes); + break; + case TZD_ISO8601_COLON: + SNPrintf(text, TIMEZONE_TEXT_LEN, "%s%02d:%02d", TimezoneSignChar(&Timezone->current), + Timezone->current.offset.hours, Timezone->current.offset.minutes); + break; + case TZD_ISO8601_SHORT: + if (offset % 60 == 0) + SNPrintf(text, TIMEZONE_TEXT_LEN, "%s%d", + TimezoneSignChar(&Timezone->current), Timezone->current.offset.hours); + else + SNPrintf(text, TIMEZONE_TEXT_LEN, "%s%d:%02d", TimezoneSignChar(&Timezone->current), + Timezone->current.offset.hours, Timezone->current.offset.minutes); + break; + case TZD_COMMON: + SNPrintf(text, TIMEZONE_TEXT_LEN, "GMT%s%02d:%02d", TimezoneSignChar(&Timezone->current), + Timezone->current.offset.hours, Timezone->current.offset.minutes); + break; + case TZD_ZONE_IN_PARENS: + GetTimezoneCommon(text, &Timezone->current, TZD_ZONE_IN_PARENS); + break; + case TZD_OFFSET_IN_PARENS: + GetTimezoneCommon(text, &Timezone->current, TZD_OFFSET_IN_PARENS); + break; + case TZD_STD: + GetTimezoneCommon(text, &Timezone->std, TZD_OFFSET_IN_PARENS); + break; + case TZD_DST: + GetTimezoneCommon(text, &Timezone->dst, TZD_OFFSET_IN_PARENS); + break; + default: + text = '\0'; + break; + } +} + +char *GetTimeText(ULONG time) +{ + char *out = AllocStringSafe(128); struct ClockData cd; struct timeval tv; ULONG t = time; - out = AllocStringSafe(128); - if (loc == NULL) - { - return out; - } - if (t == LOCAL_TIME_NOW) { GetSysTime(&tv); @@ -157,36 +157,246 @@ char *GetTimeText(struct AppLocale *loc, ULONG time) } Amiga2Date(t, &cd); - - if (Timezone == NULL) - { - long hoursOffset = (loc->AmigaLocale->loc_GMTOffset / -60L); - long minutsOffset = (loc->AmigaLocale->loc_GMTOffset % -60L); - - SNPrintf(out, 128, "%s %02ld %s %02ld:%02ld:%02ld%s%02ld%02ld %ld", - loc->weekday[cd.wday], - (long)cd.mday, - loc->month[cd.month - 1], - (long)cd.hour, - (long)cd.min, - (long)cd.sec, - (loc->AmigaLocale->loc_GMTOffset < 0L) ? "+" : "", - hoursOffset, - minutsOffset, - (long)cd.year); - } - else - { - SNPrintf(out, 128, "%s %02ld %s %02ld:%02ld:%02ld %s %ld", - loc->weekday[cd.wday], - (long)cd.mday, - loc->month[cd.month - 1], - (long)cd.hour, - (long)cd.min, - (long)cd.sec, - Timezone->current.abbreviation, - (long)cd.year); - } + int weekday = (cd.wday == 0 ? 7 : cd.wday) - 1; + SNPrintf(out, 128, "%s %02ld %s %02ld:%02ld:%02ld %s %ld", + WeekdayLong[weekday], + (long)cd.mday, + Month[cd.month - 1], + (long)cd.hour, + (long)cd.min, + (long)cd.sec, + Timezone->current.abbreviation, + (long)cd.year); return out; } + +static void Amiga2DateStamp(time_t *time, struct DateStamp *date) +{ + long t, d, m, q; + + t = *time; + d = t / SECONDSPERDAY; + m = (t - SECONDSPERDAY * d) / 60; + q = (t - SECONDSPERDAY * d - m * 60) * 50; + + date->ds_Days = d; + date->ds_Minute = m; + date->ds_Tick = q; +} + +static void DateTimeString(struct DateStamp *ds, char *time, char *date, char *day) +{ + struct DateTime dt; + + assert(ds != NULL); + assert(time != NULL); + assert(date != NULL); + assert(day != NULL); + + dt.dat_Stamp = *ds; + dt.dat_Format = FORMAT_DOS; + dt.dat_Flags = 0; + dt.dat_StrDay = (void *)day; + dt.dat_StrDate = (void *)date; + dt.dat_StrTime = (void *)time; + + DateToStr(&dt); +} + +void Utc2DOS(struct timeval *utv, char *buffer, bool showUtc) +{ + char timeString[20]; + char dateString[20]; + char dayString[20]; + + struct DateStamp ds; + struct timeval tv; + + if (showUtc) + Unix2Amiga(utv, &tv); + else + UnixUtc2AmigaLocal(utv, &tv); + + time_t time = tv.tv_secs; + Amiga2DateStamp(&time, &ds); + DateTimeString(&ds, timeString, dateString, dayString); + SNPrintf(buffer, 64, "%s %s %s", dayString, dateString, timeString); +} + +void Utc2ARexxDate(struct timeval *utv, char *buffer, bool showUtc) +{ + struct timeval tv; + struct ClockData cd; + + if (showUtc) + Unix2Amiga(utv, &tv); + else + UnixUtc2AmigaLocal(utv, &tv); + + Amiga2Date(tv.tv_secs, &cd); + SNPrintf(buffer, 10, "%04ld%02ld%02ld", (long)cd.year, (long)cd.month, (long)cd.mday); +} + +void Utc2ARexxTime(struct timeval *utv, char *buffer, bool showUtc) +{ + struct timeval tv; + + if (showUtc) + Unix2Amiga(utv, &tv); + else + UnixUtc2AmigaLocal(utv, &tv); + + long time = tv.tv_secs % (24 * 60 * 60); + LongToStr(time, buffer); +} + +void Utc2ASCII(struct timeval *utv, char *buffer, bool showUtc) +{ + struct timeval tv; + struct ClockData cd; + + if (showUtc) + Unix2Amiga(utv, &tv); + else + UnixUtc2AmigaLocal(utv, &tv); + + Amiga2Date(tv.tv_secs, &cd); + SNPrintf(buffer, DATETIME_TEXT_LEN, ASCIITEMPLATE, + WeekdayShort[cd.wday - 1], Month[cd.month - 1], (long)cd.mday, + (long)cd.hour, (long)cd.min, (long)cd.sec, (long)cd.year); +} + +void Utc2RFC850(struct timeval *utv, char *buffer) +{ + struct timeval tv; + struct ClockData cd; + + Unix2Amiga(utv, &tv); + Amiga2Date(tv.tv_secs, &cd); + SNPrintf(buffer, DATETIME_TEXT_LEN, RFC850TEMPLATE, + WeekdayLong[cd.wday - 1], (long)cd.mday, + Month[cd.month - 1], (long)cd.year % 100, + (long)cd.hour, (long)cd.min, (long)cd.sec); +} + +void Utc2RFC1123(struct timeval *utv, char *buffer) +{ + struct timeval tv; + struct ClockData cd; + + Unix2Amiga(utv, &tv); + Amiga2Date(tv.tv_secs, &cd); + SNPrintf(buffer, DATETIME_TEXT_LEN, RFC1123TEMPLATE, + WeekdayShort[cd.wday - 1], (long)cd.mday, + Month[cd.month - 1], (long)cd.year, + (long)cd.hour, (long)cd.min, (long)cd.sec); +} + +void Utc2RFC2822(struct timeval *utv, char *buffer, bool showUtc) +{ + struct timeval tv; + struct ClockData cd; + + if (showUtc) + { + Unix2Amiga(utv, &tv); + Amiga2Date(tv.tv_secs, &cd); + SNPrintf(buffer, DATETIME_TEXT_LEN, RFC2822TEMPLATE, + (long)cd.mday, Month[cd.month - 1], (long)cd.year, + (long)cd.hour, (long)cd.min, (long)cd.sec, + "+", 0L, 0L); + } + else + { + UnixUtc2AmigaLocal(utv, &tv); + Amiga2Date(tv.tv_secs, &cd); + SNPrintf(buffer, DATETIME_TEXT_LEN, RFC2822TEMPLATE, + (long)cd.mday, Month[cd.month - 1], (long)cd.year, + (long)cd.hour, (long)cd.min, (long)cd.sec, + TimezoneSignChar(&Timezone->current), + (long)Timezone->current.offset.hours, + (long)Timezone->current.offset.minutes); + } +} + +void Utc2RFC3339(struct timeval *utv, char *buffer, bool showUtc) +{ + struct timeval tv; + struct ClockData cd; + + if (showUtc) + { + Unix2Amiga(utv, &tv); + Amiga2Date(tv.tv_secs, &cd); + SNPrintf(buffer, DATETIME_TEXT_LEN, RFC3339TEMPLATEUTC, + (long)cd.year, (long)cd.month, (long)cd.mday, + (long)cd.hour, (long)cd.min, (long)cd.sec, + (long)(tv.tv_micro / 10000)); + } + else + { + UnixUtc2AmigaLocal(utv, &tv); + Amiga2Date(tv.tv_secs, &cd); + SNPrintf(buffer, DATETIME_TEXT_LEN, RFC3339TEMPLATELOC, + (long)cd.year, (long)cd.month, (long)cd.mday, + (long)cd.hour, (long)cd.min, (long)cd.sec, + (long)(tv.tv_micro / 10000), + TimezoneSignChar(&Timezone->current), + (long)Timezone->current.offset.hours, + (long)Timezone->current.offset.minutes); + } +} + +void Utc2ISO8601(struct timeval *utv, char *buffer, bool showUtc) +{ + struct timeval tv; + struct ClockData cd; + + if (showUtc) + { + Unix2Amiga(utv, &tv); + Amiga2Date(tv.tv_secs, &cd); + SNPrintf(buffer, DATETIME_TEXT_LEN, ISO8601TEMPLATEUTC, + (long)cd.year, (long)cd.month, (long)cd.mday, + (long)cd.hour, (long)cd.min, (long)cd.sec); + } + else + { + UnixUtc2AmigaLocal(utv, &tv); + Amiga2Date(tv.tv_secs, &cd); + SNPrintf(buffer, DATETIME_TEXT_LEN, ISO8601TEMPLATELOC, + (long)cd.year, (long)cd.month, (long)cd.mday, + (long)cd.hour, (long)cd.min, (long)cd.sec, + TimezoneSignChar(&Timezone->current), + (long)Timezone->current.offset.hours, + (long)Timezone->current.offset.minutes); + } +} + +void LogTransitionMap(void) +{ + struct PosixTransitionTime *transition; + struct DateStamp ds; + time_t next; + char timeString[20]; + char dateString[20]; + char dayString[20]; + int i; + + for (i = 0; i < 138; i++) + { + transition = &Timezone->transitions[i]; + next = transition->time; + Amiga2DateStamp(&next, &ds); + DateTimeString(&ds, timeString, dateString, dayString); + LogNoticeMsg("Timezone", + "Changing (%010ld) to %s time on %s %s at %s %s", + (long)next, + transition->isdst ? tzDstAbbr : tzStdAbbr, + dayString, dateString, timeString, + transition->isdst + ? Timezone->std.abbreviation + : Timezone->dst.abbreviation); + } +} diff --git a/locale.h b/locale.h index d548344..2b2c864 100644 --- a/locale.h +++ b/locale.h @@ -27,34 +27,49 @@ #ifndef LOCALE_H_INCLUDED #define LOCALE_H_INCLUDED -#include +#include "compiler.h" -struct AppLocale -{ - const char *mon[12]; - const char *month[12]; - const char *wday[7]; - const char *weekday[7]; - const char *X_fmt; - const char *x_fmt; - const char *c_fmt; - const char *t_fmt_ampm; - const char *am; - const char *pm; - const char *am_pm[2]; - const char *date_fmt; - struct Locale *AmigaLocale; -}; +#include +#include #define LOCAL_TIME_NOW 0 -#define ZONE_IN_PARENS 0 -#define OFFSET_IN_PARENS 1 -#define UTC_NO_PARENS 2 + +#define TZD_SECONDS_AHEAD 1 +#define TZD_MINUTES_AHEAD 2 +#define TZD_TZ_LONG 3 +#define TZD_TZ_SHORT 4 +#define TZD_ISO8601_NO_COLON 5 +#define TZD_ISO8601_COLON 6 +#define TZD_ISO8601_SHORT 7 +#define TZD_COMMON 8 +#define TZD_ZONE_IN_PARENS 9 +#define TZD_OFFSET_IN_PARENS 10 +#define TZD_STD 11 +#define TZD_DST 12 + #define TIMEZONE_TEXT_LEN 32 -struct AppLocale *OpenAppLocale(struct Locale *); -void CloseAppLocale(struct AppLocale *); -char* GetTimeText(struct AppLocale *, ULONG); -void GetTimezoneText(struct AppLocale *, char *, long); +#define DATETIME_TEXT_LEN 40 +#define ASCIITEMPLATE "%s %s %ld %02ld:%02ld:%02ld %04ld" +#define RFC850TEMPLATE "%s, %02ld-%s-%02ld %02ld:%02ld:%02ld GMT" +#define RFC1123TEMPLATE "%s, %02ld %s %04ld %02ld:%02ld:%02ld GMT" +#define RFC2822TEMPLATE "%02ld. %s %04ld %02ld:%02ld:%02ld %s%02ld%02ld" +#define RFC3339TEMPLATEUTC "%04ld-%02ld-%02ld %02ld:%02ld:%02ld.%02ldZ" +#define RFC3339TEMPLATELOC "%04ld-%02ld-%02ld %02ld:%02ld:%02ld.%02ld%s%02ld:%02ld" +#define ISO8601TEMPLATEUTC "%04ld%02ld%02ldT%02ld%02ld%02ldZ" +#define ISO8601TEMPLATELOC "%04ld-%02ld-%02ldT%02ld:%02ld:%02ld%s%02ld:%02ld" + +char *GetTimeText(ULONG); +void GetTimezoneText(char *, long); +void LogTransitionMap(void); +void Utc2ASCII(struct timeval *, char *, bool); +void Utc2DOS(struct timeval *, char *, bool); +void Utc2ARexxDate(struct timeval *, char *, bool); +void Utc2ARexxTime(struct timeval *, char *, bool); +void Utc2RFC850(struct timeval *, char *); +void Utc2RFC1123(struct timeval *, char *); +void Utc2RFC2822(struct timeval *, char *, bool); +void Utc2RFC3339(struct timeval *, char *, bool); +void Utc2ISO8601(struct timeval *, char *, bool); #endif diff --git a/log.c b/log.c index c03e771..29885bc 100644 --- a/log.c +++ b/log.c @@ -34,6 +34,7 @@ #include "string.h" #include "global.h" #include "message.h" +#include "setting.h" static void VLogLine(enum LogSeverity level, const char *module, const char *format, va_list ap) { @@ -49,6 +50,9 @@ static void VLogLine(enum LogSeverity level, const char *module, const char *for void LogMemTraceMsg(const char *module, const char *format, ...) { + if (Settings->NoLog) + return; + va_list args; va_start(args, format); VLogLine(MemTraceMessage, module, format, args); @@ -57,6 +61,9 @@ void LogMemTraceMsg(const char *module, const char *format, ...) void LogDebugMsg(const char *module, const char *format, ...) { + if (Settings->NoLog) + return; + va_list args; va_start(args, format); VLogLine(DebugMessage, module, format, args); @@ -65,6 +72,9 @@ void LogDebugMsg(const char *module, const char *format, ...) void LogTraceMsg(const char *module, const char *format, ...) { + if (Settings->NoLog) + return; + va_list args; va_start(args, format); VLogLine(TraceMessage, module, format, args); @@ -73,6 +83,9 @@ void LogTraceMsg(const char *module, const char *format, ...) void LogInfoMsg(const char *module, const char *format, ...) { + if (Settings->NoLog) + return; + va_list args; va_start(args, format); VLogLine(InfoMessage, module, format, args); @@ -81,6 +94,9 @@ void LogInfoMsg(const char *module, const char *format, ...) void LogNoticeMsg(const char *module, const char *format, ...) { + if (Settings->NoLog) + return; + va_list args; va_start(args, format); VLogLine(NoticeMessage, module, format, args); @@ -89,6 +105,9 @@ void LogNoticeMsg(const char *module, const char *format, ...) void LogWarnMsg(const char *module, const char *format, ...) { + if (Settings->NoLog) + return; + va_list args; va_start(args, format); VLogLine(WarningMessage, module, format, args); @@ -97,6 +116,9 @@ void LogWarnMsg(const char *module, const char *format, ...) void LogErrorMsg(const char *module, const char *format, ...) { + if (Settings->NoLog) + return; + va_list args; va_start(args, format); VLogLine(ErrorMessage, module, format, args); diff --git a/logger.c b/logger.c index 68d5436..471c37a 100644 --- a/logger.c +++ b/logger.c @@ -45,7 +45,7 @@ #define UTILITYNAME "utility.library" #endif -#define TIMELOGGER "TimeLogger 1.11 (11.01.2021)" +#define TIMELOGGER "TimeLogger 1.12 (31.01.2021)" #define ARGSFORMAT "D=DEBUG/K/N,M=MODULEOFF/S,S=SEVERITYOFF/S,H=HELP/S" #define MODULENAME "Logger" diff --git a/main.c b/main.c index 0546942..2592057 100644 --- a/main.c +++ b/main.c @@ -41,18 +41,11 @@ #define MODULENAME "Main" const char *vers = "\0$VER: " APP_ID; -static const char *template = KEYWORD_TEMPLATE; +static const char *template = KWD_TMPL; static void GetCliSettings(void); static void GetWbSettings(struct WBStartup *); -static void ShowLocalTime(void) -{ - char *time = GetTimeText(AppLocale, LOCAL_TIME_NOW); - LogInfo("Local time is %s", time); - FreeMemSafe(time); -} - int main(int argc, char **argv) { InitMemSafe(); @@ -69,10 +62,9 @@ int main(int argc, char **argv) return RETURN_FAIL; } - InitTimezone(); LoadSettings(); - if (argc != 0) + if (Cli() != NULL) GetCliSettings(); else GetWbSettings((struct WBStartup *)argv); @@ -81,14 +73,15 @@ int main(int argc, char **argv) SanitizeSettings(); ShowSettings(); + InitTimezone(); StartBroker(); - ShowLocalTime(); - CleanupTimezone(); + StopTimezoneCheck(); CloseSocketLibrary(); CloseLibraries(); CleanupSettings(); - CleanupArexx(); + CleanupARexx(); + FreePosixTimezone(Timezone); FreeAllSafe(); LogNotice("Shutdown complete"); @@ -99,23 +92,25 @@ static void GetCliSettings(void) { struct AppSettings *settings; struct RDArgs *inArgs; - long args[KEYWORD_COUNT]; + LONG args[KEYWORD_COUNT]; int i; + LogTrace("Parsing CLI settings"); + for (i = 0; i < KEYWORD_COUNT; i++) args[i] = 0; settings = CreateSettings(CliSettingType); - inArgs = ReadArgs((void *)template, (void *)&args, NULL); + inArgs = ReadArgs((CONST_STRPTR) template, (LONG *)&args, NULL); if (inArgs) { - // Keyword order in template needs to match order in settingFunctions for (i = 0; i < KEYWORD_COUNT; i++) { - if (args[i] != 0) + LONG j = FindArg((STRPTR) template, (STRPTR)settingFunctions[i].Name); + if (j != -1 && args[j] != 0) { - settingFunctions[i].Function(settings, (void *)args[i]); + settingFunctions[i].Function(settings, (void *)args[j]); } } FreeArgs(inArgs); @@ -132,6 +127,8 @@ static void GetWbSettings(struct WBStartup *wbs) BPTR oldDir; int argNo, i; + LogTrace("Parsing Workbench settings"); + filename = (STRPTR)AllocStringSafe(MAXFILEPATHLEN); settings = CreateSettings(WbSettingType); diff --git a/message.c b/message.c index ebd77b0..48a8ab3 100644 --- a/message.c +++ b/message.c @@ -40,7 +40,7 @@ struct MsgPort *volatile SyncerPort; struct MsgPort *volatile WindowPort; struct MsgPort *volatile MemoryPort; struct MsgPort *volatile ControlPort; -struct MsgPort *volatile ArexxPort; +struct MsgPort *volatile ARexxPort; int outstanding; static void PublishPort(long, char *); @@ -52,7 +52,7 @@ void InitMessages(void) WindowPort = NULL; MemoryPort = NULL; ControlPort = NULL; - ArexxPort = NULL; + ARexxPort = NULL; outstanding = 0; CreateMessagePort(MSGPORT_MEMORY); @@ -93,9 +93,9 @@ void CleanupPorts(void) DestroyMessagePort(MSGPORT_CONTROL); } - if (ArexxPort != NULL) + if (ARexxPort != NULL) { - RemPort((struct MsgPort *)ArexxPort); + RemPort((struct MsgPort *)ARexxPort); DestroyMessagePort(MSGPORT_AREXX); } @@ -130,7 +130,7 @@ static struct MsgPort *FindMessagePort(long port) return (struct MsgPort *)ControlPort; break; case MSGPORT_AREXX: - return (struct MsgPort *)ArexxPort; + return (struct MsgPort *)ARexxPort; break; default: return NULL; @@ -167,7 +167,7 @@ static struct MsgPort *volatile *FindMessagePortAddress(long port, char **name) s = "Control"; break; case MSGPORT_AREXX: - p = &ArexxPort; + p = &ARexxPort; s = "ARexx"; break; default: diff --git a/message.h b/message.h index 601d0ac..21306aa 100644 --- a/message.h +++ b/message.h @@ -33,6 +33,8 @@ #define LOGGER_PORT_NAME "TimeLoggerIn" #define CONTROL_PORT_NAME "TimeKeeperCtrl" #define AREXX_PORT_NAME "TIMEKEEPER.1" +#define AREXX_ERROR_VAR_CODE "TIMEKEEPER.ERRORCODE" +#define AREXX_ERROR_VAR_TEXT "TIMEKEEPER.ERRORTEXT" #define ATK_DEFAULT 0 #define ATK_LASTSYNC 1 @@ -54,6 +56,8 @@ #define ATK_ID 50 #define ATK_VERSION 51 #define ATK_STATUS 52 +#define ATK_TIME 53 +#define ATK_TIMEZONE 54 #define ATK_CTRL_OK 0 #define ATK_CTRL_ERROR 10 diff --git a/notify.c b/notify.c index 8aba6c1..c8412bc 100644 --- a/notify.c +++ b/notify.c @@ -26,6 +26,7 @@ #include #include +#include #include "config.h" #include "mem.h" @@ -37,21 +38,21 @@ struct NotifyRequestNode { long Registered; struct NotifyRequest *Request; - volatile struct NotifyRequestNode *Next; + struct NotifyRequestNode *Next; }; -volatile static struct NotifyRequestNode *Last = NULL; +static struct NotifyRequestNode *Last = NULL; static void AddRequest(struct NotifyRequest *request) { - volatile struct NotifyRequestNode *node; - - node = AllocStructSafe(struct NotifyRequestNode); + struct NotifyRequestNode *node = AllocStructSafe(struct NotifyRequestNode); if (node == NULL) { return; } + // Init node + node->Registered = DOSFALSE; node->Request = request; node->Next = NULL; @@ -71,14 +72,9 @@ static void AddRequest(struct NotifyRequest *request) void ActivateNotifyPort(struct MsgPort *port) { - volatile struct NotifyRequestNode *current; + assert(port != NULL); - if (port == NULL) - { - return; - } - - current = Last; + struct NotifyRequestNode *current = Last; while (current != NULL) { if (current->Registered != DOSTRUE) @@ -92,7 +88,7 @@ void ActivateNotifyPort(struct MsgPort *port) } else { - LogWarn("Could not get notifications from %s", current->Request->nr_Name); + LogWarn("Could not get notifications for %s", current->Request->nr_Name); } } current = current->Next; @@ -115,7 +111,7 @@ void WatchFile(char *file, long type) return; } - request->nr_Flags = NRF_SEND_MESSAGE | NRB_WAIT_REPLY; + request->nr_Flags = NRF_SEND_MESSAGE | NRF_WAIT_REPLY; request->nr_UserData = type; AddRequest(request); @@ -130,6 +126,7 @@ void CleanupNotifications(void) { if (current->Registered == DOSTRUE) { + LogTrace("Removing notification for %s", current->Request->nr_Name); EndNotify(current->Request); } FreeMemSafe((void *)current->Request->nr_Name); @@ -139,4 +136,6 @@ void CleanupNotifications(void) FreeMemSafe((void *)current); current = next; } + + Last = NULL; } \ No newline at end of file diff --git a/ptz.c b/ptz.c index 45ab1f3..8e37aa9 100644 --- a/ptz.c +++ b/ptz.c @@ -32,9 +32,8 @@ #include #include "ptz.h" -#include "string.h" -#include "global.h" #include "mem.h" +#include "string.h" #include "logmod.h" #define MODULENAME "Posix TZ" @@ -59,7 +58,7 @@ static const char *ParseInt(const char *p, int min, int max, int *result) /* * Parse abbreviation part of POSIX TZ string. */ -static const char *ParseAbbr(const char *p, char **result) +static const char *ParseAbbr(const char *p, char *result) { const char *start = p; @@ -72,8 +71,8 @@ static const char *ParseAbbr(const char *p, char **result) return NULL; } - *result = AllocStringSafe(p - start); - CopyMem((APTR)start, *result, p - start); + int l = (p - start > MAX_ABBR - 1 ? MAX_ABBR - 1 : p - start); + CopyMem((APTR)start, (APTR)result, (ULONG)l); return ++p; } @@ -84,8 +83,8 @@ static const char *ParseAbbr(const char *p, char **result) if (p - start < 3) return NULL; - *result = AllocStringSafe(p - start + 1); - CopyMem((APTR)start, *result, p - start); + int l = (p - start > MAX_ABBR - 1 ? MAX_ABBR - 1 : p - start); + CopyMem((APTR)start, (APTR)result, (ULONG)l); return p; } @@ -215,7 +214,7 @@ static bool ParsePosixTz(const char *tz, struct PosixTimezone *result) if (*p == ':') return false; - p = ParseAbbr(p, &result->std.abbreviation); + p = ParseAbbr(p, result->std.abbreviation); p = ParseTime(p, 0, 24, -1, &result->std.offset); if (p == NULL) @@ -224,15 +223,15 @@ static bool ParsePosixTz(const char *tz, struct PosixTimezone *result) if (*p == '\0') return true; - p = ParseAbbr(p, &result->dst.abbreviation); + p = ParseAbbr(p, result->dst.abbreviation); if (p == NULL) return false; // Default is one hour ahead of std result->dst.offset.sign = result->std.offset.sign; - result->dst.offset.hours = (result->dst.offset.sign < 0) - ? result->std.offset.hours - 1 - : result->std.offset.hours + 1; + result->dst.offset.hours = (result->dst.offset.sign > 0) + ? result->std.offset.hours + 1 + : result->std.offset.hours - 1; result->dst.offset.minutes = result->std.offset.minutes; result->dst.offset.seconds = result->std.offset.seconds; @@ -250,29 +249,6 @@ static bool ParsePosixTz(const char *tz, struct PosixTimezone *result) return p != NULL && *p == '\0'; } -char *PosixTimezoneSignChar(struct PosixLocalTimezone *ptz) -{ - static char *positive = "+"; - static char *negative = "-"; - - if (ptz == NULL) - { - return positive; - } - - if (ptz->offset.hours == 0 && ptz->offset.minutes == 0 && ptz->offset.seconds == 0) - { - return positive; - } - - if (ptz->offset.sign >= 0) - { - return positive; - } - - return negative; -} - static int FindWeekDay(int year, int month, int day) { int h = 0; @@ -315,7 +291,19 @@ static int FindDay(int year, int month, int week, int day) days = 29; } - if (week == 5) + if (week == 1) + { + int i; + for (i = 1; i <= days; i++) + { + int a = FindWeekDay(year, month, i); + if (day == a) + { + return i; + } + } + } + else if (week == 5) { int i; for (i = (days - 6); i <= days; i++) @@ -334,25 +322,26 @@ static int FindDay(int year, int month, int week, int day) for (i = 1; i <= days; i++) { int a = FindWeekDay(year, month, i); - if (a == 0 && i != 1) - { - j++; - } - if (day == a && week == j) { return i; } + + if (a == 0 && i != 1) + { + j++; + } } } - return -1; + LogError("Failed to find transition date"); + return 1; } -static ULONG FindTransitionTime(int year, struct PosixTransition *transition) +static time_t FindTransitionTime(int year, struct PosixTransition *transition) { struct ClockData day; - ULONG transitionTime = 0; + signed long transitionTime = 0; day.sec = 0; day.min = 0; @@ -387,7 +376,6 @@ static ULONG FindTransitionTime(int year, struct PosixTransition *transition) break; } - // TODO: Validate sign transitionTime += (transition->offset.sign < 0 ? -1 : 1) * (transition->offset.hours * 60 * 60 + @@ -397,90 +385,20 @@ static ULONG FindTransitionTime(int year, struct PosixTransition *transition) return transitionTime; } -struct PosixTransitionTime *FindNextTransition(time_t now) -{ - int i = 0; - - if (Timezone->start.type == 0) - { - return NULL; - } - - while (Timezone->transitions[i].time < now) - { - i++; - } - - return &Timezone->transitions[i]; -} - -static char *GetVariable(const char *variable) -{ - unsigned long memsize = 256; - STRPTR var = (STRPTR)variable; - - STRPTR buf = (STRPTR)AllocStringSafe(memsize); - if (!buf) - { - return NULL; - } - - if (GetVar(var, buf, memsize - 1, GVF_GLOBAL_ONLY) > 0) - { - return (char *)buf; - } - - FreeMemSafe(buf); - return NULL; -} - -void CleanupPosixTimezone(void) -{ - if (Timezone != NULL) - { - if (Timezone->std.abbreviation != NULL) - { - FreeMemSafe(Timezone->std.abbreviation); - } - if (Timezone->dst.abbreviation != NULL) - { - FreeMemSafe(Timezone->dst.abbreviation); - } - - FreeMemSafe(Timezone); - Timezone = NULL; - } -} - -static void InitFromTZone(void) -{ - if (Timezone->dst.abbreviation != NULL) - { - // DST is present - Timezone->current.abbreviation = Timezone->dst.abbreviation; - Timezone->current.offset = Timezone->dst.offset; - } - else - { - Timezone->current.abbreviation = Timezone->std.abbreviation; - Timezone->current.offset = Timezone->std.offset; - } -} - -static void BuildTransitionMap() +static void BuildTransitionMap(struct PosixTimezone *ptz) { int i = 0; bool isdst = false; bool north; - switch (Timezone->start.type) + switch (ptz->start.type) { case JulianDay: case JulianDayLeap: - isdst = Timezone->start.day > Timezone->end.day; + isdst = ptz->start.day > ptz->end.day; break; case DayWeekMonth: - isdst = Timezone->start.month > Timezone->end.month; + isdst = ptz->start.month > ptz->end.month; break; default: return; @@ -489,103 +407,116 @@ static void BuildTransitionMap() north = isdst ? false : true; - Timezone->transitions[0].time = 0; - Timezone->transitions[0].isdst = isdst; + ptz->transitions[0].time = 0; + ptz->transitions[0].isdst = isdst; - for (i = 0; i < 138 / 2; i++) + for (i = 0; i < MAX_TRANSITIONS / 2 - 1; i++) { - ULONG time; + time_t time; + isdst = isdst ? false : true; + time = north + ? FindTransitionTime(i + 1978, &ptz->start) + : FindTransitionTime(i + 1978, &ptz->end); + + ptz->transitions[i * 2 + 1].time = time; + ptz->transitions[i * 2 + 1].isdst = isdst; isdst = isdst ? false : true; time = north - ? FindTransitionTime(i + 1978, &Timezone->start) - : FindTransitionTime(i + 1978, &Timezone->end); + ? FindTransitionTime(i + 1978, &ptz->end) + : FindTransitionTime(i + 1978, &ptz->start); - Timezone->transitions[i * 2 + 1].time = time; - Timezone->transitions[i * 2 + 1].isdst = isdst; - - isdst = isdst ? false : true; - time = north - ? FindTransitionTime(i + 1978, &Timezone->end) - : FindTransitionTime(i + 1978, &Timezone->start); - - Timezone->transitions[i * 2 + 2].time = time; - Timezone->transitions[i * 2 + 2].isdst = isdst; + ptz->transitions[i * 2 + 2].time = time; + ptz->transitions[i * 2 + 2].isdst = isdst; } - Timezone->transitions[137].time = 2147483647; - Timezone->transitions[137].isdst = false; + ptz->transitions[MAX_TRANSITIONS - 1].time = 2147483647; + ptz->transitions[MAX_TRANSITIONS - 1].isdst = false; } -int InitPosixTimezone(char **variable) +char *TimezoneSignChar(struct PosixLocalTimezone *ptz) { - int result = 5; - char *var; + static char *positive = "+"; + static char *negative = "-"; - Timezone = NULL; - - var = GetVariable("TZ"); - if (var == NULL) + if (ptz == NULL) { - var = GetVariable("TZONE"); - result = 4; + return positive; } - if (var == NULL) + if (ptz->offset.hours == 0 && ptz->offset.minutes == 0 && ptz->offset.seconds == 0) { - return 1; + return positive; } - *variable = StrDupSafe(var); - Timezone = AllocStructSafe(struct PosixTimezone); - - if (!ParsePosixTz(var, Timezone)) + if (ptz->offset.sign >= 0) { - FreeMemSafe(var); - CleanupPosixTimezone(); - return result - 2; + return positive; } - FreeMemSafe(var); + return negative; +} - if (Timezone->start.type == 0) +void FreePosixTimezone(struct PosixTimezone *ptz) +{ + if (ptz == NULL) + return; + + FreeMemSafe(ptz); +} + +struct PosixTimezone *InitPosixTimezone(char *variable) +{ + struct PosixTimezone *ptz = AllocStructSafe(struct PosixTimezone); + ptz->source = PosixTypeTimezone; + + if (ParsePosixTz(variable, ptz)) { - InitFromTZone(); + if (ptz->start.type != 0) + { + BuildTransitionMap(ptz); + } } else { - BuildTransitionMap(); + FreePosixTimezone(ptz); + ptz = NULL; } - return result; + return ptz; } -bool SetPosixTimezone(struct timeval *tv) +void SetPosixTimezone(struct PosixTimezone *ptz, struct timeval *tv) { + if (ptz == NULL || ptz->start.type == 0) + return; + int i = 0; - - if (Timezone->start.type == 0) - { - return false; - } - - while (Timezone->transitions[i].time <= tv->tv_secs) - { + time_t time = (time_t)tv->tv_secs; + while (i < MAX_TRANSITIONS && ptz->transitions[i].time <= time) i++; - } - i--; - - if (Timezone->transitions[i].isdst) + if (ptz->transitions[--i].isdst) { - Timezone->current.abbreviation = Timezone->dst.abbreviation; - Timezone->current.offset = Timezone->dst.offset; + StrCopy(ptz->current.abbreviation, ptz->dst.abbreviation); + ptz->current.offset = ptz->dst.offset; } else { - Timezone->current.abbreviation = Timezone->std.abbreviation; - Timezone->current.offset = Timezone->std.offset; + StrCopy(ptz->current.abbreviation, ptz->std.abbreviation); + ptz->current.offset = ptz->std.offset; } - - return true; +} + +struct PosixTransitionTime *FindNextTransition(struct PosixTimezone *ptz, struct timeval *tv) +{ + if (ptz->start.type == 0) + return NULL; + + int i = 0; + time_t time = (time_t)tv->tv_secs; + while (i < MAX_TRANSITIONS && ptz->transitions[i].time < time) + i++; + + return &ptz->transitions[i]; } diff --git a/ptz.h b/ptz.h index bdc58ec..c17d8d9 100644 --- a/ptz.h +++ b/ptz.h @@ -37,6 +37,12 @@ #define JulianDayLeap 2 #define DayWeekMonth 3 +#define CustomTypeTimezone 1 +#define PosixTypeTimezone 2 + +#define MAX_ABBR 10 +#define MAX_TRANSITIONS 138 + struct PosixTimezoneOffset { int sign; // before/after 00:00:00, -1 or 1 @@ -47,7 +53,7 @@ struct PosixTimezoneOffset struct PosixTransition { - int type; // type of transistion [0:2] + int type; // type of transistion [0:3] int day; // day of year (leap or non-leap) [0:365] int weekday; // day of week [0:6] int week; // week of month [1:5] @@ -63,24 +69,25 @@ struct PosixTransitionTime struct PosixLocalTimezone { - char *abbreviation; + char abbreviation[MAX_ABBR]; struct PosixTimezoneOffset offset; }; struct PosixTimezone { + int source; // time zone source [1:2] struct PosixLocalTimezone current; struct PosixLocalTimezone std; struct PosixLocalTimezone dst; struct PosixTransition start; struct PosixTransition end; - struct PosixTransitionTime transitions[138]; + struct PosixTransitionTime transitions[MAX_TRANSITIONS]; }; -int InitPosixTimezone(char **); -void CleanupPosixTimezone(void); -bool SetPosixTimezone(struct timeval *tv); -struct PosixTransitionTime *FindNextTransition(time_t); -char* PosixTimezoneSignChar(struct PosixLocalTimezone *); +char *TimezoneSignChar(struct PosixLocalTimezone *); +struct PosixTimezone *InitPosixTimezone(char *); +void FreePosixTimezone(struct PosixTimezone *); +void SetPosixTimezone(struct PosixTimezone *, struct timeval *); +struct PosixTransitionTime *FindNextTransition(struct PosixTimezone *, struct timeval *); #endif diff --git a/script/SetTZCentralEurope b/script/SetTZCentralEurope new file mode 100644 index 0000000..bb7d243 --- /dev/null +++ b/script/SetTZCentralEurope @@ -0,0 +1,11 @@ +; Set TZ to North East American time zone +; Start with 'EXECUTE SetTZCentralEurope' +; + +ECHO "Changing to Central European time zone ..." +SETENV TZ CET-1CEST,M3.5.0,M10.5.0/3 + +ECHO "To take effect restart AmiTimeKeeper first" + +; Save between reboots with : +; COPY CLONE ENV:TZ ENVARC:TZ diff --git a/script/SetTZNorthEastAmerica b/script/SetTZNorthEastAmerica new file mode 100644 index 0000000..6ffbf72 --- /dev/null +++ b/script/SetTZNorthEastAmerica @@ -0,0 +1,11 @@ +; Set TZ to North East American time zone +; Start with 'EXECUTE SetTZNorthEastAmerica' +; + +ECHO "Changing to North East American time zone ..." +SETENV TZ EST5EDT,M3.2.0,M11.1.0 + +ECHO "To take effect restart AmiTimeKeeper first" + +; Save between reboots with : +; COPY CLONE ENV:TZ ENVARC:TZ diff --git a/setting.c b/setting.c index 6cb857a..e9a4225 100644 --- a/setting.c +++ b/setting.c @@ -46,7 +46,15 @@ static const struct AppSettingKeys SettingKeyStruct = { .Popup = KEYWORD_POPUP, .Readonly = KEYWORD_READONLY, .Expert = KEYWORD_EXPERT, - .Timeout = KEYWORD_TIMEOUT}; + .Timeout = KEYWORD_TIMEOUT, + .Active = KEYWORD_ACTIVE, + .NoLog = KEYWORD_NOLOG, + .TZ = KEYWORD_TZ, + .TimeZoneDisplay = KEYWORD_TZD, + .TimeZoneName = KEYWORD_TZNAME, + .TimeZoneValue = KEYWORD_TZVALUE, + .TimeZoneDst = KEYWORD_TZDST, + .PopupOnStart = KEYWORD_POPUP2}; const struct AppSettings DefaultSettings = { .Type = DefaultSettingType, @@ -60,7 +68,15 @@ const struct AppSettings DefaultSettings = { .Expert = EXPERT_DEF, .Priority = PRIORITY_DEF, .Threshold = THRESHOLD_DEF, - .Values = 0xFFFF}; + .Active = ACTIVE_DEF, + .NoLog = NOLOG_DEF, + .TZ = TZ_DEF, + .TimeZoneDisplay = TZD_DEF, + .TimeZoneName = TZNAME_DEF, + .TimeZoneValue = TZVALUE_DEF, + .TimeZoneDst = TZDST_DEF, + .Values = 0xFFFFF, + .PopupOnStart = false}; static void SetPrioritySetting(struct AppSettings *, void *); static void SetIntervalSetting(struct AppSettings *, void *); @@ -72,11 +88,20 @@ static void SetReadOnlySetting(struct AppSettings *, void *); static void SetExpertSetting(struct AppSettings *, void *); static void SetPopupSetting(struct AppSettings *, void *); static void SetPopKeySetting(struct AppSettings *, void *); +static void SetActiveSetting(struct AppSettings *, void *); +static void SetNoLogSetting(struct AppSettings *, void *); +static void SetTZSetting(struct AppSettings *, void *); +static void SetTimeZoneDisplaySetting(struct AppSettings *, void *); +static void SetTimeZoneNameSetting(struct AppSettings *, void *); +static void SetTimeZoneValueSetting(struct AppSettings *, void *); +static void SetTimeZoneDstSetting(struct AppSettings *, void *); +static void ExecutePopupFunction(struct AppSettings *, void *); -// Keyword order in settingFunctions needs to match order in keyword template const struct SettingFunc settingFunctions[] = { {KEYWORD_READONLY, SetReadOnlySetting}, {KEYWORD_EXPERT, SetExpertSetting}, + {KEYWORD_ACTIVE, SetActiveSetting}, + {KEYWORD_NOLOG, SetNoLogSetting}, {KEYWORD_SERVER, SetDestinationAddressSetting}, {KEYWORD_PORT, SetDestinationPortSetting}, {KEYWORD_TIMEOUT, SetTimeoutSetting}, @@ -84,7 +109,13 @@ const struct SettingFunc settingFunctions[] = { {KEYWORD_INTERVAL, SetIntervalSetting}, {KEYWORD_PRIORITY, SetPrioritySetting}, {KEYWORD_POPKEY, SetPopKeySetting}, - {KEYWORD_POPUP, SetPopupSetting}}; + {KEYWORD_POPUP, SetPopupSetting}, + {KEYWORD_TZ, SetTZSetting}, + {KEYWORD_TZD, SetTimeZoneDisplaySetting}, + {KEYWORD_TZNAME, SetTimeZoneNameSetting}, + {KEYWORD_TZVALUE, SetTimeZoneValueSetting}, + {KEYWORD_TZDST, SetTimeZoneDstSetting}, + {KEYWORD_POPUP2, ExecutePopupFunction}}; #define MAXSETTINGLINELEN 256 @@ -122,6 +153,13 @@ void ShowAppSettings(struct AppSettings *settings) LogDebug(settingValueLong, SettingKeys->Interval, settings->Interval); LogDebug(settingValueString, SettingKeys->Readonly, BooleanAsText(settings->Readonly)); LogDebug(settingValueString, SettingKeys->Expert, BooleanAsText(settings->Expert)); + LogDebug(settingValueString, SettingKeys->Active, BooleanAsText(settings->Active)); + LogDebug(settingValueString, SettingKeys->NoLog, BooleanAsText(settings->NoLog)); + LogDebug(settingValueString, SettingKeys->TZ, settings->TZ); + LogDebug(settingValueString, SettingKeys->TimeZoneName, settings->TimeZoneName); + LogDebug(settingValueLong, SettingKeys->TimeZoneValue, settings->TimeZoneValue); + LogDebug(settingValueLong, SettingKeys->TimeZoneDisplay, settings->TimeZoneDisplay); + LogDebug(settingValueLong, SettingKeys->TimeZoneDst, settings->TimeZoneDst); } void ShowSettings(void) @@ -278,12 +316,63 @@ static void SetExpertSetting(struct AppSettings *settings, void *value) &settings->Expert, value, true); } +static void SetActiveSetting(struct AppSettings *settings, void *value) +{ + ParseBooleanSetting(settings, ActiveSet, SettingKeys->Active, + &settings->Active, value, true); +} + +static void SetNoLogSetting(struct AppSettings *settings, void *value) +{ + ParseBooleanSetting(settings, NoLogSet, SettingKeys->NoLog, + &settings->NoLog, value, true); +} + static void SetPopupSetting(struct AppSettings *settings, void *value) { ParseBooleanSetting(settings, PopUpSet, SettingKeys->Popup, &settings->Popup, value, false); } +static void SetTZSetting(struct AppSettings *settings, void *value) +{ + LogFoundSetting(settings->Type, SettingKeys->TZ); + settings->TZ = StrDupSafe((const char *)value); + settings->Values |= TzSet; +} + +static void SetTimeZoneDisplaySetting(struct AppSettings *settings, void *value) +{ + ParseLongSetting(settings, TimeZoneDisplaySet, SettingKeys->TimeZoneDisplay, + &settings->TimeZoneDisplay, value); +} + +static void SetTimeZoneNameSetting(struct AppSettings *settings, void *value) +{ + LogFoundSetting(settings->Type, SettingKeys->TimeZoneName); + settings->TimeZoneName = StrDupSafe((const char *)value); + settings->Values |= TimeZoneNameSet; +} + +static void SetTimeZoneValueSetting(struct AppSettings *settings, void *value) +{ + ParseLongSetting(settings, TimeZoneValueSet, SettingKeys->TimeZoneValue, + &settings->TimeZoneValue, value); +} + +static void SetTimeZoneDstSetting(struct AppSettings *settings, void *value) +{ + ParseLongSetting(settings, TimeZoneDstSet, SettingKeys->TimeZoneDst, + &settings->TimeZoneDst, value); +} + +static void ExecutePopupFunction(struct AppSettings *settings, void *value) +{ + // Run-time switch. Show settings window when starting + LogFoundSetting(settings->Type, SettingKeys->PopupOnStart); + settings->PopupOnStart = true; +} + static void ParseSetting(struct AppSettings *settings, char *line) { char *value; @@ -399,7 +488,13 @@ static void WriteSetting(BPTR file, const char *format, ...) void SaveSettings(bool persist) { char low[MAXLONGLONGCHARSIZE]; + const char *dirName = persist ? persistentPrefsDir : prefsDir; const char *fileName = persist ? persistentPrefsFile : prefsFile; + + BPTR lock = CreateDir((STRPTR)dirName); + if (lock != NULL) + UnLock(lock); + BPTR file = Open((STRPTR)fileName, MODE_NEWFILE); if (!file) { @@ -423,6 +518,21 @@ void SaveSettings(bool persist) WriteSetting(file, saveValueLong, SettingKeys->Interval, Settings->Interval); WriteSetting(file, saveValueString, SettingKeys->Readonly, BooleanAsText(Settings->Readonly)); WriteSetting(file, saveValueString, SettingKeys->Expert, BooleanAsText(Settings->Expert)); + WriteSetting(file, saveValueString, SettingKeys->Active, BooleanAsText(Settings->Active)); + WriteSetting(file, saveValueString, SettingKeys->NoLog, BooleanAsText(Settings->NoLog)); + WriteSetting(file, saveValueLong, SettingKeys->TimeZoneDisplay, Settings->TimeZoneDisplay); + + if (Settings->TZ != NULL && *Settings->TZ != '\0') + WriteSetting(file, saveValueString, SettingKeys->TZ, Settings->TZ); + + if (Settings->TimeZoneName != NULL && *Settings->TimeZoneName != '\0') + WriteSetting(file, saveValueString, SettingKeys->TimeZoneName, Settings->TimeZoneName); + + if (Settings->TimeZoneValue != TZVALUE_DEF) + WriteSetting(file, saveValueLong, SettingKeys->TimeZoneValue, Settings->TimeZoneValue); + + if (Settings->TimeZoneDst != TZDST_DEF) + WriteSetting(file, saveValueLong, SettingKeys->TimeZoneDst, Settings->TimeZoneDst); Close(file); } @@ -442,26 +552,28 @@ struct AppSettings *CopySettings(const struct AppSettings *settings) s->DestinationAddress = StrDupSafe(settings->DestinationAddress); s->DestinationPort = StrDupSafe(settings->DestinationPort); s->PopKey = StrDupSafe(settings->PopKey); - s->Values = 0xFFFF; + s->TZ = StrDupSafe(settings->TZ); + s->TimeZoneName = StrDupSafe(settings->TimeZoneName); + s->Values = 0xFFFFF; return s; } void FreeSettings(struct AppSettings *settings) { if (settings->DestinationAddress != NULL) - { FreeMemSafe(settings->DestinationAddress); - } if (settings->DestinationPort != NULL) - { FreeMemSafe(settings->DestinationPort); - } if (settings->PopKey != NULL) - { FreeMemSafe(settings->PopKey); - } + + if (settings->TZ != NULL) + FreeMemSafe(settings->TZ); + + if (settings->TimeZoneName != NULL) + FreeMemSafe(settings->TimeZoneName); FreeMemSafe(settings); } @@ -569,6 +681,21 @@ void ApplyAppSettings(struct AppSettings *settings, bool quiet) break; } + if (settings->Type == CliSettingType || settings->Type == WbSettingType) + Settings->PopupOnStart = settings->PopupOnStart; + + ApplyStringSetting(settings, TzSet, SettingKeys->TZ, + &Settings->TZ, settings->TZ, quiet); + + ApplyLongSetting(settings, TimeZoneDisplaySet, SettingKeys->TimeZoneDisplay, + &Settings->TimeZoneDisplay, &settings->TimeZoneDisplay, quiet); + + ApplyStringSetting(settings, TimeZoneNameSet, SettingKeys->TimeZoneName, + &Settings->TimeZoneName, settings->TimeZoneName, quiet); + + ApplyLongSetting(settings, TimeZoneValueSet, SettingKeys->TimeZoneValue, + &Settings->TimeZoneValue, &settings->TimeZoneValue, quiet); + ApplyBooleanSetting(settings, PopUpSet, SettingKeys->Popup, &Settings->Popup, &settings->Popup, quiet); @@ -584,12 +711,21 @@ void ApplyAppSettings(struct AppSettings *settings, bool quiet) ApplyLongSetting(settings, IntervalSet, SettingKeys->Interval, &Settings->Interval, &settings->Interval, quiet); + ApplyLongSetting(settings, TimeZoneDstSet, SettingKeys->TimeZoneDst, + &Settings->TimeZoneDst, &settings->TimeZoneDst, quiet); + ApplyBooleanSetting(settings, ReadonlySet, SettingKeys->Readonly, &Settings->Readonly, &settings->Readonly, quiet); ApplyBooleanSetting(settings, ExpertSet, SettingKeys->Expert, &Settings->Expert, &settings->Expert, quiet); + ApplyBooleanSetting(settings, ActiveSet, SettingKeys->Active, + &Settings->Active, &settings->Active, quiet); + + ApplyBooleanSetting(settings, NoLogSet, SettingKeys->NoLog, + &Settings->NoLog, &settings->NoLog, quiet); + ApplyStringSetting(settings, DestinationAddressSet, SettingKeys->DestinationAddress, &Settings->DestinationAddress, settings->DestinationAddress, quiet); diff --git a/setting.h b/setting.h index 8fcef79..29011d8 100644 --- a/setting.h +++ b/setting.h @@ -30,16 +30,23 @@ #include "config.h" #include "locale.h" -#define PrioritySet 0x001 -#define TimeoutSet 0x004 -#define IntervalSet 0x008 -#define ReadonlySet 0x010 -#define ExpertSet 0x020 -#define DestinationAddressSet 0x040 -#define DestinationPortSet 0x080 -#define ThresholdSet 0x100 -#define PopKeySet 0x200 -#define PopUpSet 0x400 +#define PrioritySet 0x0001 +#define TimeoutSet 0x0002 +#define IntervalSet 0x0004 +#define ReadonlySet 0x0008 +#define ExpertSet 0x0010 +#define DestinationAddressSet 0x0020 +#define DestinationPortSet 0x0040 +#define ThresholdSet 0x0080 +#define PopKeySet 0x0100 +#define PopUpSet 0x0200 +#define ActiveSet 0x0400 +#define NoLogSet 0x0800 +#define TzSet 0x1000 +#define TimeZoneDisplaySet 0x2000 +#define TimeZoneNameSet 0x4000 +#define TimeZoneValueSet 0x8000 +#define TimeZoneDstSet 0x10000 #define DefaultSettingType 0x01 #define PrefsSettingType 0x02 @@ -59,8 +66,16 @@ struct AppSettings char *DestinationPort; char *PopKey; long Popup; + long Active; + long NoLog; + char *TZ; + long TimeZoneDisplay; + char *TimeZoneName; + long TimeZoneValue; + long TimeZoneDst; long Values; long Type; + long PopupOnStart; }; struct AppSettingKeys @@ -75,6 +90,14 @@ struct AppSettingKeys const char *Threshold; const char *PopKey; const char *Popup; + const char *Active; + const char *NoLog; + const char *TZ; + const char *TimeZoneDisplay; + const char *TimeZoneName; + const char *TimeZoneValue; + const char *TimeZoneDst; + const char *PopupOnStart; }; typedef void (*ParseSettingFunction)(struct AppSettings *, void *); diff --git a/sync.c b/sync.c index 21db076..0c611de 100644 --- a/sync.c +++ b/sync.c @@ -36,6 +36,7 @@ #include "timer.h" #include "conv.h" #include "sntp.h" +#include "text.h" #include "ptz.h" #include "mem.h" #include "net.h" @@ -54,7 +55,6 @@ struct AppCom struct sntp *Client; int TimerSigBit; bool Restart; - int RunCount; int PoolServerNumber; char *ServerName; char *ServerPort; @@ -63,7 +63,6 @@ struct AppCom static void SyncProc(void); static int SyncClock(void); static void ChooseServer(void); -static void CheckTimezone(void); static const char *SntpErrorText(enum sntp_err error); static struct AppCom Synchronizer; @@ -100,7 +99,7 @@ void StartSynchronizer(void) if (running) { - LogDebug("Synchronizer process is already running"); + LogDebug(Text2P, "Synchronizer", TextAlreadyRunning); } else if (task != NULL) { @@ -112,13 +111,6 @@ void StartSynchronizer(void) } } -static void ShowLocalTime(void) -{ - char *time = GetTimeText(AppLocale, LOCAL_TIME_NOW); - LogNotice("Local time is %s", time); - FreeMemSafe(time); -} - static int ComInit(void) { if (OpenSocketLibrary() != LIB_OK) @@ -164,26 +156,9 @@ static void SendSync(void) if (ComInit() == COM_OK) { if (SyncClock() == COM_OK) - { LogDebug("Sending next NTP request in %ld milliseconds", Settings->Interval); - } else - { LogDebug("Retry in %ld milliseconds", Settings->Interval); - } - - Synchronizer.RunCount++; - if (Synchronizer.RunCount % 10 == 0) - { - ShowLocalTime(); - } - - if (Synchronizer.RunCount % 25 == 0) - { - long blocks, size; - MemUsage(&blocks, &size, NULL); - LogInfo("Currently using %ld bytes in %ld blocks on heap", size, blocks); - } } ComDestroy(); @@ -250,7 +225,6 @@ static void SyncProc(void) Synchronizer.Client = NULL; Synchronizer.TimerSigBit = -1; Synchronizer.Restart = false; - Synchronizer.RunCount = 0; Synchronizer.PoolServerNumber = -1; Synchronizer.ServerName = NULL; Synchronizer.ServerPort = NULL; @@ -454,38 +428,10 @@ static int SyncClock(void) LastSync = rtv; SendWindowMessage(ATK_LASTSYNC); - CheckTimezone(); return COM_OK; } -static void CheckTimezone(void) -{ - struct timeval tv; - ULONG now; - - if (NextTransition == NULL) - { - return; - } - - GetSysTime(&tv); - now = (ULONG)(tv.tv_secs); - - if (now > NextTransition->time) - { - char Timezone[TIMEZONE_TEXT_LEN]; - GetTimezoneText(AppLocale, Timezone, OFFSET_IN_PARENS); - ShowLocalTime(); - LogNotice("Current time zone is %s", Timezone); - LogWarn("Changing time zone"); - // TODO: Make thread safe - ShowLocalTime(); - InitTimezoneShift(); - SendWindowMessage(ATK_TZ_CHANGED); - } -} - static const char *SntpErrorText(enum sntp_err error) { switch (error) diff --git a/text.c b/text.c index 9021f21..66040a0 100644 --- a/text.c +++ b/text.c @@ -27,8 +27,10 @@ #include "text.h" // Settings -const char *prefsFile = "ENV:timekeeper.prefs"; -const char *persistentPrefsFile = "ENVARC:timekeeper.prefs"; +const char *prefsDir = "ENV:AmiTimeKeeper"; +const char *prefsFile = "ENV:AmiTimeKeeper/timekeeper.prefs"; +const char *persistentPrefsDir = "ENVARC:AmiTimeKeeper"; +const char *persistentPrefsFile = "ENVARC:AmiTimeKeeper/timekeeper.prefs"; const char *prefsFileSearch = "Searching for preference in %s"; const char *prefsFileFound = "Found preference file"; const char *prefsFileNotFound = "Preference file not found"; @@ -52,6 +54,10 @@ const char *settingChangedLong = "%s changed: %ld -> %ld"; const char *settingChangedString = "%s changed: %s -> %s"; const char *settingSetLong = "%s is already set to %ld"; const char *settingSetString = "%s is already set to %s"; +const char *settingTooLow = "%s < %ld (too low)"; +const char *settingTooHigh = "%s > %ld (too high)"; +const char *settingGreaterThan = "%s * 2 > %s"; +const char *settingInvalid = "%s is invalid (%ld)"; const char *settingValueLong = "%s=%ld"; const char *settingValueString = "%s=%s"; const char *saveValueLong = "%s=%ld\n"; @@ -60,9 +66,43 @@ const char *noValueString = "NO"; const char *yesValueString = "YES"; const char *falseValueString = "TRUE"; const char *trueValueString = "FALSE"; +const char *validatingSettings = "Validating settings"; +const char *validatedSettings = "Settings validated"; + +// Time zone +const char *applyTimeZone = "Setting local time zone from %s %s"; +const char *tzSettingValid = "Found valid %s %s"; +const char *tzSettingInvalid = "Found unknown %s %s"; +const char *tzFromEnv = "environment variable"; +const char *tzFromSet = "setting value in"; +const char *tzWorkbench = "Workbench"; +const char *tzPreferences = "preferences"; +const char *tzChange = "Changing to %s on %s"; +const char *tzDstAbbr = "DST"; +const char *tzStdAbbr = "standard time"; +const char *timezoneCur = "Current time zone is %s"; +const char *timezoneStd = "Standard time zone is %s"; +const char *timezoneDst = "DST time zone is %s"; +const char *timezoneNoTrans = "There are no time zone transitions"; +const char *tzChecker = "Timezone Checker"; +const char *tzCheck = "Checking for time zone transition"; +const char *tzTrans = "Performing time zone transition"; + +// Concat text +const char *Text2P = "%s %s"; // Common text const char *TextEmpty = "Empty"; +const char *TextStarting = "Starting"; +const char *TextRestarting = "Restarting"; +const char *TextStarted = "started"; +const char *TextStopped = "stopped"; +const char *TextAlreadyRunning = "is already running"; +const char *TextNoStart = "Could not start"; +const char *TextClosing = "Closing"; +const char *TextOpened = "Opened"; +const char *TextNoOpen = "Cannot open"; +const char *TextFound = "Found"; // Error / debug messages const char *TextPortInitError = "%s message port failed to initialize"; @@ -78,3 +118,14 @@ const char *TextSyncAlreadyOn = "Synchronization is already active"; const char *TextSyncAlreadyOff = "Synchronization is already deactivated"; const char *TextNoResponses = "No responses from NTP server"; const char *TextNoClockAdjust = "Clock has not been adjusted"; +const char *TextARexxCmdUnknown = "Unknown ARexx command"; +const char *TextARexxParmUnknown = "Unknown parameter"; +const char *TextARexxParmInvalid = "Invalid parameter value"; +const char *TextARexxParmMiss = "Parameter is missing"; +const char *TextARexxVarError = "Could not set ARexx variable: %ld"; +const char *TextARexxVarValue = "ARexx variable %s = %s"; +const char *TextARexxResult = "ARexx result: %s"; +const char *TextARexxExecute1 = "Executing %s"; +const char *TextARexxExecute2 = "Executing %s %s"; +const char *TextARexxReceived = "Received %s"; +const char *TextARexxShutdown = "Requesting shutdown"; diff --git a/text.h b/text.h index 6ec786a..1bd9690 100644 --- a/text.h +++ b/text.h @@ -30,7 +30,9 @@ #include "config.h" // Settings +const char *prefsDir; const char *prefsFile; +const char *persistentPrefsDir; const char *persistentPrefsFile; const char *prefsFileSearch; const char *prefsFileFound; @@ -55,6 +57,10 @@ const char *settingChangedLong; const char *settingChangedString; const char *settingSetLong; const char *settingSetString; +const char *settingTooLow; +const char *settingTooHigh; +const char *settingGreaterThan; +const char *settingInvalid; const char *settingValueLong; const char *settingValueString; const char *saveValueLong; @@ -63,9 +69,45 @@ const char *noValueString; const char *yesValueString; const char *falseValueString; const char *trueValueString; +const char *validatingSettings; +const char *validatedSettings; + +// Library + +// Time zone +const char *applyTimeZone; +const char *tzSettingValid; +const char *tzSettingInvalid; +const char *tzFromEnv; +const char *tzFromSet; +const char *tzWorkbench; +const char *tzPreferences; +const char *tzChange; +const char *tzDstAbbr; +const char *tzStdAbbr; +const char *timezoneCur; +const char *timezoneStd; +const char *timezoneDst; +const char *timezoneNoTrans; +const char *tzChecker; +const char *tzCheck; +const char *tzTrans; + +// Concat text +const char *Text2P; // Common text const char *TextEmpty; +const char *TextStarting; +const char *TextRestarting; +const char *TextStarted; +const char *TextStopped; +const char *TextAlreadyRunning; +const char *TextNoStart; +const char *TextClosing; +const char *TextOpened; +const char *TextNoOpen; +const char *TextFound; // Error / debug messages const char *TextPortInitError; @@ -81,5 +123,16 @@ const char *TextSyncAlreadyOn; const char *TextSyncAlreadyOff; const char *TextNoResponses; const char *TextNoClockAdjust; +const char *TextARexxCmdUnknown; +const char *TextARexxParmUnknown; +const char *TextARexxParmInvalid; +const char *TextARexxParmMiss; +const char *TextARexxVarError; +const char *TextARexxVarValue; +const char *TextARexxResult; +const char *TextARexxExecute1; +const char *TextARexxExecute2; +const char *TextARexxReceived; +const char *TextARexxShutdown; #endif diff --git a/tz.c b/tz.c index 3372e17..412738d 100644 --- a/tz.c +++ b/tz.c @@ -28,206 +28,330 @@ #include "notify.h" #include "global.h" #include "message.h" +#include "setting.h" +#include "conv.h" +#include "text.h" #include "ptz.h" #include "timer.h" #include "mem.h" #include "tz.h" #include +#include #include "logmod.h" #define MODULENAME "Timezone" +#define EnvTZTimezone 1 +#define EnvTZOneTimezone 2 +#define SetTZEnvTimezone 3 +#define SetTZValTimezone 4 +#define PrefsTimezone 5 + +#define TIMEZONE_STD 1 +#define TIMEZONE_DST 2 + +#define ENV_TZ "TZ" +#define ENV_TZONE "TZONE" +#define ENV_TZ_FILE "ENV:TZ" +#define ENV_TZONE_FILE "ENV:TZONE" +#define ENV_PREFS_FILE "ENV:Sys/locale.prefs" + /* * 2922 is the number of days between 1.1.1970 and 1.1.1978 * (2 leap years and 6 normal) * 2922 * 24 * 60 * 60 = 252460800 */ #define AMIGA_OFFSET 252460800 -#define SECONDSPERDAY 86400 -static long unixEpochOffset = 0; +static volatile bool TimezoneCheckerRunning = false; +static volatile bool TimezoneCheckerRestart = false; +static volatile bool TimezoneCheckerShutdown = false; +static volatile long TimezoneSource = 0; +static volatile long unixEpochOffset = 0; -void LogTzInfo() +static void StartTimezoneCheck(void); + +static void BuildTimezone(struct PosixLocalTimezone *timezone, int offset, bool std) { - if (Timezone == NULL) - { - return; - } + long sign = (offset < 0 ? -1 : +1); + long offsetPositive = (offset < 0 ? -offset : offset); + long hoursOffset = (offsetPositive / 60L); + long minutsOffset = (offsetPositive % 60L); - if (Timezone->dst.abbreviation != NULL) + if (Settings->TimeZoneName != NULL && *Settings->TimeZoneName != '\0' && + ((Settings->TimeZoneDst <= 0 && std) || (Settings->TimeZoneDst == 1 && !std))) + SNPrintf(timezone->abbreviation, MAX_ABBR - 1, "%s", Settings->TimeZoneName); + else if (minutsOffset == 0) { - LogInfo("Standard time zone is %s (GMT%s%02ld:%02ld)", - Timezone->std.abbreviation, - PosixTimezoneSignChar(&Timezone->std), - (long)Timezone->std.offset.hours, - (long)Timezone->std.offset.minutes); - - LogInfo("DST time zone is %s (GMT%s%02ld:%02ld)", - Timezone->dst.abbreviation, - PosixTimezoneSignChar(&Timezone->dst), - (long)Timezone->dst.offset.hours, - (long)Timezone->dst.offset.minutes); + SNPrintf(timezone->abbreviation, MAX_ABBR - 1, "GMT%s%ld", + (sign <= 0 ? "+" : "-"), hoursOffset); } else { - LogInfo("time zone is %s (GMT%s%02ld:%02ld)", - Timezone->std.abbreviation, - PosixTimezoneSignChar(&Timezone->std), - (long)Timezone->std.offset.hours, - (long)Timezone->std.offset.minutes); + SNPrintf(timezone->abbreviation, MAX_ABBR - 1, "GMT%s%ld:%02ld", + (sign <= 0 ? "+" : "-"), hoursOffset, minutsOffset); + } + + timezone->offset.hours = hoursOffset; + timezone->offset.minutes = minutsOffset; + timezone->offset.seconds = 0; + timezone->offset.sign = -sign; +} + +static void FindCurrentTimezone(struct PosixTimezone *timezone, long opt) +{ + if (opt == TIMEZONE_STD) + { + StrCopy(timezone->current.abbreviation, timezone->std.abbreviation); + timezone->current.offset = timezone->std.offset; + } + else if (opt == TIMEZONE_DST) + { + StrCopy(timezone->current.abbreviation, timezone->dst.abbreviation); + timezone->current.offset = timezone->dst.offset; + } +} + +static void SetCurrentTimezone(struct PosixTimezone *timezone) +{ + if (Settings->TimeZoneDst == 0) + FindCurrentTimezone(timezone, TIMEZONE_STD); + else if (Settings->TimeZoneDst == 1) + FindCurrentTimezone(timezone, TIMEZONE_DST); + else if (timezone->source == PosixTypeTimezone && + timezone->dst.abbreviation == NULL) + FindCurrentTimezone(timezone, TIMEZONE_DST); + else + FindCurrentTimezone(timezone, TIMEZONE_STD); +} + +static struct PosixTimezone *BuildAmigaTimezone(struct Locale *locale) +{ + struct PosixTimezone *tz = AllocStructSafe(struct PosixTimezone); + tz->source = CustomTypeTimezone; + long offset = (locale != NULL ? locale->loc_GMTOffset : 0); + BuildTimezone(&tz->std, offset, true); + BuildTimezone(&tz->dst, offset - 60, false); + SetCurrentTimezone(tz); + return tz; +} + +static struct PosixTimezone *BuildSettingsTimezone(void) +{ + struct PosixTimezone *tz = AllocStructSafe(struct PosixTimezone); + tz->source = CustomTypeTimezone; + long value = Settings->TimeZoneValue; + long hours = (value != TZVALUE_DEF ? (value / -100) : 0); + long minutes = (value != TZVALUE_DEF ? (value % -100) : 0); + long offset = hours * 60 + minutes; + BuildTimezone(&tz->std, offset, true); + BuildTimezone(&tz->dst, offset - 60, false); + SetCurrentTimezone(tz); + return tz; +} + +static void ShowTimezones(void) +{ + char timezone[TIMEZONE_TEXT_LEN]; + GetTimezoneText(timezone, TZD_STD); + LogInfo(timezoneStd, timezone); + GetTimezoneText(timezone, TZD_DST); + LogInfo(timezoneDst, timezone); + GetTimezoneText(timezone, TZD_OFFSET_IN_PARENS); + LogNotice(timezoneCur, timezone); +} + +static char *GetVariable(const char *variable) +{ + unsigned long memsize = 256; + STRPTR var = (STRPTR)variable; + + STRPTR buf = (STRPTR)AllocStringSafe(memsize); + if (!buf) + { + return NULL; + } + + if (GetVar(var, buf, memsize - 1, GVF_GLOBAL_ONLY) > 0) + { + return (char *)buf; + } + + FreeMemSafe(buf); + return NULL; +} + +static struct PosixTimezone *ParseSettingsTZ() +{ + struct PosixTimezone *timezone = NULL; + if (Settings->TZ != NULL && *Settings->TZ != '\0') + { + timezone = InitPosixTimezone(Settings->TZ); + if (timezone != NULL) + LogInfo(tzSettingValid, tzFromSet, SettingKeys->TZ); + else + LogInfo(tzSettingInvalid, tzFromSet, SettingKeys->TZ); + LogDebug(settingValueString, SettingKeys->TZ, Settings->TZ); + } + return timezone; +} + +static struct PosixTimezone *ParseEnvTZ(const char *tz) +{ + struct PosixTimezone *timezone = NULL; + char *var = GetVariable(tz); + + if (var != NULL) + { + timezone = InitPosixTimezone(var); + if (timezone != NULL) + LogInfo(tzSettingValid, tzFromEnv, tz); + else + LogInfo(tzSettingInvalid, tzFromEnv, tz); + LogDebug(settingValueString, tz, var); + FreeMemSafe(var); + } + return timezone; +} + +static void ShowTimezoneSource(void) +{ + switch (TimezoneSource) + { + case SetTZValTimezone: + LogNotice(applyTimeZone, tzFromSet, SettingKeys->TimeZoneValue); + break; + case SetTZEnvTimezone: + LogNotice(applyTimeZone, tzFromSet, SettingKeys->TZ); + break; + case EnvTZTimezone: + LogNotice(applyTimeZone, tzFromEnv, ENV_TZ); + break; + case EnvTZOneTimezone: + LogNotice(applyTimeZone, tzFromEnv, ENV_TZONE); + break; + case PrefsTimezone: + LogNotice(applyTimeZone, tzWorkbench, tzPreferences); + break; + default: + break; + } +} + +static void ShowNextTransition(void) +{ + if (NextTransition != NULL) + { + char *nextTime = GetTimeText((ULONG)NextTransition->time); + const char *nextTrans = NextTransition->isdst ? tzDstAbbr : tzStdAbbr; + LogWarn(tzChange, nextTrans, nextTime); + FreeMemSafe(nextTime); } } void InitTimezone(void) { - char TimezoneText[TIMEZONE_TEXT_LEN]; - char *tz; - int result; + CleanupNotifications(); - tz = NULL; - result = InitPosixTimezone(&tz); - switch (result) + struct Locale *locale = OpenLocale(NULL); + struct PosixTimezone *set1Timezone = ParseSettingsTZ(); + struct PosixTimezone *set2Timezone = BuildSettingsTimezone(); + struct PosixTimezone *env1Timezone = ParseEnvTZ(ENV_TZ); + struct PosixTimezone *env2Timezone = ParseEnvTZ(ENV_TZONE); + struct PosixTimezone *prefTimezone = BuildAmigaTimezone(locale); + CloseLocale(locale); + + struct timeval tv; + GetSysTime(&tv); + SetPosixTimezone(set1Timezone, &tv); + SetPosixTimezone(env1Timezone, &tv); + SetPosixTimezone(env2Timezone, &tv); + + long source = 0; + struct PosixTimezone *timezone = NULL; + if (Settings->TimeZoneValue != TZVALUE_DEF) { - case 1: - LogInfo("No TZ or TZONE variable found"); - break; - case 2: - LogInfo("Found unknown TZONE variable"); - LogDebug("TZONE = %s", tz); - break; - case 3: - LogInfo("Found unknown TZ variable"); - LogDebug("TZ = %s", tz); - break; - case 4: - LogInfo("Found valid TZONE variable"); - LogNotice("Using local time zone from the TZONE variable"); - LogDebug("TZONE = %s", tz); - WatchFile("ENV:TZONE", ATK_TZONE_CHANGED); - break; - case 5: - LogInfo("Found valid TZ variable"); - LogNotice("Using local time zone from the TZ variable"); - LogDebug("TZ = %s", tz); - WatchFile("ENV:TZ", ATK_TZ_CHANGED); - break; - default: - LogWarn("Unknown TZ or TZONE code"); - break; + source = SetTZValTimezone; + timezone = set2Timezone; } - - if (tz != NULL) + else if (set1Timezone != NULL) { - FreeMemSafe(tz); + source = SetTZEnvTimezone; + timezone = set1Timezone; } - - if (result == 4 || result == 5) + else if (env1Timezone != NULL) { - struct timeval tv; - GetSysTime(&tv); - SetPosixTimezone(&tv); - LogTzInfo(); - unixEpochOffset = - (Timezone->current.offset.sign < 0 ? +1 : -1) * - (Timezone->current.offset.hours * 60 * 60 + - Timezone->current.offset.minutes * 60 + - Timezone->current.offset.seconds); - unixEpochOffset += AMIGA_OFFSET; - GetTimezoneText(AppLocale, TimezoneText, OFFSET_IN_PARENS); - LogNotice("Current time zone is %s", TimezoneText); + source = EnvTZTimezone; + timezone = env1Timezone; + } + else if (env2Timezone != NULL) + { + source = EnvTZOneTimezone; + timezone = env2Timezone; } else { - LogNotice("Using local time zone from Workbench preferences"); - WatchFile("ENV:Sys/locale.prefs", ATK_LOCALE_CHANGED); - unixEpochOffset = AppLocale->AmigaLocale->loc_GMTOffset * 60 + AMIGA_OFFSET; - GetTimezoneText(AppLocale, TimezoneText, OFFSET_IN_PARENS); - LogNotice("Current time zone is %s", TimezoneText); + source = PrefsTimezone; + timezone = prefTimezone; } - InitTimezoneShift(); + struct PosixTimezone *activeTimezone = AllocStructSafe(struct PosixTimezone); + CopyMem(timezone, activeTimezone, sizeof(struct PosixTimezone)); + FreePosixTimezone(set1Timezone); + FreePosixTimezone(set2Timezone); + FreePosixTimezone(env1Timezone); + FreePosixTimezone(env2Timezone); + FreePosixTimezone(prefTimezone); + + struct PosixTransitionTime *activeTransition = FindNextTransition(activeTimezone, &tv); + long offset = (activeTimezone->current.offset.sign < 0 ? +1 : -1) * + (activeTimezone->current.offset.hours * 60 * 60 + + activeTimezone->current.offset.minutes * 60 + + activeTimezone->current.offset.seconds); + offset += AMIGA_OFFSET; + + // Switch global time zone variables + Forbid(); + struct PosixTimezone *lastTimezone = Timezone; + Timezone = activeTimezone; + TimezoneSource = source; + NextTransition = activeTransition; + unixEpochOffset = offset; + Permit(); + + switch (source) + { + case EnvTZTimezone: + WatchFile(ENV_TZ_FILE, ATK_TZ_CHANGED); + break; + case EnvTZOneTimezone: + WatchFile(ENV_TZONE_FILE, ATK_TZONE_CHANGED); + break; + case PrefsTimezone: + WatchFile(ENV_PREFS_FILE, ATK_LOCALE_CHANGED); + break; + default: + break; + } + + ShowTimezoneSource(); + ShowTimezones(); + ShowNextTransition(); + StartTimezoneCheck(); + FreePosixTimezone(lastTimezone); } -void Amiga2DateStamp(time_t *time, struct DateStamp *date) +void Unix2Amiga(struct timeval *utc, struct timeval *tv) { - long t, d, m, q; + assert(utc != NULL); + assert(tv != NULL); - t = *time; - d = t / SECONDSPERDAY; - m = (t - SECONDSPERDAY * d) / 60; - q = (t - SECONDSPERDAY * d - m * 60) * 60; - - date->ds_Days = d; - date->ds_Minute = m; - date->ds_Tick = q; + tv->tv_secs = utc->tv_secs - AMIGA_OFFSET; + tv->tv_micro = utc->tv_micro; } -void InitTimezoneShift(void) -{ - struct PosixTransitionTime *transition; - struct timeval tv; - char *nextTime; - char *nextTrans; - time_t next; - ULONG now; - - if (Timezone == NULL || Timezone->start.type == 0) - { - NextTransition = NULL; - return; - } - - /* - { - int i; - for (i = 0; i < 138; i++) - { - transition = &Timezone->transitions[i]; - next = transition->time; - Amiga2DateStamp(&next, &ds); - DateTimeString(&ds, timeString, dateString, dayString); - LogWarn("XChanging (%ld) to %s time on %s %s at %s %s", - next, - transition->isdst ? "DST" : "standard", - dayString, dateString, timeString, - transition->isdst - ? Timezone->std.abbreviation - : Timezone->dst.abbreviation); - } - } - */ - - GetSysTime(&tv); - now = (ULONG)(tv.tv_secs); - transition = FindNextTransition(now); - - if (transition == NULL) - { - NextTransition = NULL; - return; - } - - if (NextTransition != NULL) - { - FreeMemSafe(NextTransition); - } - - NextTransition = transition; - next = transition->time; - - nextTime = GetTimeText(AppLocale, next); - nextTrans = transition->isdst ? "DST" : "standard time"; - LogWarn("Changing to %s on %s", nextTrans, nextTime); - FreeMemSafe(nextTime); -} - -void CleanupTimezone(void) -{ - CleanupPosixTimezone(); -} - -void Utc2Local(struct timeval *utc, struct timeval *tv) +void UnixUtc2AmigaLocal(struct timeval *utc, struct timeval *tv) { assert(utc != NULL); assert(tv != NULL); @@ -251,6 +375,11 @@ void GetLocalTimeOfDay(struct timeval *tv) GetSysTime(tv); } +long GetUtcOffsetValue(void) +{ + return (unixEpochOffset - AMIGA_OFFSET); +} + void SetTimeOfDay(const struct TimerInfo *info, const struct timeval *tv) { struct timeval t; @@ -269,39 +398,118 @@ void SaveTimeOfDay(const struct timeval *tv) WriteBattClock((long)tv->tv_secs - unixEpochOffset); } -void DateTimeString(struct DateStamp *ds, char *time, char *date, char *day) +static long GetNextTransition(void) { - struct DateTime dt; - - assert(ds != NULL); - assert(time != NULL); - assert(date != NULL); - assert(day != NULL); - - dt.dat_Stamp = *ds; - dt.dat_Format = FORMAT_DOS; - dt.dat_Flags = 0; - dt.dat_StrDay = (void *)day; - dt.dat_StrDate = (void *)date; - dt.dat_StrTime = (void *)time; - - DateToStr(&dt); + time_t next = (NextTransition != NULL ? NextTransition->time : 2147483647); + return (long)next; } -void Utc2Str(struct timeval *tv, char *buffer) +static long GetNextTransitionCheck(long now) { - char timeString[20]; - char dateString[20]; - char dayString[20]; - - struct DateStamp ds; - struct timeval last; - time_t time; - - Utc2Local(tv, &last); - time = last.tv_secs; - - Amiga2DateStamp(&time, &ds); - DateTimeString(&ds, timeString, dateString, dayString); - SNPrintf(buffer, 64, "%s %s %s", dayString, dateString, timeString); + long diff = GetNextTransition() - now; + diff = (diff > 60 ? 60 : diff); + return diff; +} + +static void CheckTransition(void) +{ + TimezoneCheckerShutdown = false; + TimezoneCheckerRestart = false; + + if (NextTransition == NULL) + { + LogNotice(timezoneNoTrans); + LogInfo(Text2P, tzChecker, TextStopped); + TimezoneCheckerRunning = false; + return; + } + + LogInfo(Text2P, tzChecker, TextStarted); + + struct timeval tv; + GetSysTime(&tv); + ULONG next, now = (ULONG)(tv.tv_secs); + long diff = GetNextTransitionCheck(now); + + while (!TimezoneCheckerShutdown) + { + if (diff < 0) + { + LogTrace(tzCheck); + GetSysTime(&tv); + now = (ULONG)(tv.tv_secs); + next = GetNextTransition(); + + if (now > next) + { + ShowTimezones(); + LogNotice(tzTrans); + + Forbid(); + SetPosixTimezone(Timezone, &tv); + NextTransition = FindNextTransition(Timezone, &tv); + Permit(); + + ShowTimezones(); + SendWindowMessage(ATK_TZ_CHANGED); + } + + diff = GetNextTransitionCheck(now); + } + + diff--; + Delay(50); + + if (TimezoneCheckerRestart) + { + LogInfo(Text2P, TextRestarting, tzChecker); + TimezoneCheckerRestart = false; + diff = -1; + } + } + + LogInfo(Text2P, tzChecker, TextStopped); + TimezoneCheckerRunning = false; + TimezoneCheckerShutdown = false; +} + +void StopTimezoneCheck(void) +{ + TimezoneCheckerShutdown = true; + while (TimezoneCheckerRunning) + Delay(10); +} + +static void StartTimezoneCheck(void) +{ + if (TimezoneCheckerRunning) + { + LogTrace(Text2P, tzChecker, TextAlreadyRunning); + TimezoneCheckerRestart = true; + return; + } + + TimezoneCheckerRunning = true; + LogNotice(Text2P, TextStarting, tzChecker); + + struct Task *task = (struct Task *)CreateNewProcTags( + NP_Entry, (IPTR)CheckTransition, + NP_StackSize, 32 * 1024, + NP_Name, 0, + NP_Input, 0, + NP_Output, 0, + NP_Error, 0, + NP_CloseInput, FALSE, + NP_CloseOutput, FALSE, + NP_CloseError, FALSE, + NP_WindowPtr, 0, + NP_ConsoleTask, 0, + NP_Cli, FALSE, + TAG_DONE); + + if (task == NULL) + { + LogError(Text2P, TextNoStart, tzChecker); + TimezoneCheckerRunning = false; + } } diff --git a/tz.h b/tz.h index a8ccef4..19318bf 100644 --- a/tz.h +++ b/tz.h @@ -31,14 +31,13 @@ #include "timer.h" void InitTimezone(void); -void InitTimezoneShift(void); -void CleanupTimezone(void); void GetTimeOfDay(struct timeval *); void SetTimeOfDay(const struct TimerInfo *, const struct timeval *); void SaveTimeOfDay(const struct timeval *); void GetLocalTimeOfDay(struct timeval *); -void Amiga2DateStamp(time_t *, struct DateStamp *); -void Utc2Local(struct timeval *, struct timeval *); -void Utc2Str(struct timeval *, char *); +long GetUtcOffsetValue(void); +void UnixUtc2AmigaLocal(struct timeval *, struct timeval *); +void Unix2Amiga(struct timeval *, struct timeval *); +void StopTimezoneCheck(void); #endif diff --git a/val.c b/val.c index b3d5997..7abff94 100644 --- a/val.c +++ b/val.c @@ -27,21 +27,18 @@ #include "config.h" #include "message.h" #include "setting.h" +#include "text.h" #include "conv.h" +#include "sync.h" #include "log.h" #include "mem.h" #include "val.h" #include "win.h" +#include "tz.h" #include "logmod.h" #define MODULENAME "Settings" -static const char *settingChangedLong = "%s changed: %ld -> %ld"; -static const char *settingChangedString = "%s changed: %s -> %s"; -static const char *settingTooLow = "%s < %ld (too low)"; -static const char *settingTooHigh = "%s > %ld (too high)"; -static const char *settingGreaterThan = "%s * 2 > %s"; - static void ValidateInterval(void) { if (Settings->Interval < INTERVAL_MIN) @@ -55,6 +52,17 @@ static void ValidateInterval(void) INTERVAL_MIN); Settings->Interval = INTERVAL_MIN; } + else if (Settings->Interval > INTERVAL_MAX) + { + LogInfo(settingTooHigh, + SettingKeys->Interval, + INTERVAL_MAX); + LogNotice(settingChangedLong, + SettingKeys->Interval, + Settings->Interval, + INTERVAL_MAX); + Settings->Interval = INTERVAL_MAX; + } } static void ValidateTimeout(void) @@ -126,17 +134,59 @@ static void ValidateThreshold(void) { if (Settings->Threshold < THRESHOLD_MIN) { - LogInfo(settingTooLow, - SettingKeys->Threshold, - THRESHOLD_MIN); - LogNotice(settingChangedLong, - SettingKeys->Threshold, - Settings->Threshold, - THRESHOLD_MIN); + LogInfo(settingTooLow, SettingKeys->Threshold, THRESHOLD_MIN); + LogNotice(settingChangedLong, SettingKeys->Threshold, + Settings->Threshold, THRESHOLD_MIN); Settings->Threshold = THRESHOLD_MIN; } } +static void ValidateTimeZoneDisplay(void) +{ + if (Settings->TimeZoneDisplay < TZD_MIN) + { + LogInfo(settingTooLow, SettingKeys->TimeZoneDisplay, TZD_MIN); + LogNotice(settingChangedLong, SettingKeys->TimeZoneDisplay, + Settings->TimeZoneDisplay, TZD_MIN); + Settings->TimeZoneDisplay = TZD_MIN; + } + else if (Settings->TimeZoneDisplay > TZD_MAX) + { + LogInfo(settingTooHigh, SettingKeys->TimeZoneDisplay, TZD_MAX); + LogNotice(settingChangedLong, SettingKeys->TimeZoneDisplay, + Settings->TimeZoneDisplay, TZD_MAX); + Settings->TimeZoneDisplay = TZD_MAX; + } +} + +static void ValidateTimeZoneValue(void) +{ + if (Settings->TimeZoneValue < TZVALUE_MIN) + { + LogInfo(settingTooLow, SettingKeys->TimeZoneValue, TZVALUE_MIN); + LogNotice(settingChangedLong, SettingKeys->TimeZoneValue, + Settings->TimeZoneValue, TZVALUE_MIN); + Settings->TimeZoneValue = TZVALUE_MIN; + } + else if (Settings->TimeZoneValue != TZVALUE_DEF && + Settings->TimeZoneValue > TZVALUE_MAX) + { + LogInfo(settingTooHigh, SettingKeys->TimeZoneValue, TZVALUE_MAX); + LogNotice(settingChangedLong, SettingKeys->TimeZoneValue, + Settings->TimeZoneValue, TZVALUE_MAX); + Settings->TimeZoneValue = TZVALUE_MAX; + } + + if (Settings->TimeZoneValue % 60 >= 60) + { + long oldValue = Settings->TimeZoneValue; + Settings->TimeZoneValue = (Settings->TimeZoneValue / 60) * 60; + LogInfo(settingInvalid, SettingKeys->TimeZoneValue, oldValue); + LogNotice(settingChangedLong, SettingKeys->TimeZoneValue, + oldValue, Settings->TimeZoneValue); + } +} + static bool ValidateServer(char *name) { return true; @@ -149,12 +199,14 @@ static bool ValidatePort(char *name) void SanitizeSettings(void) { - LogTrace("Validating settings"); + LogDebug(validatingSettings); ValidateInterval(); ValidateTimeout(); ValidatePriority(); ValidateThreshold(); - LogTrace("Settings validated"); + ValidateTimeZoneDisplay(); + ValidateTimeZoneValue(); + LogTrace(validatedSettings); } void SetInterval(long value, long opt) @@ -323,7 +375,8 @@ void SetServer(char *valueString, long opt) valueString); FreeMemSafe(oldValue); - if ((opt & APPLY_RUNTIME) == APPLY_RUNTIME) + bool active = SynchronizerRunning; + if ((opt & APPLY_RUNTIME) == APPLY_RUNTIME && active) { Deactivate(); } @@ -359,8 +412,10 @@ void SetServer(char *valueString, long opt) WriteUiServer(); } - if ((opt & APPLY_RUNTIME) == APPLY_RUNTIME) + if ((opt & APPLY_RUNTIME) == APPLY_RUNTIME && active) { + while (SynchronizerRunning) + Delay(10); Activate(); } } @@ -386,7 +441,8 @@ void SetPort(char *valueString, long opt) valueString); FreeMemSafe(oldValue); - if ((opt & APPLY_RUNTIME) == APPLY_RUNTIME) + bool active = SynchronizerRunning; + if ((opt & APPLY_RUNTIME) == APPLY_RUNTIME && active) { Deactivate(); } @@ -422,8 +478,92 @@ void SetPort(char *valueString, long opt) WriteUiPort(); } - if ((opt & APPLY_RUNTIME) == APPLY_RUNTIME) + if ((opt & APPLY_RUNTIME) == APPLY_RUNTIME && active) { + while (SynchronizerRunning) + Delay(10); Activate(); } } + +void SetTimeZoneSetting(const char *settingKey, char **setting, char *valueString, long opt) +{ + char *oldSettingValue, *newSettingValue; + char *oldValue; + + oldValue = StrDupSafe(*setting); + if (Stricmp((STRPTR)oldValue, (STRPTR)valueString) == 0) + { + FreeMemSafe(oldValue); + return; + } + + LogNotice(settingChangedString, + settingKey, + oldValue, + valueString); + FreeMemSafe(oldValue); + + bool active = SynchronizerRunning; + if ((opt & APPLY_RUNTIME) == APPLY_RUNTIME && active) + { + Deactivate(); + } + + newSettingValue = StrDupSafe(valueString); + Forbid(); + oldSettingValue = *setting; + *setting = newSettingValue; + Permit(); + FreeMemSafe(oldSettingValue); + + if ((opt & APPLY_RUNTIME) == APPLY_RUNTIME && settingKey == SettingKeys->TZ) + { + InitTimezone(); + } + + if ((opt & APPLY_UI_FRONT) == APPLY_UI_FRONT) + { + SendWindowMessage(ATK_TZ_CHANGED); + } + + if ((opt & APPLY_RUNTIME) == APPLY_RUNTIME && active) + { + while (SynchronizerRunning) + Delay(10); + Activate(); + } +} + +void SetTimeZoneSettingLong(const char *settingKey, long *setting, char *valueString, long opt) +{ + LONG value, oldValue = *setting; + int p = StrToLong((STRPTR)valueString, &value); + + if (p != -1 && oldValue != value) + { + LogNotice(settingChangedLong, + settingKey, + oldValue, + value); + *setting = value; + } + + if ((opt & APPLY_RUNTIME) == APPLY_RUNTIME) + { + if (settingKey == SettingKeys->TimeZoneValue || settingKey == SettingKeys->TimeZoneDst) + { + InitTimezone(); + } + } + + if ((opt & APPLY_UI_FRONT) == APPLY_UI_FRONT) + { + SendWindowMessage(ATK_TZ_CHANGED); + } + + if ((opt & APPLY_RUNTIME) == APPLY_RUNTIME) + { + SendBrokerMessage(ATK_RESTART); + } +} \ No newline at end of file diff --git a/val.h b/val.h index 722c1b7..0b1cf92 100644 --- a/val.h +++ b/val.h @@ -46,5 +46,7 @@ void SetInterval(long, long); void SetTimeout(long, long); void SetThreshold(char *, long); void SetPriority(char *, long); +void SetTimeZoneSetting(const char *, char **, char *, long); +void SetTimeZoneSettingLong(const char *, long *, char *, long); #endif diff --git a/win_gad.c b/win_gad.c index 1ff472e..3d9b752 100644 --- a/win_gad.c +++ b/win_gad.c @@ -583,7 +583,7 @@ void ShowLastSync(struct timeval *tv) } GetLocalTimeOfDay(&nowLocal); - Utc2Local(tv, &lastLocal); + UnixUtc2AmigaLocal(tv, &lastLocal); seconds = nowLocal.tv_secs - lastLocal.tv_secs; if (seconds > 60 * 60 * 24) @@ -615,7 +615,7 @@ void ShowNewTimezone(void) char *temp = SettingWindow.TimezoneText; char *new = AllocStringSafe(TIMEZONE_TEXT_LEN); - GetTimezoneText(AppLocale, new, ZONE_IN_PARENS); + GetTimezoneText(new, TZD_ZONE_IN_PARENS); GT_SetGadgetAttrs( SettingWindow.Gadgets->TimezoneGadget, SettingWindow.Window, NULL, diff --git a/win_main.c b/win_main.c index 81893b8..4b358c1 100644 --- a/win_main.c +++ b/win_main.c @@ -52,10 +52,12 @@ volatile bool WindowProcRunning = false; struct AppSettings *WindowSettings = NULL; struct AppSettingWindow SettingWindow; +static APTR OrgWindowPtr = NULL; + void ShowSettingWindow(void) { bool running; - struct Task *task; + struct Task *task = NULL; if (GadToolsBase == NULL) { @@ -84,7 +86,7 @@ void ShowSettingWindow(void) if (running) { - LogDebug("Setting process is already running"); + LogDebug(Text2P, "Setting process", TextAlreadyRunning); SendWindowMessage(ATK_SHOW); } else if (task != NULL) @@ -143,7 +145,7 @@ static bool InitWindow(void) LongToStr(Settings->Priority, SettingWindow.PriorityText); LongLongToStr(Settings->Threshold, SettingWindow.ThresholdText); - GetTimezoneText(AppLocale, SettingWindow.TimezoneText, ZONE_IN_PARENS); + GetTimezoneText(SettingWindow.TimezoneText, Settings->TimeZoneDisplay); return true; } @@ -159,6 +161,11 @@ static void DestroyWindow(void) while ((msg = GetMsg(SettingWindow.Window->UserPort))) ReplyMsg(msg); + Forbid(); + struct Process *proc = (struct Process *)FindTask(NULL); + proc->pr_WindowPtr = OrgWindowPtr; + Permit(); + CloseWindow(SettingWindow.Window); SettingWindow.Window = NULL; } @@ -215,6 +222,12 @@ static bool CreateWindow(void) return false; } + Forbid(); + struct Process *proc = (struct Process *)FindTask(NULL); + OrgWindowPtr = proc->pr_WindowPtr; + proc->pr_WindowPtr = x; + Permit(); + return true; }