diff --git a/crm/admin/Makefile.am b/crm/admin/Makefile.am index 162eee5f09..72b41c00d3 100644 --- a/crm/admin/Makefile.am +++ b/crm/admin/Makefile.am @@ -1,93 +1,100 @@ # # Copyright (C) 2004 Andrew Beekhof # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \ -I$(top_builddir) -I$(top_srcdir) hadir = $(sysconfdir)/ha.d halibdir = $(libdir)/@HB_PKG@ commmoddir = $(halibdir)/modules/comm havarlibdir = $(localstatedir)/lib/@HB_PKG@ XML_FLAGS = `xml2-config --cflags` XML_LIBS = `xml2-config --libs` # sockets with path crmdir = $(havarlibdir)/crm apigid = @HA_APIGID@ crmuid = @HA_CCMUID@ COMMONLIBS = $(CRM_DEBUG_LIBS) \ $(top_builddir)/lib/clplumbing/libplumb.la \ $(top_builddir)/lib/crm/common/libcrmcommon.la \ $(top_builddir)/lib/crm/cib/libcib.la \ $(top_builddir)/lib/apphb/libapphb.la \ $(top_builddir)/lib/hbclient/libhbclient.la \ $(GLIBLIB) \ $(CURSESLIBS) \ $(LIBRT) LIBRT = @LIBRT@ AM_CFLAGS = @CFLAGS@ \ $(CRM_DEBUG_FLAGS) ## libraries lib_LTLIBRARIES = ## binary progs -halib_PROGRAMS = crmadmin cibadmin ccm_tool crm_diff crm_mon +halib_PROGRAMS = crmadmin cibadmin ccm_tool crm_diff crm_mon iso8601 ## SOURCES #noinst_HEADERS = config.h control.h crmd.h noinst_HEADERS = crmadmin_SOURCES = crmadmin.c crmadmin_CFLAGS = $(XML_FLAGS) -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' crmadmin_LDFLAGS = $(XML_LIBS) crmadmin_LDADD = $(COMMONLIBS) cibadmin_SOURCES = cibadmin.c cibadmin_CFLAGS = $(XML_FLAGS) -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' cibadmin_LDFLAGS = $(XML_LIBS) cibadmin_LDADD = $(COMMONLIBS) ccm_tool_SOURCES = ccm_epoche.c ccm_tool_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' ccm_tool_LDADD = $(COMMONLIBS) \ $(top_builddir)/membership/ccm/libccmclient.la crm_diff_SOURCES = xml_diff.c crm_diff_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' crm_diff_LDADD = $(COMMONLIBS) crm_mon_SOURCES = crm_mon.c crm_mon_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' crm_mon_LDADD = $(COMMONLIBS) \ $(top_builddir)/crm/pengine/libpengine.la + +iso8601_SOURCES = test.iso8601.c +iso8601_CFLAGS = $(XML_FLAGS) -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' +iso8601_LDADD = $(GLIBLIB) \ + $(top_builddir)/lib/clplumbing/libplumb.la \ + $(top_builddir)/lib/crm/common/libcrmcommon.la + clean-generic: rm -f *.log *.debug *.xml *~ install-exec-local: uninstall-local: diff --git a/crm/admin/test.iso8601.c b/crm/admin/test.iso8601.c new file mode 100644 index 0000000000..e86781b2a9 --- /dev/null +++ b/crm/admin/test.iso8601.c @@ -0,0 +1,93 @@ +/* $Id: test.iso8601.c,v 1.1 2005/08/02 16:14:39 andrew Exp $ */ +/* + * Copyright (C) 2005 Andrew Beekhof + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#define OPTARGS "V?d:p:D:WOL" + +char command = 0; + +int +main(int argc, char **argv) +{ + int argerr = 0; + int flag; + int print_options = 0; + char *input_s = NULL; + char *mutable_s = NULL; + + crm_log_init("iso8601"); + cl_log_set_facility(LOG_USER); + + if(argc < 2) { + argerr++; + } + + while (1) { + flag = getopt(argc, argv, OPTARGS); + if (flag == -1) + break; + + switch(flag) { + case 'V': + cl_log_enable_stderr(TRUE); + alter_debug(DEBUG_INC); + break; + case '?': + break; + case 'd': + case 'p': + case 'D': + command = flag; + input_s = crm_strdup(optarg); + break; + case 'W': + print_options |= ha_date_weeks; + break; + case 'O': + print_options |= ha_date_ordinal; + break; + case 'L': + print_options |= ha_log_local; + break; + } + } + + CRM_ASSERT(input_s != NULL); + mutable_s = input_s; + + if(command == 'd') { + ha_time_t *date_time = parse_date(&mutable_s); + CRM_ASSERT(date_time != NULL); + log_date(LOG_INFO, "parsed", date_time, + print_options|ha_log_date|ha_log_time); + + } else if(command == 'p') { + ha_time_period_t *interval = parse_time_period(&mutable_s); + CRM_ASSERT(interval != NULL); + log_time_period(LOG_INFO, interval, + print_options|ha_log_date|ha_log_time); + + } else if(command == 'D') { + ha_time_t *duration = parse_time_duration(&mutable_s); + CRM_ASSERT(duration != NULL); + + } + return 0; +} diff --git a/include/crm/common/iso8601.h b/include/crm/common/iso8601.h new file mode 100644 index 0000000000..5210ea9a73 --- /dev/null +++ b/include/crm/common/iso8601.h @@ -0,0 +1,154 @@ +/* $Id: iso8601.h,v 1.1 2005/08/02 16:14:39 andrew Exp $ */ +/* + * Copyright (C) 2005 Andrew Beekhof + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * http://en.wikipedia.org/wiki/ISO_8601 + * + */ + +#ifndef CRM_COMMON_ISO8601 +#define CRM_COMMON_ISO8601 + +#include +#include +#include + +typedef struct ha_has_time_s { + gboolean years; + + gboolean months; + gboolean days; + + gboolean weeks; + gboolean weekdays; + gboolean weekyears; + + gboolean yeardays; + + gboolean hours; + gboolean minutes; + gboolean seconds; +} ha_has_time_t; + +typedef struct ha_time_s +{ + int years; + + int months; + int days; + + int weeks; + int weekdays; + int weekyears; + + int yeardays; + + int hours; + int minutes; + int seconds; + + struct ha_time_s *offset; + struct ha_time_s *normalized; + struct ha_has_time_s *has; +} ha_time_t; + +enum date_fields { + date_month, + date_day +}; + +typedef struct ha_time_period_s +{ + ha_time_t *start; + ha_time_t *end; + ha_time_t *diff; +} ha_time_period_t; + + +#define ha_log_date 0x01 +#define ha_log_time 0x02 +#define ha_log_local 0x04 + +#define ha_date_ordinal 0x10 +#define ha_date_weeks 0x20 + +extern int str_lookup(const char *str, enum date_fields); + +extern void log_date(int log_level, const char *prefix, ha_time_t *dt, int flags); +extern void log_time_period(int log_level, ha_time_period_t *dtp, int flags); + +extern ha_time_t *parse_time (char **time_str, ha_time_t *atime, gboolean with_offset); +extern ha_time_t *parse_time_offset (char **offset_str); +extern ha_time_t *parse_date (char **date_str); +extern ha_time_t *parse_time_duration(char **duration_str); +extern ha_time_period_t *parse_time_period (char **period_str); +/* ha_time_interval_t *parse_time_interval(char **interval_str); */ + +int compare_date(ha_time_t *lhs, ha_time_t *rhs); + +extern gboolean parse_int(char **str, int field_width, int uppper_bound, int *result); +extern gboolean check_for_ordinal(const char *str); + +extern void ha_set_time(ha_time_t *lhs, ha_time_t *rhs, gboolean offset); +extern void ha_set_tm_time(ha_time_t *lhs, struct tm *rhs); +extern void ha_set_timet_time(ha_time_t *lhs, time_t *rhs); +extern ha_time_t *add_time(ha_time_t *lhs, ha_time_t *rhs); +extern ha_time_t *subtract_time(ha_time_t *lhs, ha_time_t *rhs); +extern void reset_tm(struct tm *some_tm); +extern void add_seconds(ha_time_t *a_time, int extra); +extern void add_minutes(ha_time_t *a_time, int extra); +extern void add_hours(ha_time_t *a_time, int extra); +extern void add_days(ha_time_t *a_time, int extra); +extern void add_weekdays(ha_time_t *a_time, int extra); +extern void add_yeardays(ha_time_t *a_time, int extra); +extern void add_weeks(ha_time_t *a_time, int extra); +extern void add_months(ha_time_t *a_time, int extra); +extern void add_years(ha_time_t *a_time, int extra); +extern void add_ordinalyears(ha_time_t *a_time, int extra); +extern void add_weekyears(ha_time_t *a_time, int extra); +extern void sub_seconds(ha_time_t *a_time, int extra); +extern void sub_minutes(ha_time_t *a_time, int extra); +extern void sub_hours(ha_time_t *a_time, int extra); +extern void sub_days(ha_time_t *a_time, int extra); +extern void sub_weekdays(ha_time_t *a_time, int extra); +extern void sub_yeardays(ha_time_t *a_time, int extra); +extern void sub_weeks(ha_time_t *a_time, int extra); +extern void sub_months(ha_time_t *a_time, int extra); +extern void sub_years(ha_time_t *a_time, int extra); +extern void sub_ordinalyears(ha_time_t *a_time, int extra); +extern void sub_weekyears(ha_time_t *a_time, int extra); + +/* conversion functions */ +extern int january1(int year); + +extern gboolean convert_from_weekdays(ha_time_t *a_date); +extern gboolean convert_from_ordinal(ha_time_t *a_date); +extern gboolean convert_from_gregorian(ha_time_t *a_date); + +extern gboolean is_leap_year(int year); + +extern int weeks_in_year(int year); +extern int days_per_month(int month, int year); + +gboolean is_date_sane(ha_time_t *a_date); + + +void reset_time(ha_time_t *a_time); + +#endif diff --git a/include/crm/crm.h b/include/crm/crm.h index a620bffe48..bb36dac99e 100644 --- a/include/crm/crm.h +++ b/include/crm/crm.h @@ -1,293 +1,295 @@ -/* $Id: crm.h,v 1.68 2005/07/27 19:10:14 andrew Exp $ */ +/* $Id: crm.h,v 1.69 2005/08/02 16:14:39 andrew Exp $ */ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CRM__H #define CRM__H #include #include #include #include #include #include #include #ifdef MCHECK #include #endif #include #ifndef CRM_DEV_BUILD # define CRM_DEV_BUILD 0 #endif #define ipc_call_diff_max_ms 5000 #define action_diff_warn_ms 5000 #define action_diff_max_ms 20000 #define fsa_diff_warn_ms 10000 #define fsa_diff_max_ms 30000 #include #define CRM_ASSERT(expr) if((expr) == FALSE) { \ do_crm_log(LOG_CRIT, __FILE__, __PRETTY_FUNCTION__, \ "Triggered dev assert at %s:%d : %s", \ __FILE__, __LINE__, #expr); \ abort(); \ } extern gboolean crm_assert_failed; #define CRM_DEV_ASSERT(expr) crm_assert_failed = FALSE; \ if((expr) == FALSE) { \ crm_assert_failed = TRUE; \ do_crm_log(CRM_DEV_BUILD?LOG_CRIT:LOG_ERR, \ __FILE__, __PRETTY_FUNCTION__, \ "Triggered dev assert at %s:%d : %s", \ __FILE__, __LINE__, #expr); \ if(CRM_DEV_BUILD) { \ abort(); \ } \ } /* Clean these up at some point, some probably should be runtime options */ #define WORKING_DIR HA_VARLIBDIR"/heartbeat/crm" #define BIN_DIR HA_LIBDIR"/heartbeat" #define SOCKET_LEN 1024 #define APPNAME_LEN 256 #define MAX_IPC_FAIL 5 #define CIB_FILENAME WORKING_DIR"/cib.xml" #define CIB_BACKUP WORKING_DIR"/cib_backup.xml" #define CRM_VERSION "1.0" #define MSG_LOG 1 #define DOT_FSA_ACTIONS 1 #define DOT_ALL_FSA_INPUTS 1 /* #define FSA_TRACE 1 */ #define INFINITY_S "INFINITY" #define MINUS_INFINITY_S "-INFINITY" #define INFINITY 1000000.0 /* Sub-systems */ #define CRM_SYSTEM_DC "dc" #define CRM_SYSTEM_DCIB "dcib" /* The master CIB */ #define CRM_SYSTEM_CIB "cib" #define CRM_SYSTEM_CRMD "crmd" #define CRM_SYSTEM_LRMD "lrmd" #define CRM_SYSTEM_PENGINE "pengine" #define CRM_SYSTEM_TENGINE "tengine" /* Valid operations */ #define CRM_OP_NOOP "noop" #define CRM_OP_JOIN_ANNOUNCE "join_announce" #define CRM_OP_JOIN_OFFER "join_offer" #define CRM_OP_JOIN_REQUEST "join_request" #define CRM_OP_JOIN_ACKNAK "join_ack_nack" #define CRM_OP_JOIN_CONFIRM "join_confirm" #define CRM_OP_DIE "die_no_respawn" #define CRM_OP_RETRIVE_CIB "retrieve_cib" #define CRM_OP_PING "ping" #define CRM_OP_VOTE "vote" #define CRM_OP_HELLO "hello" #define CRM_OP_HBEAT "dc_beat" #define CRM_OP_PECALC "pe_calc" #define CRM_OP_ABORT "abort" #define CRM_OP_QUIT "quit" #define CRM_OP_LOCAL_SHUTDOWN "start_shutdown" #define CRM_OP_SHUTDOWN_REQ "req_shutdown" #define CRM_OP_SHUTDOWN "do_shutdown" #define CRM_OP_FENCE "stonith" #define CRM_OP_EVENTCC "event_cc" #define CRM_OP_TEABORT "te_abort" #define CRM_OP_TEABORTED "te_abort_confirmed" /* we asked */ #define CRM_OP_TE_HALT "te_halt" #define CRM_OP_TECOMPLETE "te_complete" #define CRM_OP_TETIMEOUT "te_timeout" #define CRM_OP_TRANSITION "transition" #define CRM_OP_REGISTER "register" #define CRM_OP_DEBUG_UP "debug_inc" #define CRM_OP_DEBUG_DOWN "debug_dec" #define CRMD_STATE_ACTIVE "member" #define CRMD_STATE_INACTIVE "down" #define CRMD_JOINSTATE_DOWN "down" #define CRMD_JOINSTATE_PENDING "pending" #define CRMD_JOINSTATE_MEMBER "member" #define CRMD_ACTION_START "start" #define CRMD_ACTION_STARTED "running" #define CRMD_ACTION_START_FAIL "start_failed" #define CRMD_ACTION_START_PENDING "starting" #define CRMD_ACTION_STOP "stop" #define CRMD_ACTION_STOPPED "stopped" #define CRMD_ACTION_STOP_FAIL "stop_failed" #define CRMD_ACTION_STOP_PENDING "stopping" #define CRMD_ACTION_MON "monitor" #define CRMD_ACTION_MON_PENDING CRMD_ACTION_STARTED #define CRMD_ACTION_MON_OK CRMD_ACTION_STARTED #define CRMD_ACTION_MON_FAIL "monitor_failed" /* #define CRMD_ACTION_GENERIC "pending" */ #define CRMD_ACTION_GENERIC_PENDING "pending" #define CRMD_ACTION_GENERIC_OK "complete" #define CRMD_ACTION_GENERIC_FAIL "pending_failed" typedef GList* GListPtr; #define crm_atoi(text, default) atoi(text?text:default) extern gboolean safe_str_eq(const char *a, const char *b); extern gboolean safe_str_neq(const char *a, const char *b); #define slist_iter(child, child_type, parent, counter, a) \ { \ GListPtr __crm_iter_head = parent; \ child_type *child = NULL; \ int counter = 0; \ for(; __crm_iter_head != NULL; counter++) { \ child = __crm_iter_head->data; \ __crm_iter_head = __crm_iter_head->next; \ { a; } \ } \ } #define LOG_DEBUG_2 LOG_DEBUG+1 #define LOG_DEBUG_3 LOG_DEBUG+2 #define LOG_DEBUG_4 LOG_DEBUG+3 #define LOG_DEBUG_5 LOG_DEBUG+4 #define LOG_DEBUG_6 LOG_DEBUG+5 #define LOG_MSG LOG_DEBUG_3 #define crm_crit(w...) do_crm_log(LOG_CRIT, __FILE__, __FUNCTION__, w) #define crm_err(w...) do_crm_log(LOG_ERR, __FILE__, __FUNCTION__, w) #define crm_warn(w...) do_crm_log(LOG_WARNING, __FILE__, __FUNCTION__, w) #define crm_notice(w...) do_crm_log(LOG_NOTICE, __FILE__, __FUNCTION__, w) #define crm_info(w...) do_crm_log(LOG_INFO, __FILE__, __FUNCTION__, w) #define crm_log_maybe(level, fmt...) if(crm_log_level >= level) { \ do_crm_log(level, __FILE__, __FUNCTION__, fmt); \ } #define crm_debug(fmt...) crm_log_maybe(LOG_DEBUG, fmt) #define crm_debug_2(fmt...) crm_log_maybe(LOG_DEBUG_2, fmt) /* If this is not a developmental build, give the compiler every chance to * optimize these away */ #if CRM_DEV_BUILD # define crm_debug_3(fmt...) crm_log_maybe(LOG_DEBUG_3, fmt) # define crm_debug_4(fmt...) crm_log_maybe(LOG_DEBUG_4, fmt) # define crm_debug_5(fmt...) crm_log_maybe(LOG_DEBUG_5, fmt) +# define crm_debug_6(fmt...) crm_log_maybe(LOG_DEBUG_6, fmt) #else # define crm_debug_3(w...) if(0) { do_crm_log(LOG_DEBUG, NULL, NULL, w); } # define crm_debug_4(w...) if(0) { do_crm_log(LOG_DEBUG, NULL, NULL, w); } # define crm_debug_5(w...) if(0) { do_crm_log(LOG_DEBUG, NULL, NULL, w); } +# define crm_debug_6(w...) if(0) { do_crm_log(LOG_DEBUG, NULL, NULL, w); } #endif extern void crm_log_message_adv( int level, const char *alt_debugfile, const HA_Message *msg); #define crm_log_message(level, msg) if(crm_log_level >= level) { \ crm_log_message_adv(level, NULL, msg); \ } #define crm_do_action(level, actions) if(crm_log_level >= level) { \ actions; \ } #define crm_action_info(x) crm_do_action(LOG_INFO, x) #define crm_action_debug(x) crm_do_action(LOG_DEBUG, x) #define crm_action_debug_2(x) crm_do_action(LOG_DEBUG_2, x) #define crm_action_debug_3(x) crm_do_action(LOG_DEBUG_3, x) #define crm_action_debug_4(x) crm_do_action(LOG_DEBUG_4, x) #define crm_log_xml(level, text, xml) if(crm_log_level >= level) { \ print_xml_formatted(level, __FUNCTION__, xml, text); \ } #define crm_log_xml_crit(xml, text) crm_log_xml(LOG_CRIT, text, xml) #define crm_log_xml_err(xml, text) crm_log_xml(LOG_ERR, text, xml) #define crm_log_xml_warn(xml, text) crm_log_xml(LOG_WARNING, text, xml) #define crm_log_xml_notice(xml, text) crm_log_xml(LOG_NOTICE, text, xml) #define crm_log_xml_info(xml, text) crm_log_xml(LOG_INFO, text, xml) #define crm_log_xml_debug(xml, text) crm_log_xml(LOG_DEBUG, text, xml) #define crm_log_xml_debug_2(xml, text) crm_log_xml(LOG_DEBUG_2, text, xml) #define crm_log_xml_debug_3(xml, text) crm_log_xml(LOG_DEBUG_3, text, xml) #define crm_log_xml_debug_4(xml, text) crm_log_xml(LOG_DEBUG_4, text, xml) #define crm_log_xml_debug_5(xml, text) crm_log_xml(LOG_DEBUG_5, text, xml) #define crm_str(x) (const char*)(x?x:"") #if CRM_USE_MALLOC # define crm_malloc0(new_obj,length) \ { \ new_obj = malloc(length); \ if(new_obj == NULL) { \ crm_crit("Out of memory... exiting"); \ exit(1); \ } else { \ memset(new_obj, 0, length); \ } \ } # define crm_free(x) if(x) { free(x); x=NULL; } # define crm_is_allocated(obj) obj?TRUE:FALSE #else # if CRM_DEV_BUILD # define crm_malloc0(new_obj,length) \ { \ if(new_obj) { \ crm_err("Potential memory leak:" \ " %s at %s:%d not NULL before alloc.", \ #new_obj, __FILE__, __LINE__); \ abort(); \ } \ new_obj = cl_malloc(length); \ if(new_obj == NULL) { \ crm_crit("Out of memory... exiting"); \ abort(); \ } \ memset(new_obj, 0, length); \ } #else # define crm_malloc0(new_obj,length) \ { \ new_obj = cl_malloc(length); \ if(new_obj == NULL) { \ crm_crit("Out of memory... exiting"); \ abort(); \ } \ memset(new_obj, 0, length); \ } # endif # define crm_free(x) if(x) { \ CRM_ASSERT(cl_is_allocated(x) == 1); \ cl_free(x); \ x=NULL; \ } # define crm_is_allocated(obj) cl_is_allocated(obj) #endif #define crm_msg_del(msg) if(msg != NULL) { ha_msg_del(msg); msg = NULL; } #endif diff --git a/lib/crm/common/Makefile.am b/lib/crm/common/Makefile.am index 214068e6d2..a4890a79d8 100644 --- a/lib/crm/common/Makefile.am +++ b/lib/crm/common/Makefile.am @@ -1,50 +1,50 @@ # # Copyright (C) 2004 Andrew Beekhof # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \ -I$(top_builddir) -I$(top_srcdir) AM_CFLAGS = @CFLAGS@ $(CRM_DEBUG_FLAGS) hadir = $(sysconfdir)/ha.d halibdir = $(libdir)/@HB_PKG@ ## libraries lib_LTLIBRARIES = libcrmcommon.la ## binary progs halib_PROGRAMS = - ## SOURCES noinst_HEADERS = -libcrmcommon_la_SOURCES = ipc.c msg.c utils.c xml.c ctrl.c +libcrmcommon_la_SOURCES = ipc.c msg.c utils.c xml.c ctrl.c \ + iso8601.c iso8601_fields.c libcrmcommon_la_LDFLAGS = -version-info 0:0:0 clean-generic: rm -f *.log *.debug *.xml *~ install-exec-local: uninstall-local: diff --git a/lib/crm/common/iso8601.c b/lib/crm/common/iso8601.c new file mode 100644 index 0000000000..0d16940317 --- /dev/null +++ b/lib/crm/common/iso8601.c @@ -0,0 +1,1149 @@ +/* $Id: iso8601.c,v 1.1 2005/08/02 16:14:39 andrew Exp $ */ +/* + * Copyright (C) 2005 Andrew Beekhof + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * http://en.wikipedia.org/wiki/ISO_8601 as at 2005-08-01 + * + */ + +#include +#include +#include +#include + +gboolean gregorian_to_ordinal(ha_time_t *a_date); +gboolean ordinal_to_gregorian(ha_time_t *a_date); +gboolean ordinal_to_weekdays(ha_time_t *a_date); +void normalize_time(ha_time_t *a_time); + + +void +log_date(int log_level, const char *prefix, ha_time_t *date_time, int flags) +{ + char *date_s = NULL; + char *time_s = NULL; + char *offset_s = NULL; + ha_time_t *dt = NULL; + + if(flags & ha_log_local) { + crm_debug_6("Local version"); + dt = date_time; + } else { + dt = date_time->normalized; + } + + CRM_DEV_ASSERT(dt != NULL); if(crm_assert_failed) { return; } + + if(flags & ha_log_date) { + crm_malloc0(date_s, sizeof(char)*(32)); + if(date_s == NULL) { + } else if(flags & ha_date_weeks) { + snprintf(date_s, 31, "%d-W%.2d-%d", + dt->weekyears, dt->weeks, dt->weekdays); + + } else if(flags & ha_date_ordinal) { + snprintf(date_s, 31, "%d-%.3d",dt->years, dt->yeardays); + + } else { + snprintf(date_s, 31, "%.4d-%.2d-%.2d", + dt->years, dt->months, dt->days); + } + } + if(flags & ha_log_time) { + int offset = 0; + crm_malloc0(time_s, sizeof(char)*(32)); + if(time_s == NULL) { + return; + } + + snprintf(time_s, 31, "%.2d:%.2d:%.2d", + dt->hours, dt->minutes, dt->seconds); + + if(dt->offset != NULL) { + offset =(dt->offset->hours * 100) + dt->offset->minutes; + } + + crm_malloc0(offset_s, sizeof(char)*(32)); + if((flags & ha_log_local) == 0 || offset == 0) { + snprintf(offset_s, 31, "Z"); + + } else { + snprintf(offset_s, 31, " %s%.2d:%.2d", + offset>0?"+":"-", + dt->offset->hours, dt->offset->minutes); + } + } + crm_log_maybe(log_level, "%s%s%s%s%s%s", + prefix?prefix:"", prefix?": ":"", + date_s?date_s:"", (date_s!=NULL&&time_s!=NULL)?" ":"", + time_s?time_s:"", offset_s?offset_s:""); + + crm_free(date_s); + crm_free(time_s); + crm_free(offset_s); +} + +void +log_time_period(int log_level, ha_time_period_t *dtp, int flags) +{ + log_date(log_level, "Period start:", dtp->start, flags); + log_date(log_level, "Period end:", dtp->end, flags); +} + +ha_time_t* +parse_time_offset(char **offset_str) +{ + ha_time_t *new_time = NULL; + crm_malloc0(new_time, sizeof(ha_time_t)); + crm_malloc0(new_time->has, sizeof(ha_has_time_t)); + + if((*offset_str)[0] != 'Z') { + parse_time(offset_str, new_time, FALSE); + } + return new_time; +} + +ha_time_t* +parse_time(char **time_str, ha_time_t *a_time, gboolean with_offset) +{ + ha_time_t *new_time = a_time; + if(a_time == NULL) { + crm_malloc0(new_time, sizeof(ha_time_t)); + crm_malloc0(new_time->has, sizeof(ha_has_time_t)); + } + + CRM_DEV_ASSERT(new_time != NULL); + CRM_DEV_ASSERT(new_time->has != NULL); + + crm_debug_4("Get hours..."); + if(parse_int(time_str, 2, 24, &new_time->hours)) { + new_time->has->hours = TRUE; + } + + crm_debug_4("Get minutes..."); + if(parse_int(time_str, 2, 60, &new_time->minutes)) { + new_time->has->minutes = TRUE; + } + + crm_debug_4("Get seconds..."); + if(parse_int(time_str, 2, 60, &new_time->seconds)){ + new_time->has->seconds = TRUE; + } + + if(with_offset) { + crm_debug_4("Get offset..."); + while(isspace((*time_str)[0])) { + (*time_str)++; + } + + new_time->offset = parse_time_offset(time_str); + } + return new_time; +} + +void +normalize_time(ha_time_t *a_time) +{ + CRM_DEV_ASSERT(a_time != NULL); + CRM_DEV_ASSERT(a_time->has != NULL); + + if(a_time->normalized == NULL) { + crm_malloc0(a_time->normalized, sizeof(ha_time_t)); + } + if(a_time->normalized->has == NULL) { + crm_malloc0(a_time->normalized->has, sizeof(ha_has_time_t)); + } + + ha_set_time(a_time->normalized, a_time, FALSE); + if(a_time->offset != NULL) { + if(a_time->offset->has->hours) { + add_hours(a_time->normalized, a_time->offset->hours); + } + if(a_time->offset->has->minutes) { + add_minutes(a_time->normalized,a_time->offset->minutes); + } + if(a_time->offset->has->seconds) { + add_seconds(a_time->normalized,a_time->offset->seconds); + } + } + CRM_DEV_ASSERT(is_date_sane(a_time)); +} + + +/* +Dates + +Dates can be described in three different ways in ISO 8601: Calendar dates, ordinal dates, and week dates. Each is detailed below. +There is an important note on years however. Year 0001 corresponds to AD 1. The year before that is 0000, which corresponds to 1 BC. The year before that is -0001, which corresponds to 2 BC. This pattern continues. This system had already been used by astronomers (see astronomical year numbering) and may clarify the dispute about when a new century begins (see 20th century). +The standard uses the Gregorian calendar. Dates in other calendars such as the Julian calendar should in theory be converted to the Gregorian calendar before representation in ISO 8601. However, in practice, this may or may not happen, and the standard recognizes this fact by suggesting that, in cases where there may be confusion, it is imperative that the sender and receiver must both agree which calendar is to be used. When the Gregorian calendar is applied to dates prior to its adoption, it is known as the proleptic Gregorian calendar. +Years must be written with at least four digits to be unambiguous. Years written with two digits preceded by a hyphen may represent a year in an implied century, and two digits alone are used to designate a century. Unfortunately, common practice is to use two digits for either a year in an implied modern century or in the first century (89 might be 89 or 1989.) To prevent confusion, four digits are strongly recommended, as 0089 or 1989. Years with three digits such as 879 must be written with a leading zero, as 0879. Years may be described by more than four characters by agreement of sender and receiver. This addresses the year 10,000 problem, allowing the standard to specify dates later than AD 10000 or earlier than 10001 BC. +For purposes of reference ISO 8601 assigns the number 1875 to the year in which the Convention du Mètre was signed in Paris. +With the exception of years (as noted above), whenever a specification includes multiple characters, all the characters must be filled. This means that if hh is specified and the current hour is 4, the result must include a leading 0, as 04. + +Calendar dates +Calendar dates are dates as most people know them. They involve determining the day within a year by counting within a month. For example, April 5 is a calendar date. ISO 8601 specifies calendar dates like this: YYYY-MM-DD. This means, for example, that the fifth day of the fourth month of 1981, or 1981-04-05, is written 1981-04-05. It may also be written 19810405 if compactness is more important than readability by human beings. The format with separators is called the extended format, while the format without is called the basic format. +The standard allows for dates to be written with less precision. For example, you may write 1981-04 to mean April, 1981 (this is, in fact, the basic version -- you should not leave out the separator.) You may simply write 1981 to refer to that year. Or you may even write 19 to refer to a century. +The standard also allows for dates which include an implied element, such as an implied century. It is careful to emphasize the importance of clear communication between sender and receiver when implied elements are being used. It is a lack of such clarity that led to the year 2000 problem. + +Ordinal dates +Ordinal dates are dates in which a day is determined by counting within the entire year. Thus a day is represented by three digits, starting with January 1 being 001, February 1 being 032, and so on. December 31 will therefore be 365, except in leap years, where it will be 366. +An ordinal date is specified with YYYY-DDD. Thus, 1981-04-05 is represented as 1981-095. The basic format of this drops the separator, becoming 1981095. + +Week dates +Week dates are dates in which a day is determined by counting within a week. Weeks are given two digits beginning with 01 as the first week of the year (see below), up to 52 or 53 being the last week of the year. By convention, the week starts with Monday. Each day is given one digit beginning with 1 as Monday leading to 7 as Sunday. The extended format representation of week dates is YYYY-Www-d, where W is the actual letter W, ww refers to the two digit week number, and d is the day's number. This means that 1981-04-05, the Sunday of the 14th week in 1981, is represented as 1981-W14-7, or 1981W147. This format can also specify a week, as 1981W14. +The first week of a year is the first week which includes at least four days in the new year (again using the convention that Monday is the first day of the week). It follows that the first week of the year is always the week which includes the first Thursday of January, and also the week which includes January 4. This means that week 01 may include days from the previous year, or that week 52 or 53 may include days from the next year. For example, 2004-01-01 occurs on a Thursday. This means that 2004-W01 consists of Monday, 2003-12-29 through Sunday, 2004-01-04. 2005-01-01 occurs on a Saturday, meaning that 2004-W53 is 2004-12-27 through 2005-01-02, and 2005-W01 starts on 2005-01-03. +For a good mathematical treatment of the ISO 8601 Calendar see the external link The Mathematics of the ISO 8601 Calendar below. +*/ + +ha_time_t * +parse_date(char **date_str) +{ + gboolean converted = FALSE; + ha_time_t *new_time = NULL; + crm_malloc0(new_time, sizeof(ha_time_t)); + crm_malloc0(new_time->has, sizeof(ha_has_time_t)); + + CRM_DEV_ASSERT(date_str != NULL); + CRM_DEV_ASSERT(strlen(*date_str) > 0); + + while(isspace((*date_str)[0]) == FALSE) { + char ch = (*date_str)[0]; + crm_debug_5("Switching on ch=%c (len=%ld)", + ch, strlen(*date_str)); + + if(ch == 0) { + /* all done */ + break; + + } else if(ch == '/') { + /* all done - interval marker */ + break; + + } else if(ch == 'W') { + CRM_DEV_ASSERT(new_time->has->weeks == FALSE); + (*date_str)++; + if(parse_int(date_str, 2, 53, &new_time->weeks)){ + new_time->has->weeks = TRUE; + new_time->weekyears = new_time->years; + new_time->has->weekyears = new_time->has->years; + } + if((*date_str)[0] == '-') { + (*date_str)++; + if(parse_int(date_str, 1, 7, &new_time->weekdays)) { + new_time->has->weekdays = TRUE; + } + } + + if(new_time->weekdays == 0 + || new_time->has->weekdays == FALSE) { + new_time->weekdays = 1; + new_time->has->weekdays = TRUE; + } + + } else if(ch == '-') { + (*date_str)++; + if(check_for_ordinal(*date_str)) { + if(parse_int(date_str, 3, 366, &new_time->yeardays)) { + new_time->has->yeardays = TRUE; + } + } + + } else if(ch == 'O') { + /* ordinal date */ + (*date_str)++; + if(parse_int(date_str, 3, 366, &new_time->yeardays)){ + new_time->has->yeardays = TRUE; + } + + } else if(ch == 'T') { + if(new_time->has->yeardays) { + converted = convert_from_ordinal(new_time); + + } else if(new_time->has->weekdays) { + converted = convert_from_weekdays(new_time); + + } else { + converted = convert_from_gregorian(new_time); + } + (*date_str)++; + parse_time(date_str, new_time, TRUE); + + } else if(isnumber(ch)) { + if(new_time->has->years == FALSE + && parse_int(date_str, 4, 9999, &new_time->years)) { + new_time->has->years = TRUE; + + } else if(check_for_ordinal(*date_str) && parse_int( + date_str, 3, + is_leap_year(new_time->years)?366:365, + &new_time->yeardays)) { + new_time->has->yeardays = TRUE; + + } else if(new_time->has->months == FALSE + && parse_int(date_str, 2, 12, &new_time->months)) { + new_time->has->months = TRUE; + + } else if(new_time->has->days == FALSE) { + if(parse_int(date_str, 2, + days_per_month(new_time->months, new_time->years), + &new_time->days)) { + new_time->has->days = TRUE; + } + } + + } else if(isspace(ch)) { + (*date_str)++; +/* } else if(new_time->has->months == FALSE) { */ +/* new_time->months = str_lookup(*date_str, date_month); */ +/* new_time->has->months = TRUE; */ + +/* } else if(new_time->has->days == FALSE) { */ +/* new_time->days = str_lookup(*date_str, date_day); */ +/* new_time->has->days = TRUE; */ + } else { + crm_err("Unexpected characters at: %s", *date_str); + break; + } + } + + if(converted) { + + } else if(new_time->has->yeardays) { + convert_from_ordinal(new_time); + + } else if(new_time->has->weekdays) { + convert_from_weekdays(new_time); + + } else { + convert_from_gregorian(new_time); + } + + normalize_time(new_time); + + log_date(LOG_DEBUG_3, "Unpacked", new_time, ha_log_date|ha_log_time); + + CRM_DEV_ASSERT(is_date_sane(new_time)); + + return new_time; +} + +ha_time_t* +parse_time_duration(char **interval_str) +{ + gboolean is_time = FALSE; + ha_time_t *diff = NULL; + crm_malloc0(diff, sizeof(ha_time_t)); + crm_malloc0(diff->has, sizeof(ha_has_time_t)); + + CRM_DEV_ASSERT(interval_str != NULL); + CRM_DEV_ASSERT(strlen(*interval_str) > 0); + + CRM_DEV_ASSERT((*interval_str)[0] == 'P'); + (*interval_str)++; + + while(isspace((*interval_str)[0]) == FALSE) { + int an_int = 0; + char ch = 0; + + if((*interval_str)[0] == 'T') { + is_time = TRUE; + (*interval_str)++; + } + + if(parse_int(interval_str, 10, 0, &an_int) == FALSE) { + break; + } + ch = (*interval_str)[0]; + (*interval_str)++; + + crm_debug_4("%c=%d", ch, an_int); + + switch(ch) { + case 0: + return diff; + break; + case 'Y': + diff->years = an_int; + diff->has->years = TRUE; + break; + case 'M': + if(is_time) { + diff->minutes = an_int; + diff->has->minutes = TRUE; + } else { + diff->months = an_int; + diff->has->months = TRUE; + } + break; + case 'W': + diff->weeks = an_int; + diff->has->weeks = TRUE; + break; + case 'D': + diff->days = an_int; + diff->has->days = TRUE; + break; + case 'H': + diff->hours = an_int; + diff->has->hours = TRUE; + break; + case 'S': + diff->seconds = an_int; + diff->has->seconds = TRUE; + break; + default: + break; + } + } + return diff; +} + +ha_time_period_t* +parse_time_period(char **period_str) +{ + gboolean invalid = FALSE; + const char *original = *period_str; + ha_time_period_t *period = NULL; + crm_malloc0(period, sizeof(ha_time_period_t)); + + CRM_DEV_ASSERT(period_str != NULL); + CRM_DEV_ASSERT(strlen(*period_str) > 0); + + tzset(); + + if((*period_str)[0] == 'P') { + period->diff = parse_time_duration(period_str); + } else { + period->start = parse_date(period_str); + } + + if((*period_str)[0] != 0) { + CRM_DEV_ASSERT((*period_str)[0] == '/'); + (*period_str)++; + + if((*period_str)[0] == 'P') { + period->diff = parse_time_duration(period_str); + } else { + period->end = parse_date(period_str); + } + + } else if(period->diff != NULL) { + /* just aduration starting from now */ + time_t now = time(NULL); + crm_malloc0(period->start, sizeof(ha_time_t)); + crm_malloc0(period->start->has, sizeof(ha_has_time_t)); + crm_malloc0(period->start->offset, sizeof(ha_time_t)); + crm_malloc0(period->start->offset->has, sizeof(ha_has_time_t)); + + ha_set_timet_time(period->start, &now); + normalize_time(period->start); + } else { + CRM_DEV_ASSERT((*period_str)[0] == '/'); + return NULL; + } + + + /* sanity checks */ + if(period->start == NULL && period->end == NULL) { + crm_err("Invalid time period: %s", original); + invalid = TRUE; + + } else if(period->start == NULL && period->diff == NULL) { + crm_err("Invalid time period: %s", original); + invalid = TRUE; + + } else if(period->end == NULL && period->diff == NULL) { + crm_err("Invalid time period: %s", original); + invalid = TRUE; + } + + if(invalid) { + crm_free(period->start); + crm_free(period->end); + crm_free(period->diff); + crm_free(period); + return NULL; + } + if(period->end == NULL && period->diff == NULL) { + } + + if(period->start == NULL) { + period->start = subtract_time(period->end, period->diff); + normalize_time(period->start); + + } else if(period->end == NULL) { + period->end = add_time(period->start, period->diff); + normalize_time(period->end); + } + + is_date_sane(period->start); + is_date_sane(period->end); + + return period; +} + +int month2days[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; + +/* http://www.personal.ecu.edu/mccartyr/ISOwdALG.txt */ +int +january1(int year) +{ + int YY = (year - 1) % 100; + int C = (year - 1) - YY; + int G = YY + YY/4; + int jan1 = 1 + (((((C / 100) % 4) * 5) + G) % 7); + crm_debug_6("YY=%d, C=%d, G=%d", YY, C, G); + crm_debug_5("January 1 %.4d: %d", year, jan1); + return jan1; +} + +int +weeks_in_year(int year) +{ + int weeks = 52; + int jan1 = january1(year); + /* if jan1 == thursday */ + if(jan1 == 4) { + weeks++; + } else { + jan1 = january1(year+1); + /* if dec31 == thursday aka. jan1 of next year is a friday */ + if(jan1 == 5) { + weeks++; + } + + } + return weeks; +} + +gboolean +convert_from_gregorian(ha_time_t *a_date) +{ + CRM_DEV_ASSERT(gregorian_to_ordinal(a_date)); + CRM_DEV_ASSERT(ordinal_to_weekdays(a_date)); + return TRUE; +} + +gboolean +gregorian_to_ordinal(ha_time_t *a_date) +{ + CRM_DEV_ASSERT(a_date->has->years); + CRM_DEV_ASSERT(a_date->has->months); + CRM_DEV_ASSERT(a_date->has->days); + + CRM_DEV_ASSERT(a_date->months > 0); + CRM_DEV_ASSERT(a_date->days > 0); + + a_date->yeardays = month2days[a_date->months-1]; + a_date->yeardays += a_date->days; + a_date->has->yeardays = TRUE; + + if(is_leap_year(a_date->years) && a_date->months > 2) { + (a_date->yeardays)++; + } + crm_debug_4("Converted %.4d-%.2d-%.2d to %.4d-%.3d", + a_date->years, a_date->months, a_date->days, + a_date->years, a_date->yeardays); + + return TRUE; +} + +gboolean +convert_from_ordinal(ha_time_t *a_date) +{ + CRM_DEV_ASSERT(ordinal_to_gregorian(a_date)); + CRM_DEV_ASSERT(ordinal_to_weekdays(a_date)); + return TRUE; +} + + +gboolean ordinal_to_gregorian(ha_time_t *a_date) +{ + CRM_DEV_ASSERT(a_date->has->years); + CRM_DEV_ASSERT(a_date->has->yeardays); + + CRM_DEV_ASSERT(a_date->yeardays > 0); + + a_date->days = a_date->yeardays; + a_date->months = 11; + if(is_leap_year(a_date->years) && a_date->yeardays > 366) { + crm_err("Year %.4d only has 366 days (supplied %.3d)", + a_date->years, a_date->yeardays); + a_date->yeardays = 366; + + } else if(!is_leap_year(a_date->years) && a_date->yeardays > 365) { + crm_err("Year %.4d only has 365 days (supplied %.3d)", + a_date->years, a_date->yeardays); + a_date->yeardays = 365; + } + + while(a_date->months > 0 + && a_date->yeardays <= month2days[a_date->months]) { + crm_debug_6("month %d: %d vs. %d", + a_date->months, a_date->yeardays, + month2days[a_date->months]); + (a_date->months)--; + } + + a_date->days -= month2days[a_date->months]; + (a_date->months)++; + + CRM_DEV_ASSERT(a_date->months > 0); + + if(is_leap_year(a_date->years) && a_date->months > 2) { + (a_date->days)--; + } + if(a_date->days == 0) { + /* annoying underflow */ + a_date->days = days_per_month(a_date->months, a_date->years); + (a_date->months)--; + } + + a_date->has->days = TRUE; + a_date->has->months = TRUE; + a_date->has->years = TRUE; + + crm_debug_4("Converted %.4d-%.3d to %.4d-%.2d-%.2d", + a_date->years, a_date->yeardays, + a_date->years, a_date->months, a_date->days); + + return TRUE; +} + + +gboolean +ordinal_to_weekdays(ha_time_t *a_date) +{ + int year_num = 0; + int jan1 = january1(a_date->years); + int h = -1; + + CRM_DEV_ASSERT(a_date->has->years); + CRM_DEV_ASSERT(a_date->has->yeardays); + CRM_DEV_ASSERT(a_date->yeardays > 0); + + h = a_date->yeardays + jan1 - 1; + a_date->weekdays = 1 + ((h-1) % 7); + a_date->has->weekdays = TRUE; + + if(a_date->yeardays <= (8-jan1) && jan1 > 4) { + year_num = a_date->years - 1; + a_date->weeks = weeks_in_year(year_num); + a_date->has->weeks = TRUE; + + } else { + year_num = a_date->years; + } + + if(year_num == a_date->years) { + int i = 365; + if(is_leap_year(year_num)) { + i = 366; + } + if( (i - a_date->yeardays) < (4 - a_date->weekdays) ) { + year_num = a_date->years + 1; + a_date->weeks = 1; + a_date->has->weeks = TRUE; + } + } + + if(year_num == a_date->years) { + int j = a_date->yeardays + (7-a_date->weekdays) + (jan1 - 1); + a_date->weeks = j / 7; + a_date->has->weeks = TRUE; + if(jan1 > 4) { + a_date->weeks -= 1; + } + } + + a_date->weekyears = year_num; + a_date->has->weekyears = TRUE; + crm_debug_4("Converted %.4d-%.3d to %.4dW%.2d-%d", + a_date->years, a_date->yeardays, + a_date->weekyears, a_date->weeks, a_date->weekdays); + return TRUE; +} + +gboolean +convert_from_weekdays(ha_time_t *a_date) +{ + gboolean conversion = FALSE; + int jan1 = january1(a_date->weekyears); + + CRM_DEV_ASSERT(a_date->has->weekyears); + CRM_DEV_ASSERT(a_date->has->weeks); + CRM_DEV_ASSERT(a_date->has->weekdays); + + CRM_DEV_ASSERT(a_date->weeks > 0); + CRM_DEV_ASSERT(a_date->weekdays > 0); + CRM_DEV_ASSERT(a_date->weekdays < 8); + + a_date->has->years = TRUE; + a_date->years = a_date->weekyears; + + a_date->has->yeardays = TRUE; + a_date->yeardays = (7 * (a_date->weeks-1)); + + /* break up the addition to make sure overflows are correctly handled */ + if(a_date->yeardays == 0) { + a_date->yeardays = a_date->weekdays; + } else { + add_yeardays(a_date, a_date->weekdays); + } + + crm_debug_5("Pre-conversion: %dW%d-%d to %.4d-%.3d", + a_date->weekyears, a_date->weeks, a_date->weekdays, + a_date->years, a_date->yeardays); + + conversion = ordinal_to_gregorian(a_date); + + if(conversion) { + if(jan1 < 4) { + sub_days(a_date, jan1-1); + } else if(jan1 > 4) { + add_days(a_date, jan1-4); + } + } + return conversion; +} + +void +ha_set_time(ha_time_t *lhs, ha_time_t *rhs, gboolean offset) +{ + crm_debug_6("lhs=%p, rhs=%p, offset=%d", lhs, rhs, offset); + + CRM_DEV_ASSERT(lhs != NULL && rhs != NULL); + CRM_DEV_ASSERT(lhs->has != NULL && rhs->has != NULL); + + lhs->years = rhs->years; + lhs->has->years = rhs->has->years; + + lhs->weekyears = rhs->weekyears; + lhs->has->weekyears = rhs->has->weekyears; + + lhs->months = rhs->months; + lhs->has->months = rhs->has->months; + + lhs->weeks = rhs->weeks; + lhs->has->weeks = rhs->has->weeks; + + lhs->days = rhs->days; + lhs->has->days = rhs->has->days; + + lhs->weekdays = rhs->weekdays; + lhs->has->weekdays = rhs->has->weekdays; + + lhs->yeardays = rhs->yeardays; + lhs->has->yeardays = rhs->has->yeardays; + + lhs->hours = rhs->hours; + lhs->has->hours = rhs->has->hours; + + lhs->minutes = rhs->minutes; + lhs->has->minutes = rhs->has->minutes; + + lhs->seconds = rhs->seconds; + lhs->has->seconds = rhs->has->seconds; + + if(lhs->offset) { + reset_time(lhs->offset); + } + if(offset && rhs->offset) { + ha_set_time(lhs->offset, rhs->offset, FALSE); + } + +} + + + +void +ha_set_tm_time(ha_time_t *lhs, struct tm *rhs) +{ + int wday = rhs->tm_wday; + int h_offset = 0; + int m_offset = 0; + + if(rhs->tm_year > 0) { + /* years since 1900 */ + lhs->years = 1900 + rhs->tm_year; + lhs->has->years = TRUE; + } + + if(rhs->tm_yday > 0) { + /* days since January 1 [0-365] */ + lhs->yeardays = 1 + rhs->tm_yday; + lhs->has->yeardays =TRUE; + } + + if(rhs->tm_hour >= 0) { + lhs->hours = rhs->tm_hour; + lhs->has->hours =TRUE; + } + if(rhs->tm_min >= 0) { + lhs->minutes = rhs->tm_min; + lhs->has->minutes =TRUE; + } + if(rhs->tm_sec >= 0) { + lhs->seconds = rhs->tm_sec; + lhs->has->seconds =TRUE; + } + + convert_from_ordinal(lhs); + + /* months since January [0-11] */ + CRM_DEV_ASSERT(rhs->tm_mon < 0 || lhs->months == (1 + rhs->tm_mon)); + + /* day of the month [1-31] */ + CRM_DEV_ASSERT(rhs->tm_mday < 0 || lhs->days == rhs->tm_mday); + + /* days since Sunday [0-6] */ + if(wday == 0) { + wday= 7; + } + CRM_DEV_ASSERT(rhs->tm_wday < 0 || lhs->weekdays == wday); + + CRM_DEV_ASSERT(lhs->offset != NULL); + CRM_DEV_ASSERT(lhs->offset->has != NULL); + + /* tm_gmtoff == offset from UTC in seconds */ + h_offset = rhs->tm_gmtoff / (3600); + m_offset = (rhs->tm_gmtoff - (3600 * h_offset)) / (60); + crm_debug_6("Offset (s): %ld, offset (hh:mm): %.2d:%.2d", + rhs->tm_gmtoff, h_offset, m_offset); + + lhs->offset->hours = h_offset; + lhs->offset->has->hours = TRUE; + + lhs->offset->minutes = m_offset; + lhs->offset->has->minutes = TRUE; + + normalize_time(lhs); +} + + +void +ha_set_timet_time(ha_time_t *lhs, time_t *rhs) +{ + ha_set_tm_time(lhs, localtime(rhs)); +} + +ha_time_t * +add_time(ha_time_t *lhs, ha_time_t *rhs) +{ + ha_time_t *answer = NULL; + CRM_DEV_ASSERT(lhs != NULL && rhs != NULL); + + crm_malloc0(answer, sizeof(ha_time_t)); + crm_malloc0(answer->has, sizeof(ha_has_time_t)); + crm_malloc0(answer->offset, sizeof(ha_time_t)); + crm_malloc0(answer->offset->has, sizeof(ha_has_time_t)); + + ha_set_time(answer, lhs, TRUE); + normalize_time(lhs); + normalize_time(answer); + + if(rhs->has->years) { + add_years(answer, rhs->years); + } + if(rhs->has->months) { + add_months(answer, rhs->months); + } + if(rhs->has->weeks) { + add_weeks(answer, rhs->weeks); + } + if(rhs->has->days) { + add_days(answer, rhs->days); + } + + add_hours(answer, rhs->hours); + add_minutes(answer, rhs->minutes); + add_seconds(answer, rhs->seconds); + + return answer; +} + + +ha_time_t * +subtract_time(ha_time_t *lhs, ha_time_t *rhs) +{ + ha_time_t *answer = NULL; + CRM_DEV_ASSERT(lhs != NULL && rhs != NULL); + + crm_malloc0(answer, sizeof(ha_time_t)); + crm_malloc0(answer->has, sizeof(ha_has_time_t)); + crm_malloc0(answer->offset, sizeof(ha_time_t)); + crm_malloc0(answer->offset->has, sizeof(ha_has_time_t)); + + ha_set_time(answer, lhs, TRUE); + normalize_time(lhs); + normalize_time(rhs); + normalize_time(answer); + + sub_years(answer, rhs->years); + sub_months(answer, rhs->months); + sub_weeks(answer, rhs->weeks); + sub_days(answer, rhs->days); + sub_hours(answer, rhs->hours); + sub_minutes(answer, rhs->minutes); + sub_seconds(answer, rhs->seconds); +} + +/* ha_time_interval_t* */ +/* parse_time_interval(char **interval_str) */ +/* { */ +/* return NULL; */ +/* } */ + +int month_days[13] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 29 }; +int +days_per_month(int month, int year) +{ + month--; + if(month == 2 && is_leap_year(year)) { + month = 13; + } + return month_days[month]; +} + +gboolean +is_leap_year(int year) +{ + gboolean is_leap = FALSE; + if(year % 4 == 0) { + is_leap = TRUE; + } + if(year % 100 == 0 && year % 400 != 0 ) { + is_leap = FALSE; + } + return is_leap; +} + + +gboolean +parse_int(char **str, int field_width, int uppper_bound, int *result) +{ + int lpc = 0; + int intermediate = 0; + gboolean fraction = FALSE; + gboolean negate = FALSE; + + CRM_DEV_ASSERT(str != NULL); + CRM_DEV_ASSERT(*str != NULL); + CRM_DEV_ASSERT(result != NULL); + + *result = 0; + + if(strlen(*str) <= 0) { + return FALSE; + } + + crm_debug_6("max width: %d, first char: %c", field_width, (*str)[0]); + + if((*str)[0] == '.' || (*str)[0] == ',') { + fraction = TRUE; + field_width = -1; + (*str)++; + } else if((*str)[0] == '-') { + negate = TRUE; + (*str)++; + } else if((*str)[0] == '+' + || (*str)[0] == ':') { + (*str)++; + } + + for(; (fraction || lpc < field_width) && isnumber((*str)[0]); lpc++) { + if(fraction) { + intermediate = ((*str)[0] - '0')/(10^lpc); + } else { + *result *= 10; + intermediate = (*str)[0] - '0'; + } + *result += intermediate; + (*str)++; + } + if(fraction) { + *result = (int)(*result * uppper_bound); + + } else if(uppper_bound > 0 && *result > uppper_bound) { + *result = uppper_bound; + } + if(negate) { + *result = 0 - *result; + } + if(lpc > 0) { + crm_debug_5("Found int: %d", *result); + return TRUE; + } + return FALSE; +} + +gboolean +check_for_ordinal(const char *str) +{ + if(isnumber(str[2]) == FALSE) { + crm_debug_6("char 3 == %c", str[2]); + return FALSE; + } + if(isspace(str[3])) { + return TRUE; + } else if(str[3] == 0) { + return TRUE; + } else if(str[3] == 'T') { + return TRUE; + } else if(str[3] == '/') { + return TRUE; + } + crm_debug_6("char 4 == %c", str[3]); + return FALSE; +} + +int str_lookup(const char *str, enum date_fields field) +{ + return 0; +} + +void +reset_time(ha_time_t *a_time) +{ + a_time->years = 0; + a_time->has->years = FALSE; + + a_time->weekyears = 0; + a_time->has->weekyears = FALSE; + + a_time->months = 0; + a_time->has->months = FALSE; + + a_time->weeks = 0; + a_time->has->weeks = FALSE; + + a_time->days = 0; + a_time->has->days = FALSE; + + a_time->weekdays = 0; + a_time->has->weekdays = FALSE; + + a_time->yeardays = 0; + a_time->has->yeardays = FALSE; + + a_time->hours = 0; + a_time->has->hours = FALSE; + + a_time->minutes = 0; + a_time->has->minutes = FALSE; + + a_time->seconds = 0; + a_time->has->seconds = FALSE; +} + + +void +reset_tm(struct tm *some_tm) +{ + some_tm->tm_sec = -1; /* seconds after the minute [0-60] */ + some_tm->tm_min = -1; /* minutes after the hour [0-59] */ + some_tm->tm_hour = -1; /* hours since midnight [0-23] */ + some_tm->tm_mday = -1; /* day of the month [1-31] */ + some_tm->tm_mon = -1; /* months since January [0-11] */ + some_tm->tm_year = -1; /* years since 1900 */ + some_tm->tm_wday = -1; /* days since Sunday [0-6] */ + some_tm->tm_yday = -1; /* days since January 1 [0-365] */ + some_tm->tm_isdst = -1; /* Daylight Savings Time flag */ + some_tm->tm_gmtoff = -1; /* offset from CUT in seconds */ + some_tm->tm_zone = NULL;/* timezone abbreviation */ +} + +gboolean +is_date_sane(ha_time_t *a_date) +{ + int ydays = 0; + int mdays = 0; + int weeks = 0; + + CRM_DEV_ASSERT(a_date != NULL); + + ydays = is_leap_year(a_date->years)?366:365; + mdays = days_per_month(a_date->months, a_date->years); + weeks = weeks_in_year(a_date->weekyears); + crm_debug_5("max ydays: %d, max mdays: %d, max weeks: %d", + ydays, mdays, weeks); + + CRM_DEV_ASSERT(a_date->has->years); + CRM_DEV_ASSERT(a_date->has->weekyears); + + CRM_DEV_ASSERT(a_date->has->months); + CRM_DEV_ASSERT(a_date->months > 0); + CRM_DEV_ASSERT(a_date->months < 13); + + CRM_DEV_ASSERT(a_date->has->weeks); + CRM_DEV_ASSERT(a_date->weeks > 0); + CRM_DEV_ASSERT(a_date->weeks < weeks); + + CRM_DEV_ASSERT(a_date->has->days); + CRM_DEV_ASSERT(a_date->days > 0); + CRM_DEV_ASSERT(a_date->days <= mdays); + + CRM_DEV_ASSERT(a_date->has->weekdays); + CRM_DEV_ASSERT(a_date->weekdays > 0); + CRM_DEV_ASSERT(a_date->weekdays < 8); + + CRM_DEV_ASSERT(a_date->has->yeardays); + CRM_DEV_ASSERT(a_date->yeardays > 0); + CRM_DEV_ASSERT(a_date->yeardays <= ydays); + + CRM_DEV_ASSERT(a_date->hours >= 0); + CRM_DEV_ASSERT(a_date->hours < 24); + + CRM_DEV_ASSERT(a_date->minutes >= 0); + CRM_DEV_ASSERT(a_date->minutes < 60); + + CRM_DEV_ASSERT(a_date->seconds >= 0); + CRM_DEV_ASSERT(a_date->seconds < 60); + + return TRUE; +} + +#define do_cmp_field(lhs, rhs, field) \ + { \ + if(lhs->field > rhs->field) { \ + crm_debug_2("%s: %d > %d", \ + #field, lhs->field, rhs->field); \ + return 1; \ + } else if(lhs->field < rhs->field) { \ + crm_debug_2("%s: %d < %d", \ + #field, lhs->field, rhs->field); \ + return -1; \ + } \ + } + +int +compare_date(ha_time_t *lhs, ha_time_t *rhs) +{ + normalize_time(lhs); + normalize_time(rhs); + + do_cmp_field(lhs->normalized, rhs->normalized, years); + do_cmp_field(lhs->normalized, rhs->normalized, yeardays); + do_cmp_field(lhs->normalized, rhs->normalized, hours); + do_cmp_field(lhs->normalized, rhs->normalized, minutes); + do_cmp_field(lhs->normalized, rhs->normalized, seconds); + + return 0; +} + diff --git a/lib/crm/common/iso8601_fields.c b/lib/crm/common/iso8601_fields.c new file mode 100644 index 0000000000..260407cc44 --- /dev/null +++ b/lib/crm/common/iso8601_fields.c @@ -0,0 +1,374 @@ +/* $Id: iso8601_fields.c,v 1.1 2005/08/02 16:14:39 andrew Exp $ */ +/* + * Copyright (C) 2005 Andrew Beekhof + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * http://en.wikipedia.org/wiki/ISO_8601 as at 2005-08-01 + * + */ + +#include +#include +#include +#include + + +#define do_add_field(atime, field, extra, limit, overflow) \ + { \ + crm_debug_6("Adding %d to %d (limit=%d)", \ + extra, atime->field, limit); \ + atime->field += extra; \ + if(limit > 0) { \ + while(limit < atime->field) { \ + crm_debug_6("Overflowing: %d", atime->field); \ + atime->field -= limit; \ + overflow(atime, 1); \ + } \ + } \ + atime->field = atime->field; \ + crm_debug_6("Result: %d", atime->field); \ + } + +#define do_sub_field(atime, field, extra, limit, overflow) \ + { \ + crm_debug_6("Subtracting %d from %d (limit=%d)", \ + extra, atime->field, limit); \ + atime->field -= extra; \ + while(atime->field <= 1) { \ + crm_debug_6("Underflowing: %d", atime->field); \ + atime->field += limit; \ + overflow(atime, 1); \ + } \ + crm_debug_6("Result: %d", atime->field); \ + } + +void +add_seconds(ha_time_t *a_time, int extra) +{ + if(extra < 0) { + sub_seconds(a_time, -extra); + } else { + do_add_field(a_time, seconds, extra, 60, add_minutes); + } +} + + +void +add_minutes(ha_time_t *a_time, int extra) +{ + if(extra < 0) { + sub_minutes(a_time, -extra); + } else { + do_add_field(a_time, minutes, extra, 60, add_hours); + } +} + + +void +add_hours(ha_time_t *a_time, int extra) +{ + if(extra < 0) { + sub_hours(a_time, -extra); + } else { + do_add_field(a_time, hours, extra, 24, add_days); + } +} + +void +add_days(ha_time_t *a_time, int extra) +{ + if(a_time->has->days == FALSE) { + crm_debug_4("has->days == FALSE"); + return; + } + if(extra < 0) { + sub_days(a_time, -extra); + } else { + do_add_field(a_time, days, extra, + days_per_month(a_time->months, a_time->years), + add_months); + } + + convert_from_gregorian(a_time); +} + +void +add_weekdays(ha_time_t *a_time, int extra) +{ + if(a_time->has->weekdays == FALSE) { + crm_debug_4("has->weekdays == FALSE"); + return; + } + if(extra < 0) { + sub_weekdays(a_time, -extra); + } else { + do_add_field(a_time, weekdays, extra, 7, add_weeks); + } + + convert_from_weekdays(a_time); +} + +void +add_yeardays(ha_time_t *a_time, int extra) +{ + if(a_time->has->yeardays == FALSE) { + crm_debug_4("has->yeardays == FALSE"); + return; + } + if(extra < 0) { + sub_yeardays(a_time, -extra); + } else { + do_add_field(a_time, yeardays, extra, + is_leap_year(a_time->years)?366:365, add_ordinalyears); + } + + convert_from_ordinal(a_time); +} + +void +add_weeks(ha_time_t *a_time, int extra) +{ + if(a_time->has->weeks== FALSE) { + crm_debug_4("has->weeks == FALSE"); + return; + } + if(extra < 0) { + sub_weeks(a_time, -extra); + } else { + do_add_field(a_time, weeks, extra, + weeks_in_year(a_time->years), add_weekyears); + } + + convert_from_weekdays(a_time); +} + +void +add_months(ha_time_t *a_time, int extra) +{ + int max = 0; + if(a_time->has->months == FALSE) { + crm_debug_4("has->months == FALSE"); + return; + } + if(extra < 0) { + sub_months(a_time, -extra); + } else { + do_add_field(a_time, months, extra, 12, add_years); + } + + + max = days_per_month(a_time->months, a_time->years); + if(a_time->days > max) { + a_time->days = max; + } + convert_from_gregorian(a_time); +} + +void +add_years(ha_time_t *a_time, int extra) +{ + if(a_time->has->years == FALSE) { + crm_debug_4("has->years == FALSE"); + return; + } + a_time->years += extra; + convert_from_gregorian(a_time); +} + +void +add_ordinalyears(ha_time_t *a_time, int extra) +{ + if(a_time->has->years == FALSE) { + crm_debug_4("has->years == FALSE"); + return; + } + a_time->years += extra; + convert_from_ordinal(a_time); +} + +void +add_weekyears(ha_time_t *a_time, int extra) +{ + if(a_time->has->weekyears == FALSE) { + crm_debug_4("has->weekyears == FALSE"); + return; + } + a_time->weekyears += extra; + convert_from_weekdays(a_time); +} + +void +sub_seconds(ha_time_t *a_time, int extra) +{ + if(extra < 0) { + add_seconds(a_time, -extra); + } else { + do_sub_field(a_time, seconds, extra, 60, sub_minutes); + } +} + +void +sub_minutes(ha_time_t *a_time, int extra) +{ + if(extra < 0) { + add_minutes(a_time, -extra); + } else { + do_sub_field(a_time, minutes, extra, 60, sub_hours); + } +} + +void +sub_hours(ha_time_t *a_time, int extra) +{ + if(extra < 0) { + add_hours(a_time, -extra); + } else { + do_sub_field(a_time, hours, extra, 24, sub_days); + } +} + +void +sub_days(ha_time_t *a_time, int extra) +{ + if(a_time->has->days == FALSE) { + crm_debug_4("has->days == FALSE"); + return; + } + + crm_debug_5("Subtracting %d days from %.4d-%.2d-%.2d", + extra, a_time->years, a_time->months, a_time->days); + + if(extra < 0) { + add_days(a_time, -extra); + } else { + do_sub_field(a_time, days, extra, + days_per_month(a_time->months, a_time->years), + sub_months); + } + + convert_from_gregorian(a_time); +} + +void +sub_weekdays(ha_time_t *a_time, int extra) +{ + if(a_time->has->weekdays == FALSE) { + crm_debug_4("has->weekdays == FALSE"); + return; + } + + crm_debug_5("Subtracting %d days from %.4d-%.2d-%.2d", + extra, a_time->years, a_time->months, a_time->days); + + if(extra < 0) { + add_weekdays(a_time, -extra); + } else { + do_sub_field(a_time, weekdays, extra, 7, sub_weeks); + } + + convert_from_weekdays(a_time); +} + +void +sub_yeardays(ha_time_t *a_time, int extra) +{ + if(a_time->has->yeardays == FALSE) { + crm_debug_4("has->yeardays == FALSE"); + return; + } + + crm_debug_5("Subtracting %d days from %.4d-%.3d", + extra, a_time->years, a_time->yeardays); + + if(extra < 0) { + add_yeardays(a_time, -extra); + } else { + do_sub_field(a_time, yeardays, extra, + is_leap_year(a_time->years)?366:365, sub_ordinalyears); + } + + convert_from_ordinal(a_time); +} + + +void +sub_weeks(ha_time_t *a_time, int extra) +{ + if(a_time->has->weeks == FALSE) { + crm_debug_4("has->weeks == FALSE"); + return; + } + if(extra < 0) { + add_weeks(a_time, -extra); + } else { + do_sub_field(a_time, weeks, extra, + weeks_in_year(a_time->years), sub_weekyears); + } + + convert_from_weekdays(a_time); +} + +void +sub_months(ha_time_t *a_time, int extra) +{ + if(a_time->has->months == FALSE) { + crm_debug_4("has->months == FALSE"); + return; + } + if(extra < 0) { + add_months(a_time, -extra); + } else { + do_sub_field(a_time, months, extra, 12, sub_years); + } + convert_from_gregorian(a_time); +} + +void +sub_years(ha_time_t *a_time, int extra) +{ + if(a_time->has->years == FALSE) { + crm_debug_4("has->years == FALSE"); + return; + } + a_time->years -= extra; + convert_from_gregorian(a_time); +} + +void +sub_weekyears(ha_time_t *a_time, int extra) +{ + if(a_time->has->weekyears == FALSE) { + crm_debug_4("has->weekyears == FALSE"); + return; + } + a_time->weekyears -= extra; + + convert_from_weekdays(a_time); +} + +void +sub_ordinalyears(ha_time_t *a_time, int extra) +{ + if(a_time->has->years == FALSE) { + crm_debug_4("has->years == FALSE"); + return; + } + a_time->years -= extra; + + convert_from_ordinal(a_time); +}