Index: ossp-pkg/tai/tai-config.in RCS File: /v/ossp/cvs/ossp-pkg/tai/tai-config.in,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/tai/tai-config.in,v' | diff -u /dev/null - -L'ossp-pkg/tai/tai-config.in' 2>/dev/null --- ossp-pkg/tai/tai-config.in +++ - 2024-05-19 05:23:09.108147418 +0200 @@ -0,0 +1,145 @@ +#!/bin/sh +## +## OSSP tai - Time Handling +## Copyright (c) 2002 Ralf S. Engelschall +## Copyright (c) 2002 The OSSP Project +## Copyright (c) 2002 Cable & Wireless Deutschland +## +## This file is part of OSSP tai, a time handling library +## which can be found at http://www.ossp.org/pkg/lib/tai/. +## +## Permission to use, copy, modify, and distribute this software for +## any purpose with or without fee is hereby granted, provided that +## the above copyright notice and this permission notice appear in all +## copies. +## +## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED +## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +## SUCH DAMAGE. +## +## tm-config.in: OSSP tm build utility +## + +DIFS=' +' + +prefix="@prefix@" +exec_prefix="@exec_prefix@" + +tai_prefix="$prefix" +tai_exec_prefix="$exec_prefix" +tai_bindir="@bindir@" +tai_libdir="@libdir@" +tai_includedir="@includedir@" +tai_mandir="@mandir@" +tai_datadir="@datadir@" +tai_acdir="@datadir@/aclocal" +tai_cflags="@CFLAGS@" +tai_ldflags="@LDFLAGS@" +tai_libs="@LIBS@" +tai_version="@TM_VERSION_STR@" + +help=no +version=no + +usage="tm-config" +usage="$usage [--help] [--version] [--all]" +usage="$usage [--prefix] [--exec-prefix] [--bindir] [--libdir] [--includedir] [--mandir] [--datadir] [--acdir]" +usage="$usage [--cflags] [--ldflags] [--libs]" +if [ $# -eq 0 ]; then + echo "tm-config:Error: Invalid option" 1>&2 + echo "tm-config:Usage: $usage" 1>&2 + exit 1 +fi +output='' +output_extra='' +all=no +prev='' +OIFS="$IFS" IFS="$DIFS" +for option +do + if [ ".$prev" != . ]; then + eval "$prev=\$option" + prev='' + continue + fi + case "$option" in + -*=*) optarg=`echo "$option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg='' ;; + esac + case "$option" in + --help|-h) + echo "Usage: $usage" + exit 0 + ;; + --version|-v) + echo "OSSP tm $tai_version" + exit 0 + ;; + --all) + all=yes + ;; + --prefix) + output="$output $tai_prefix" + ;; + --exec-prefix) + output="$output $tai_exec_prefix" + ;; + --bindir) + output="$output $tai_bindir" + ;; + --libdir) + output="$output $tai_libdir" + ;; + --includedir) + output="$output $tai_includedir" + ;; + --mandir) + output="$output $tai_mandir" + ;; + --datadir) + output="$output $tai_datadir" + ;; + --acdir) + output="$output $tai_acdir" + ;; + --cflags) + output="$output -I$tai_includedir" + output_extra="$output_extra $tai_cflags" + ;; + --ldflags) + output="$output -L$tai_libdir" + output_extra="$output_extra $tai_ldflags" + ;; + --libs) + output="$output -ltai" + output_extra="$output_extra $tai_libs" + ;; + * ) + echo "tai-config:Error: Invalid option" 1>&2 + echo "tai-config:Usage: $usage" 1>&2 + exit 1; + ;; + esac +done +IFS="$OIFS" +if [ ".$prev" != . ]; then + echo "tai-config:Error: missing argument to --`echo $prev | sed 's/_/-/g'`" 1>&2 + exit 1 +fi +if [ ".$output" != . ]; then + if [ ".$all" = .yes ]; then + output="$output $output_extra" + fi + echo $output +fi + Index: ossp-pkg/tai/tai-config.in RCS File: /v/ossp/cvs/ossp-pkg/tai/tai-config.in,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/tai/tai-config.in,v' 2>/dev/null Index: ossp-pkg/tai/tai_format.c RCS File: /v/ossp/cvs/ossp-pkg/tai/tai_format.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/tai/tai_format.c,v' | diff -u /dev/null - -L'ossp-pkg/tai/tai_format.c' 2>/dev/null --- ossp-pkg/tai/tai_format.c +++ - 2024-05-19 05:23:09.115233886 +0200 @@ -0,0 +1,454 @@ +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if 0 +static const char elsieid[] = "@(#)strftime.c 7.38"; +static const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89"; +__FBSDID("$FreeBSD: src/lib/libc/stdtime/strftime.c,v 1.35 2002/03/22 21:53:13 obrien Exp $"); +#endif + +#include +#include +#include +#include +#include + +#include "tai.h" +#include "tai_p.h" + +#define INT_STRLEN_MAXIMUM(type) \ + ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type)) +#define TYPE_BIT(type) (sizeof(type)*8) +#define TYPE_SIGNED(type) (((type) -1) < 0) +#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) +#define DAYSPERWEEK 7 +#define DAYSPERNYEAR 365 +#define DAYSPERLYEAR 366 + +static char * _add(const char *, char *, const char *); +static char * _conv(int, const char *, char *, const char *); +static char * _fmt(const char *, const struct tm *, char *, const char *); + +#if 0 /* FIXME */ +extern char * tzname[]; +#endif + +size_t +tai_format_int( + char *const s, + const size_t maxsize, + const char *const format, + const struct tm *const t) +{ + char *p; + +#if 0 /* FIXME */ + tzset(); +#endif + p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize); + if (p == s + maxsize) + return 0; + *p = '\0'; + return p - s; +} + +static char * +_fmt( + const char *format, + const struct tm *const t, + char *pt, + const char *const ptlim) +{ + int Ealternative, Oalternative; + const tai_locale_t *tptr = &tai_locale; + + for ( ; *format; ++format) { + if (*format == '%') { + Ealternative = 0; + Oalternative = 0; +label: + switch (*++format) { + case '\0': + --format; + break; + case 'A': + pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ? + "?" : tptr->weekday[t->tm_wday], + pt, ptlim); + continue; + case 'a': + pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ? + "?" : tptr->wday[t->tm_wday], + pt, ptlim); + continue; + case 'B': + pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ? + "?" : (Oalternative ? tptr->alt_month : + tptr->month)[t->tm_mon], + pt, ptlim); + continue; + case 'b': + case 'h': + pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ? + "?" : tptr->mon[t->tm_mon], + pt, ptlim); + continue; + case 'C': + /* + ** %C used to do a... + ** _fmt("%a %b %e %X %Y", t); + ** ...whereas now POSIX 1003.2 calls for + ** something completely different. + ** (ado, 5/24/93) + */ + pt = _conv((t->tm_year + TM_YEAR_BASE) / 100, + "%02d", pt, ptlim); + continue; + case 'c': + pt = _fmt(tptr->c_fmt, t, pt, ptlim); + continue; + case 'D': + pt = _fmt("%m/%d/%y", t, pt, ptlim); + continue; + case 'd': + pt = _conv(t->tm_mday, "%02d", pt, ptlim); + continue; + case 'E': + if (Ealternative || Oalternative) + break; + Ealternative++; + goto label; + case 'O': + /* + ** POSIX locale extensions, a la + ** Arnold Robbins' strftime version 3.0. + ** The sequences + ** %Ec %EC %Ex %EX %Ey %EY + ** %Od %oe %OH %OI %Om %OM + ** %OS %Ou %OU %OV %Ow %OW %Oy + ** are supposed to provide alternate + ** representations. + ** (ado, 5/24/93) + ** + ** FreeBSD extensions + ** %OB %Ef %EF + */ + if (Ealternative || Oalternative) + break; + Oalternative++; + goto label; + case 'e': + pt = _conv(t->tm_mday, "%2d", pt, ptlim); + continue; + case 'F': + pt = _fmt("%Y-%m-%d", t, pt, ptlim); + continue; + case 'H': + pt = _conv(t->tm_hour, "%02d", pt, ptlim); + continue; + case 'I': + pt = _conv((t->tm_hour % 12) ? + (t->tm_hour % 12) : 12, + "%02d", pt, ptlim); + continue; + case 'j': + pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim); + continue; + case 'k': + /* + ** This used to be... + ** _conv(t->tm_hour % 12 ? + ** t->tm_hour % 12 : 12, 2, ' '); + ** ...and has been changed to the below to + ** match SunOS 4.1.1 and Arnold Robbins' + ** strftime version 3.0. That is, "%k" and + ** "%l" have been swapped. + ** (ado, 5/24/93) + */ + pt = _conv(t->tm_hour, "%2d", pt, ptlim); + continue; +#ifdef KITCHEN_SINK + case 'K': + /* + ** After all this time, still unclaimed! + */ + pt = _add("kitchen sink", pt, ptlim); + continue; +#endif /* defined KITCHEN_SINK */ + case 'l': + /* + ** This used to be... + ** _conv(t->tm_hour, 2, ' '); + ** ...and has been changed to the below to + ** match SunOS 4.1.1 and Arnold Robbin's + ** strftime version 3.0. That is, "%k" and + ** "%l" have been swapped. + ** (ado, 5/24/93) + */ + pt = _conv((t->tm_hour % 12) ? + (t->tm_hour % 12) : 12, + "%2d", pt, ptlim); + continue; + case 'M': + pt = _conv(t->tm_min, "%02d", pt, ptlim); + continue; + case 'm': + pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim); + continue; + case 'n': + pt = _add("\n", pt, ptlim); + continue; + case 'p': + pt = _add((t->tm_hour >= 12) ? + tptr->pm : + tptr->am, + pt, ptlim); + continue; + case 'R': + pt = _fmt("%H:%M", t, pt, ptlim); + continue; + case 'r': + pt = _fmt(tptr->ampm_fmt, t, pt, ptlim); + continue; + case 'S': + pt = _conv(t->tm_sec, "%02d", pt, ptlim); + continue; + case 's': + { + struct tm tm; + char buf[INT_STRLEN_MAXIMUM( + time_t) + 1]; + time_t mkt; + + tm = *t; + mkt = mktime(&tm); + if (TYPE_SIGNED(time_t)) + (void) sprintf(buf, "%ld", + (long) mkt); + else (void) sprintf(buf, "%lu", + (unsigned long) mkt); + pt = _add(buf, pt, ptlim); + } + continue; + case 'T': + pt = _fmt("%H:%M:%S", t, pt, ptlim); + continue; + case 't': + pt = _add("\t", pt, ptlim); + continue; + case 'U': + pt = _conv((t->tm_yday + 7 - t->tm_wday) / 7, + "%02d", pt, ptlim); + continue; + case 'u': + /* + ** From Arnold Robbins' strftime version 3.0: + ** "ISO 8601: Weekday as a decimal number + ** [1 (Monday) - 7]" + ** (ado, 5/24/93) + */ + pt = _conv((t->tm_wday == 0) ? 7 : t->tm_wday, + "%d", pt, ptlim); + continue; + case 'V': /* ISO 8601 week number */ + case 'G': /* ISO 8601 year (four digits) */ + case 'g': /* ISO 8601 year (two digits) */ +/* +** From Arnold Robbins' strftime version 3.0: "the week number of the +** year (the first Monday as the first day of week 1) as a decimal number +** (01-53)." +** (ado, 1993-05-24) +** +** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn: +** "Week 01 of a year is per definition the first week which has the +** Thursday in this year, which is equivalent to the week which contains +** the fourth day of January. In other words, the first week of a new year +** is the week which has the majority of its days in the new year. Week 01 +** might also contain days from the previous year and the week before week +** 01 of a year is the last week (52 or 53) of the previous year even if +** it contains days from the new year. A week starts with Monday (day 1) +** and ends with Sunday (day 7). For example, the first week of the year +** 1997 lasts from 1996-12-30 to 1997-01-05..." +** (ado, 1996-01-02) +*/ + { + int year; + int yday; + int wday; + int w; + + year = t->tm_year + TM_YEAR_BASE; + yday = t->tm_yday; + wday = t->tm_wday; + for ( ; ; ) { + int len; + int bot; + int top; + + len = isleap(year) ? + DAYSPERLYEAR : + DAYSPERNYEAR; + /* + ** What yday (-3 ... 3) does + ** the ISO year begin on? + */ + bot = ((yday + 11 - wday) % + DAYSPERWEEK) - 3; + /* + ** What yday does the NEXT + ** ISO year begin on? + */ + top = bot - + (len % DAYSPERWEEK); + if (top < -3) + top += DAYSPERWEEK; + top += len; + if (yday >= top) { + ++year; + w = 1; + break; + } + if (yday >= bot) { + w = 1 + ((yday - bot) / + DAYSPERWEEK); + break; + } + --year; + yday += isleap(year) ? + DAYSPERLYEAR : + DAYSPERNYEAR; + } +#ifdef XPG4_1994_04_09 + if ((w == 52 + && t->tm_mon == TM_JANUARY) + || (w == 1 + && t->tm_mon == TM_DECEMBER)) + w = 53; +#endif /* defined XPG4_1994_04_09 */ + if (*format == 'V') + pt = _conv(w, "%02d", + pt, ptlim); + else if (*format == 'g') { + pt = _conv(year % 100, "%02d", + pt, ptlim); + } else pt = _conv(year, "%04d", + pt, ptlim); + } + continue; + case 'v': + /* + ** From Arnold Robbins' strftime version 3.0: + ** "date as dd-bbb-YYYY" + ** (ado, 5/24/93) + */ + pt = _fmt("%e-%b-%Y", t, pt, ptlim); + continue; + case 'W': + pt = _conv((t->tm_yday + 7 - + (t->tm_wday ? + (t->tm_wday - 1) : 6)) / 7, + "%02d", pt, ptlim); + continue; + case 'w': + pt = _conv(t->tm_wday, "%d", pt, ptlim); + continue; + case 'X': + pt = _fmt(tptr->X_fmt, t, pt, ptlim); + continue; + case 'x': + pt = _fmt(tptr->x_fmt, t, pt, ptlim); + continue; + case 'y': + pt = _conv((t->tm_year + TM_YEAR_BASE) % 100, + "%02d", pt, ptlim); + continue; + case 'Y': + pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d", + pt, ptlim); + continue; + case 'Z': + if (t->tm_zone != NULL) + pt = _add(t->tm_zone, pt, ptlim); + else +#if 0 /* FIXME: strftime("%Z") or keep "?" */ + if (t->tm_isdst == 0 || t->tm_isdst == 1) { + pt = _add(tzname[t->tm_isdst], + pt, ptlim); + } else +#endif + pt = _add("?", pt, ptlim); + continue; + case 'z': + { + long absoff; + if (t->tm_gmtoff >= 0) { + absoff = t->tm_gmtoff; + pt = _add("+", pt, ptlim); + } else { + absoff = -t->tm_gmtoff; + pt = _add("-", pt, ptlim); + } + pt = _conv(absoff / 3600, "%02d", + pt, ptlim); + pt = _conv((absoff % 3600) / 60, "%02d", + pt, ptlim); + }; + continue; + case '+': + pt = _fmt(tptr->date_fmt, t, pt, ptlim); + continue; + case '%': + /* + * X311J/88-090 (4.12.3.5): if conversion char is + * undefined, behavior is undefined. Print out the + * character itself as printf(3) also does. + */ + default: + break; + } + } + if (pt == ptlim) + break; + *pt++ = *format; + } + return pt; +} + +static char * +_conv(n, format, pt, ptlim) + const int n; + const char *const format; + char *const pt; + const char *const ptlim; +{ + char buf[INT_STRLEN_MAXIMUM(int) + 1]; + + (void) sprintf(buf, format, n); + return _add(buf, pt, ptlim); +} + +static char * +_add(str, pt, ptlim) + const char *str; + char *pt; + const char *const ptlim; +{ + while (pt < ptlim && (*pt = *str++) != '\0') + ++pt; + return pt; +} + Index: ossp-pkg/tai/tai_format.c RCS File: /v/ossp/cvs/ossp-pkg/tai/tai_format.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/tai/tai_format.c,v' 2>/dev/null Index: ossp-pkg/tai/ts.c RCS File: /v/ossp/cvs/ossp-pkg/tai/ts.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/tai/ts.c,v' | diff -u /dev/null - -L'ossp-pkg/tai/ts.c' 2>/dev/null --- ossp-pkg/tai/ts.c +++ - 2024-05-19 05:23:09.122267973 +0200 @@ -0,0 +1,468 @@ +/* +** TS - OSSP Test Suite Library +** Copyright (c) 2001-2002 Ralf S. Engelschall +** Copyright (c) 2001-2002 The OSSP Project +** Copyright (c) 2001-2002 Cable & Wireless Deutschland +** +** This file is part of OSSP TS, a small test suite library which +** can be found at http://www.ossp.org/pkg/ts/. +** +** Permission to use, copy, modify, and distribute this software for +** any purpose with or without fee is hereby granted, provided that +** the above copyright notice and this permission notice appear in all +** copies. +** +** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED +** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR +** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +** +** ts.c: test suite library +*/ + +#include +#include +#include +#include + +#include "config.h" +#if defined(HAVE_DMALLOC_H) && defined(WITH_DMALLOC) +#include "dmalloc.h" +#endif + +#include "ts.h" + +/* embedded ring data structure library */ +#define RING_ENTRY(elem) \ + struct { elem *next; elem *prev; } +#define RING_HEAD(elem) \ + struct { elem *next; elem *prev; } +#define RING_SENTINEL(hp, elem, link) \ + (elem *)((char *)(hp) - ((size_t)(&((elem *)0)->link))) +#define RING_FIRST(hp) \ + (hp)->next +#define RING_LAST(hp) \ + (hp)->prev +#define RING_NEXT(ep, link) \ + (ep)->link.next +#define RING_PREV(ep, link) \ + (ep)->link.prev +#define RING_INIT(hp, elem, link) \ + do { RING_FIRST((hp)) = RING_SENTINEL((hp), elem, link); \ + RING_LAST((hp)) = RING_SENTINEL((hp), elem, link); } while (0) +#define RING_EMPTY(hp, elem, link) \ + (RING_FIRST((hp)) == RING_SENTINEL((hp), elem, link)) +#define RING_ELEM_INIT(ep, link) \ + do { RING_NEXT((ep), link) = (ep); \ + RING_PREV((ep), link) = (ep); } while (0) +#define RING_SPLICE_BEFORE(lep, ep1, epN, link) \ + do { RING_NEXT((epN), link) = (lep); \ + RING_PREV((ep1), link) = RING_PREV((lep), link); \ + RING_NEXT(RING_PREV((lep), link), link) = (ep1); \ + RING_PREV((lep), link) = (epN); } while (0) +#define RING_SPLICE_TAIL(hp, ep1, epN, elem, link) \ + RING_SPLICE_BEFORE(RING_SENTINEL((hp), elem, link), (ep1), (epN), link) +#define RING_INSERT_TAIL(hp, nep, elem, link) \ + RING_SPLICE_TAIL((hp), (nep), (nep), elem, link) +#define RING_FOREACH(ep, hp, elem, link) \ + for ((ep) = RING_FIRST((hp)); \ + (ep) != RING_SENTINEL((hp), elem, link); \ + (ep) = RING_NEXT((ep), link)) + +/* test suite test log */ +struct tstl_st; +typedef struct tstl_st tstl_t; +struct tstl_st { + RING_ENTRY(tstl_t) next; + char *text; + const char *file; + int line; +}; + +/* test suite test check */ +struct tstc_st; +typedef struct tstc_st tstc_t; +struct tstc_st { + RING_ENTRY(tstc_t) next; + char *title; + int failed; + const char *file; + int line; + RING_HEAD(tstl_t) logs; +}; + +/* test suite test */ +struct ts_test_st { + RING_ENTRY(ts_test_t) next; + char *title; + ts_test_cb_t func; + const char *file; + int line; + RING_HEAD(tstc_t) checks; +}; + +/* test suite */ +struct ts_suite_st { + char *title; + RING_HEAD(ts_test_t) tests; +}; + +/* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only */ +static int ts_suite_mvxprintf(char *buffer, size_t bufsize, const char *format, va_list ap) +{ + /* sufficient integer buffer: x log_10(2) + safety */ + char ibuf[((sizeof(int)*8)/3)+10]; + char *cp; + char c; + int d; + int n; + int bytes; + + if (format == NULL || ap == NULL) + return -1; + bytes = 0; + while (*format != '\0') { + if (*format == '%') { + c = *(format+1); + if (c == '%') { + /* expand "%%" */ + cp = &c; + n = sizeof(char); + } + else if (c == 'c') { + /* expand "%c" */ + c = (char)va_arg(ap, int); + cp = &c; + n = sizeof(char); + } + else if (c == 's') { + /* expand "%s" */ + if ((cp = (char *)va_arg(ap, char *)) == NULL) + cp = "(null)"; + n = strlen(cp); + } + else if (c == 'd') { + /* expand "%d" */ + d = (int)va_arg(ap, int); +#ifdef HAVE_SNPRINTF + snprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */ +#else + sprintf(ibuf, "%d", d); /* implicitly secure */ +#endif + cp = ibuf; + n = strlen(cp); + } + else { + /* any other "%X" */ + cp = (char *)format; + n = 2; + } + format += 2; + } + else { + /* plain text */ + cp = (char *)format; + if ((format = strchr(cp, '%')) == NULL) + format = strchr(cp, '\0'); + n = format - cp; + } + /* perform output operation */ + if (buffer != NULL) { + if (n > bufsize) + return -1; + memcpy(buffer, cp, n); + buffer += n; + bufsize -= n; + } + bytes += n; + } + /* nul-terminate output */ + if (buffer != NULL) { + if (bufsize == 0) + return -1; + *buffer = '\0'; + } + return bytes; +} + +/* minimal vasprintf(3) variant which supports %{c,s,d} only */ +static char *ts_suite_mvasprintf(const char *format, va_list ap) +{ + char *buffer; + int n; + va_list ap2; + + if (format == NULL || ap == NULL) + return NULL; + ap2 = ap; + if ((n = ts_suite_mvxprintf(NULL, 0, format, ap)) == -1) + return NULL; + if ((buffer = (char *)malloc(n+1)) == NULL) + return NULL; + ts_suite_mvxprintf(buffer, n+1, format, ap2); + return buffer; +} + +/* minimal asprintf(3) variant which supports %{c,s,d} only */ +static char *ts_suite_masprintf(const char *format, ...) +{ + va_list ap; + char *cp; + + va_start(ap, format); + cp = ts_suite_mvasprintf(format, ap); + va_end(ap); + return cp; +} + +/* create test suite */ +ts_suite_t *ts_suite_new(const char *fmt, ...) +{ + ts_suite_t *ts; + va_list ap; + + if ((ts = (ts_suite_t *)malloc(sizeof(ts_suite_t))) == NULL) + return NULL; + va_start(ap, fmt); + ts->title = ts_suite_mvasprintf(fmt, ap); + RING_INIT(&ts->tests, ts_test_t, next); + va_end(ap); + return ts; +} + +/* add test case to test suite */ +void ts_suite_test(ts_suite_t *ts, ts_test_cb_t func, const char *fmt, ...) +{ + ts_test_t *tst; + va_list ap; + + if (ts == NULL || func == NULL || fmt == NULL) + return; + if ((tst = (ts_test_t *)malloc(sizeof(ts_test_t))) == NULL) + return; + RING_ELEM_INIT(tst, next); + va_start(ap, fmt); + tst->title = ts_suite_mvasprintf(fmt, ap); + va_end(ap); + tst->func = func; + tst->file = NULL; + tst->line = 0; + RING_INIT(&tst->checks, tstc_t, next); + RING_INSERT_TAIL(&ts->tests, tst, ts_test_t, next); + return; +} + +/* run test suite */ +int ts_suite_run(ts_suite_t *ts) +{ + ts_test_t *tst; + tstc_t *tstc; + tstl_t *tstl; + int total_tests, total_tests_suite_failed; + int total_checks, total_checks_failed; + int test_checks, test_checks_failed; + const char *file; + int line; + char *cp; + + if (ts == NULL) + return 0; + + /* init total counters */ + total_tests = 0; + total_tests_suite_failed = 0; + total_checks = 0; + total_checks_failed = 0; + + fprintf(stdout, "\n"); + fprintf(stdout, " Test Suite: %s\n", ts->title); + fprintf(stdout, " __________________________________________________________________\n"); + fprintf(stdout, "\n"); + fflush(stdout); + + /* iterate through all test cases */ + RING_FOREACH(tst, &ts->tests, ts_test_t, next) { + cp = ts_suite_masprintf(" Test: %s ........................................" + "........................................", tst->title); + cp[60] = '\0'; + fprintf(stdout, "%s", cp); + free(cp); + fflush(stdout); + + /* init test case counters */ + test_checks = 0; + test_checks_failed = 0; + + /* run the test case function */ + tst->func(tst); + + /* iterate through all performed checks to determine status */ + RING_FOREACH(tstc, &tst->checks, tstc_t, next) { + test_checks++; + if (tstc->failed) + test_checks_failed++; + } + + if (test_checks_failed > 0) { + /* some checks failed, so do detailed reporting of test case */ + fprintf(stdout, " FAILED\n"); + fprintf(stdout, " Ops, %d/%d checks failed! Detailed report follows:\n", + test_checks_failed, test_checks); + RING_FOREACH(tstc, &tst->checks, tstc_t, next) { + file = (tstc->file != NULL ? tstc->file : tst->file); + line = (tstc->line != 0 ? tstc->line : tst->line); + if (file != NULL) + fprintf(stdout, " Check: %s [%s:%d]\n", tstc->title, file, line); + else + fprintf(stdout, " Check: %s\n", tstc->title); + RING_FOREACH(tstl, &tstc->logs, tstl_t, next) { + file = (tstl->file != NULL ? tstl->file : file); + line = (tstl->line != 0 ? tstl->line : line); + if (file != NULL) + fprintf(stdout, " Log: %s [%s:%d]\n", tstl->text, file, line); + else + fprintf(stdout, " Log: %s\n", tstl->text); + } + } + } + else { + /* test case ran successfully */ + fprintf(stdout, ".... OK\n"); + } + fflush(stdout); + + /* accumulate counters */ + total_checks += test_checks; + total_tests++; + if (test_checks_failed > 0) { + total_checks_failed += test_checks_failed; + total_tests_suite_failed++; + } + } + + /* print test suite summary */ + fprintf(stdout, " __________________________________________________________________\n"); + fprintf(stdout, "\n"); + fprintf(stdout, " Test Summary: %d tests (%d ok, %d failed), %d checks (%d ok, %d failed)\n", + total_tests, (total_tests - total_tests_suite_failed), total_tests_suite_failed, + total_checks, (total_checks - total_checks_failed), total_checks_failed); + if (total_tests_suite_failed > 0) + fprintf(stdout, " Test Suite: FAILED\n"); + else + fprintf(stdout, " Test Suite: OK\n"); + fprintf(stdout, "\n"); + fflush(stdout); + + return total_checks_failed; +} + +/* destroy test suite */ +void ts_suite_free(ts_suite_t *ts) +{ + ts_test_t *tst; + tstc_t *tstc; + tstl_t *tstl; + + if (ts == NULL) + return; + RING_FOREACH(tst, &ts->tests, ts_test_t, next) { + RING_FOREACH(tstc, &tst->checks, tstc_t, next) { + RING_FOREACH(tstl, &tstc->logs, tstl_t, next) { + free(tstl->text); + } + free(tstc->title); + free(tstc); + } + free(tst->title); + free(tst); + } + free(ts->title); + free(ts); + return; +} + +/* annotate test case with file name and line number */ +ts_test_t *ts_test_ctx(ts_test_t *tst, const char *file, int line) +{ + if (tst != NULL && file != NULL) { + tst->file = file; + tst->line = line; + } + return tst; +} + +/* annotate test case with check */ +void ts_test_check(ts_test_t *tst, const char *fmt, ...) +{ + tstc_t *tstc; + va_list ap; + + if (tst == NULL || fmt == NULL) + return; + if ((tstc = (tstc_t *)malloc(sizeof(tstc_t))) == NULL) + return; + va_start(ap, fmt); + RING_ELEM_INIT(tstc, next); + tstc->title = ts_suite_mvasprintf(fmt, ap); + tstc->failed = 0; + tstc->file = tst->file; + tstc->line = tst->line; + RING_INIT(&tstc->logs, tstl_t, next); + RING_INSERT_TAIL(&tst->checks, tstc, tstc_t, next); + va_end(ap); + return; +} + +/* annotate test case with log message and failure */ +void ts_test_fail(ts_test_t *tst, const char *fmt, ...) +{ + tstc_t *tstc; + tstl_t *tstl; + va_list ap; + + if (tst == NULL || fmt == NULL) + return; + if ((tstl = (tstl_t *)malloc(sizeof(tstl_t))) == NULL) + return; + va_start(ap, fmt); + tstl->text = ts_suite_mvasprintf(fmt, ap); + tstl->file = tst->file; + tstl->line = tst->line; + RING_ELEM_INIT(tstl, next); + tstc = RING_LAST(&tst->checks); + RING_INSERT_TAIL(&tstc->logs, tstl, tstl_t, next); + tstc->failed = 1; + va_end(ap); + return; +} + +/* annotate test case with log message only */ +void ts_test_log(ts_test_t *tst, const char *fmt, ...) +{ + tstc_t *tstc; + tstl_t *tstl; + va_list ap; + + if (tst == NULL || fmt == NULL) + return; + if ((tstl = (tstl_t *)malloc(sizeof(tstl_t))) == NULL) + return; + va_start(ap, fmt); + tstl->text = ts_suite_mvasprintf(fmt, ap); + tstl->file = tst->file; + tstl->line = tst->line; + RING_ELEM_INIT(tstl, next); + tstc = RING_LAST(&tst->checks); + RING_INSERT_TAIL(&tstc->logs, tstl, tstl_t, next); + va_end(ap); + return; +} + Index: ossp-pkg/tai/ts.c RCS File: /v/ossp/cvs/ossp-pkg/tai/ts.c,v rcsdiff -q -kk '-r1.1' '-r1.1.1.1' -u '/v/ossp/cvs/ossp-pkg/tai/ts.c,v' 2>/dev/null