Add an RPN date manipulator
This commit is contained in:
		
							parent
							
								
									124df16711
								
							
						
					
					
						commit
						e59586b265
					
				| 
						 | 
					@ -25,9 +25,14 @@ then
 | 
				
			||||||
            fi
 | 
					            fi
 | 
				
			||||||
        done
 | 
					        done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case "${TEST}" in
 | 
				
			||||||
 | 
					        obj/tests/manip) TEST_EXTRAS="-lm" ;;
 | 
				
			||||||
 | 
					        *) TEST_EXTRAS="" ;;
 | 
				
			||||||
 | 
					        esac
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if [ ${MODIFIED} -ne 0 ]
 | 
					        if [ ${MODIFIED} -ne 0 ]
 | 
				
			||||||
        then
 | 
					        then
 | 
				
			||||||
            do_cmd ${CC} -Iobj ${CFLAGS} -o ${TEST} ${SRC} ${LIBS} ${EXTRAS} || return 1
 | 
					            do_cmd ${CC} -Iobj ${CFLAGS} -o ${TEST} ${SRC} ${LIBS} ${EXTRAS} ${TEST_EXTRAS} || return 1
 | 
				
			||||||
            print_success "Built ${TEST}"
 | 
					            print_success "Built ${TEST}"
 | 
				
			||||||
        else
 | 
					        else
 | 
				
			||||||
            print_success "${TEST} is up to date"
 | 
					            print_success "${TEST} is up to date"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,299 @@
 | 
				
			||||||
 | 
					/* libiso8601/src/tests/manip.c
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  (c)2007, Laurence Withers, <l@lwithers.me.uk>.
 | 
				
			||||||
 | 
					 *  Released under the GNU GPLv2. See file COPYING or
 | 
				
			||||||
 | 
					 *  http://www.gnu.org/copyleft/gpl.html for details.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "iso8601.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <math.h>
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* safe_strtod()
 | 
				
			||||||
 | 
					 *  Converts `str' into a double, which it will store in `out'. If an error occurs, returns -1,
 | 
				
			||||||
 | 
					 *  else 0 on success.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int safe_strtod(const char* str, double* out)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    char* endp = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    errno = 0;
 | 
				
			||||||
 | 
					    *out = strtod(str, &endp);
 | 
				
			||||||
 | 
					    if(errno || !endp || *endp) return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* date_stack
 | 
				
			||||||
 | 
					 *  We use a stack of dates and/or periods to implement effectively an RPN manipulator.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					enum date_stack_type {
 | 
				
			||||||
 | 
					    date_stack_type_date,
 | 
				
			||||||
 | 
					    date_stack_type_per
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct date_stack {
 | 
				
			||||||
 | 
					    enum date_stack_type type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    union {
 | 
				
			||||||
 | 
					        struct {
 | 
				
			||||||
 | 
					            struct iso8601_date d;
 | 
				
			||||||
 | 
					            struct iso8601_details det;
 | 
				
			||||||
 | 
					        }date;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        struct iso8601_elapsed per;
 | 
				
			||||||
 | 
					    }u;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct date_stack* next;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct date_stack* date_stack_top = 0;
 | 
				
			||||||
 | 
					int date_stack_size = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* date_stack_push_*()
 | 
				
			||||||
 | 
					 *  Routines for pushing an object onto the date stack.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void date_stack_push_date(const struct iso8601_date* d, const struct iso8601_details* det)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    struct date_stack* s;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s = malloc(sizeof(struct date_stack));
 | 
				
			||||||
 | 
					    s->type = date_stack_type_date;
 | 
				
			||||||
 | 
					    s->u.date.d = *d;
 | 
				
			||||||
 | 
					    s->u.date.det = *det;
 | 
				
			||||||
 | 
					    s->next = date_stack_top;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    date_stack_top = s;
 | 
				
			||||||
 | 
					    ++date_stack_size;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void date_stack_push_elapsed(const struct iso8601_elapsed* per)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    struct date_stack* s;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s = malloc(sizeof(struct date_stack));
 | 
				
			||||||
 | 
					    s->type = date_stack_type_per;
 | 
				
			||||||
 | 
					    s->u.per = *per;
 | 
				
			||||||
 | 
					    s->next = date_stack_top;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    date_stack_top = s;
 | 
				
			||||||
 | 
					    ++date_stack_size;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* date_stack_pop_date()
 | 
				
			||||||
 | 
					 *  Pops a date from the stack, returning non-0 on error (underflow or type mismatch).
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int date_stack_pop_date(struct iso8601_date* d, struct iso8601_details* det)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    struct date_stack* next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(!date_stack_top) {
 | 
				
			||||||
 | 
					        fputs("Stack underflow.\n", stderr);
 | 
				
			||||||
 | 
					        return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(date_stack_top->type != date_stack_type_date) {
 | 
				
			||||||
 | 
					        fputs("Type mismatch.\n", stderr);
 | 
				
			||||||
 | 
					        return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    *d = date_stack_top->u.date.d;
 | 
				
			||||||
 | 
					    *det = date_stack_top->u.date.det;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    --date_stack_size;
 | 
				
			||||||
 | 
					    next = date_stack_top->next;
 | 
				
			||||||
 | 
					    free(date_stack_top);
 | 
				
			||||||
 | 
					    date_stack_top = next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* date_stack_pop_elapsed()
 | 
				
			||||||
 | 
					 *  Pops an elapsed period from the stack, returning non-0 on error (underflow or type mismatch).
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int date_stack_pop_elapsed(struct iso8601_elapsed* per)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    struct date_stack* next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(!date_stack_top) {
 | 
				
			||||||
 | 
					        fputs("Stack underflow.\n", stderr);
 | 
				
			||||||
 | 
					        return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if(date_stack_top->type != date_stack_type_per) {
 | 
				
			||||||
 | 
					        fputs("Type mismatch.\n", stderr);
 | 
				
			||||||
 | 
					        return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    *per = date_stack_top->u.per;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    --date_stack_size;
 | 
				
			||||||
 | 
					    next = date_stack_top->next;
 | 
				
			||||||
 | 
					    free(date_stack_top);
 | 
				
			||||||
 | 
					    date_stack_top = next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* usage()
 | 
				
			||||||
 | 
					 *  Displays help.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void usage(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    fputs("Push a date or elapsed time onto the stack by entering it, e.g.:\n"
 | 
				
			||||||
 | 
					        "   2007-07-29      12345.67\n"
 | 
				
			||||||
 | 
					        "Operators:\n"
 | 
				
			||||||
 | 
					        "   +   (date, period)      Advance date by elapsed time, push date.\n"
 | 
				
			||||||
 | 
					        "   -   (date, period)      Regress date by elapsed time, push date.\n"
 | 
				
			||||||
 | 
					        "   <   (date, date)        Prints 1 if first date less than second.\n"
 | 
				
			||||||
 | 
					        "   <=  (date, date)        Prints 1 if first date less than or equal to second.\n"
 | 
				
			||||||
 | 
					        "   ==  (date, date)        Prints 1 if dates equal.\n"
 | 
				
			||||||
 | 
					        "   p   (date)              Prints a date.\n"
 | 
				
			||||||
 | 
					        "\n"
 | 
				
			||||||
 | 
					        "Type help for this screen at any point.\n"
 | 
				
			||||||
 | 
					    "\n", stdout);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int parse_command(char* cmd)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    struct iso8601_date date, date2;
 | 
				
			||||||
 | 
					    struct iso8601_details details, details2;
 | 
				
			||||||
 | 
					    struct iso8601_elapsed period;
 | 
				
			||||||
 | 
					    double period_d;
 | 
				
			||||||
 | 
					    char date_str[40];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cmd = strtok(cmd, " \n");
 | 
				
			||||||
 | 
					    while(cmd) {
 | 
				
			||||||
 | 
					        if(!iso8601_parse(cmd, &date, 0, &details)) {
 | 
				
			||||||
 | 
					            date_stack_push_date(&date, &details);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else if(!safe_strtod(cmd, &period_d) && period_d >= 0) {
 | 
				
			||||||
 | 
					            period.sec = trunc(period_d);
 | 
				
			||||||
 | 
					            period.nsec = trunc((period_d - period.sec) * 1e9);
 | 
				
			||||||
 | 
					            date_stack_push_elapsed(&period);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else if(!strcmp(cmd, "+")) {
 | 
				
			||||||
 | 
					            if(date_stack_pop_elapsed(&period) || date_stack_pop_date(&date, &details)) {
 | 
				
			||||||
 | 
					                ret = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                iso8601_add_elapsed(&date, &period);
 | 
				
			||||||
 | 
					                date_stack_push_date(&date, &details);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else if(!strcmp(cmd, "-")) {
 | 
				
			||||||
 | 
					            if(date_stack_pop_elapsed(&period) || date_stack_pop_date(&date, &details)) {
 | 
				
			||||||
 | 
					                ret = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                iso8601_subtract_elapsed(&date, &period);
 | 
				
			||||||
 | 
					                date_stack_push_date(&date, &details);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else if(!strcmp(cmd, "<")) {
 | 
				
			||||||
 | 
					            if(date_stack_pop_date(&date, &details) || date_stack_pop_date(&date2, &details2)) {
 | 
				
			||||||
 | 
					                ret = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                printf("%d\n", iso8601_lt(&date2, &date));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else if(!strcmp(cmd, "<=")) {
 | 
				
			||||||
 | 
					            if(date_stack_pop_date(&date, &details) || date_stack_pop_date(&date2, &details2)) {
 | 
				
			||||||
 | 
					                ret = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                printf("%d\n", iso8601_lte(&date2, &date));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else if(!strcmp(cmd, "==")) {
 | 
				
			||||||
 | 
					            if(date_stack_pop_date(&date, &details) || date_stack_pop_date(&date2, &details2)) {
 | 
				
			||||||
 | 
					                ret = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                printf("%d\n", iso8601_eq(&date2, &date));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else if(!strcmp(cmd, "p")) {
 | 
				
			||||||
 | 
					            if(date_stack_pop_date(&date, &details)) {
 | 
				
			||||||
 | 
					                ret = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                iso8601_print(date_str, sizeof(date_str), &date, &details);
 | 
				
			||||||
 | 
					                fputs(date_str, stdout);
 | 
				
			||||||
 | 
					                putc('\n', stdout);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            fputs("Unrecognised command or argument.\n", stderr);
 | 
				
			||||||
 | 
					            ret = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        cmd = strtok(0, " \n");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, char* argv[])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int ret = 0;
 | 
				
			||||||
 | 
					    char* buf = 0;
 | 
				
			||||||
 | 
					    size_t buf_len = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if(argc == 2 && !strcmp(argv[1], "--print-summary")) {
 | 
				
			||||||
 | 
					        printf("Interactive date manipulator. Also evaluates commandline.\n");
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* commandline -- evaluate arguments as though interactive */
 | 
				
			||||||
 | 
					    if(argc > 1) {
 | 
				
			||||||
 | 
					        for(ret = 1; ret < argc; ++ret) {
 | 
				
			||||||
 | 
					            if(parse_command(argv[ret])) return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    usage();
 | 
				
			||||||
 | 
					    while(!feof(stdin)) {
 | 
				
			||||||
 | 
					        printf("%d >> ", date_stack_size);
 | 
				
			||||||
 | 
					        ret = getline(&buf, &buf_len, stdin);
 | 
				
			||||||
 | 
					        if(ret == -1) break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        parse_command(buf);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* options for text editors
 | 
				
			||||||
 | 
					kate: replace-trailing-space-save true; space-indent true; tab-width 4;
 | 
				
			||||||
 | 
					vim: expandtab:ts=4:sw=4
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
		Loading…
	
		Reference in New Issue