// This may look like C code, but it is really -*- C++ -*-
// 
// <copyright> 
//  
//  Copyright (c) 1996
//  Institute for Information Processing and Computer Supported New Media (IICM), 
//  Graz University of Technology, Austria. 
//  
// </copyright> 
// 
// 
// <file> 
// 
// Name:        rfc1123.h
// 
// Purpose:     
// 
// Created:     12 Sep 1996   Joerg Faschingbauer
// 
// Modified:    
// 
// Description: 
// 
// $Id: robustdate.C,v 1.4 1997/01/29 16:35:06 gorasche Exp $
// 
// $Log: robustdate.C,v $
// Revision 1.4  1997/01/29 16:35:06  gorasche
// minor changes for Win32
// - types.h for bool with MSVC++
// - strncasecmp define
//
// Revision 1.3  1996/12/09 12:56:48  jfasch
// bug fix: tm month [0-11] instead of [1-12]
//
// Revision 1.2  1996/11/27 17:01:51  tvollmer
// robustdate.h
//
// Revision 1.1  1996/11/27 16:27:43  tvollmer
// Initial revision
//
// Revision 1.1  1996/11/27 13:03:33  tvollmer
// Initial revision
//
// 
// </file> 
#include "robustdate.h"

#include <string.h>
#include <ctype.h>

#ifdef WIN32
#define strncasecmp _strnicmp
#endif

// --------------------------------------------------------------------
const char* TMDate :: version1 = "TMDate: $Id: robustdate.C,v 1.4 1997/01/29 16:35:06 gorasche Exp $" ;

TMDate :: TMDate() {
   ::memset (this, 0, sizeof(tm)) ;
   errpos_ = -1 ;
}
// --------------------------------------------------------------------
const char* DateUtils :: version1 = "DateUtils: $Id: robustdate.C,v 1.4 1997/01/29 16:35:06 gorasche Exp $" ;

Verbose DateUtils :: verbose ;

int DateUtils::wkday(const char* s, int len,tm& tm) {
   if (len < 3) {
      DEBUGNL ("DateUtils::wkday(): insufficient number of characters left") ;
      return 0 ;
   }

   if (! strncasecmp (s, "mon", 3)) tm.tm_wday = Mon ;
   else if (! strncasecmp (s, "tue", 3)) tm.tm_wday = Tue ;
   else if (! strncasecmp (s, "wed", 3)) tm.tm_wday = Wed ;
   else if (! strncasecmp (s, "thu", 3)) tm.tm_wday = Thu ;
   else if (! strncasecmp (s, "fri", 3)) tm.tm_wday = Fri ;
   else if (! strncasecmp (s, "sat", 3)) tm.tm_wday = Sat ;
   else if (! strncasecmp (s, "sun", 3)) tm.tm_wday = Sun ;
   else {
      DEBUGNL ("DateUtils::wkday(): invalid weekday") ;
      return 0 ;
   }
   return 3 ;
}
int DateUtils::weekday(const char* s, int len,tm& tm) {
    if (len < 6)   // Friday
    {
      DEBUGNL ("DateUtils::weekday(): insufficient number of characters left") ;
      return 0 ;
    }

    if (! strncasecmp (s, "monday", 6)) 
        {tm.tm_wday = Mon ; return 6;}
    else if (! strncasecmp (s, "tuesday", 7))
        {tm.tm_wday = Tue ; return 7;}
    else if (! strncasecmp (s, "wednesday", 9)) 
        { tm.tm_wday = Wed ; return 9;} 
    else if (! strncasecmp (s, "thursday", 8)) 
        { tm.tm_wday = Thu ; return 8;}
    else if (! strncasecmp (s, "friday", 6))
        {tm.tm_wday = Fri ; return 6; }
    else if (! strncasecmp (s, "saturday", 8))
        { tm.tm_wday = Sat ; return 8;}
    else if (! strncasecmp (s, "sunday", 6))
        { tm.tm_wday = Sun ; return 6; }
   else {
      DEBUGNL ("DateUtils::weekday(): invalid weekday") ;
      return 0 ;
   }
}

int DateUtils :: date1(const char* s, int len,tm& tm) {
   int curlen = len ;
   int oldlen ;
   int consumed ;

   // consume and convert mday
   tm.tm_mday = 0 ;
   char c ;
   oldlen = curlen ;
   while (curlen  &&  isdigit (c = *s)) {
      incr_int_(tm.tm_mday, c) ;
      s++ ;
      curlen-- ;
   }

   if (! curlen) {
      DEBUGNL ("DateUtils::date1(): unexpected end of input after day") ;
      return -len ;
   }
   if (oldlen  == curlen) {
      DEBUGNL ("DateUtils::date1(): invalid date format: day must be digits") ;
      return -(len - curlen) ;
   }

   while (curlen && isspace (*s)) { s++; curlen--; }
   if (! curlen) {
      DEBUGNL ("DateUtils::date1(): unexpected end of input in date after day") ;
      return -len ;
   }

   if ((consumed = month(s, curlen,tm)) <= 0)
      return -(len - curlen + (-consumed)) ;

   s += consumed ;
   curlen -= consumed ;
   
   while (curlen && isspace (*s)) { s++; curlen--; }
   if (! curlen) {
      DEBUGNL ("DateUtils::date1(): unexpected end of input in date after month") ;
      return -len ;
   }

   tm.tm_year = 0 ;
   char y ;
   oldlen = curlen ;
   while (curlen  &&  isdigit (y = *s)) {
      incr_int_(tm.tm_year, y) ;
      s++ ;
      curlen-- ;
   }
   
   if (oldlen  == curlen) {
      DEBUGNL ("DateUtils::date1(): invalid date format: year must be digits") ;
      return -(len - curlen) ;
   }

   if (tm.tm_year <= 1900) {
      DEBUGNL ("DateUtils::date1(): invalid year, must be greater than 1900") ;
      return -(len - curlen) ;
   }
   tm.tm_year -= 1900 ;

   return len - curlen ;
}

int DateUtils :: date2(const char* s, int len,tm& tm) {
   int curlen = len ;
   int oldlen ;
   int consumed ;

   // consume and convert mday
   tm.tm_mday = 0 ;
   char c ;
   oldlen = curlen ;
   while (curlen  &&  isdigit (c = *s)) {
      incr_int_(tm.tm_mday, c) ;
      s++ ;
      curlen-- ;
   }

   if (! curlen) {
      DEBUGNL ("DateUtils::date2(): unexpected end of input after day") ;
      return -len ;
   }
   if (oldlen  == curlen) {
      DEBUGNL ("DateUtils::date2(): invalid date format: day must be digits") ;
      return -(len - curlen) ;
   }
   
   
   if (*s != '-')
   {
       DEBUGNL("DateUtils::date2(): no - after day");
       return -(len - curlen);
   }
   else
   {
       s++;
       curlen--;
   }
   if (! curlen) {
      DEBUGNL ("DateUtils::date2(): unexpected end of input after first -") ;
      return -len ;
   }
       
   if ((consumed = month(s, curlen,tm)) <= 0)
      return -(len - curlen + (-consumed)) ;

   s += consumed ;
   curlen -= consumed ;
   
   if (*s !='-')
   {
       DEBUGNL("DateUtils::date2(): no - after month");
       return - (len -curlen);
   }
   else
   {
       s++;
       curlen--;
   }
   if (! curlen) {
      DEBUGNL ("DateUtils::date2(): unexpected end of input after first -") ;
      return -len ;
   }
   
   tm.tm_year = 0 ;
   char y ;
   oldlen = curlen ;
   while (curlen  &&  isdigit (y = *s)) {
      incr_int_(tm.tm_year, y) ;
      s++ ;
      curlen-- ;
   }
   
   if (oldlen  == curlen) {
      DEBUGNL ("DateUtils::date2(): invalid date format: year must be digits") ;
      return -(len - curlen) ;
   }

//   if (tm.tm_year <= 1900) {
//      DEBUGNL ("DateUtils::date2(): invalid year, must be greater than 1900") ;
//      return -(len - curlen) ;
//   }
//   tm.tm_year -= 1900 ;

   return len - curlen ;
}

int DateUtils :: date3(const char* s, int len,tm& tm) 
{
   int curlen = len ;
   int oldlen ;
   int consumed ;

   
   if ((consumed = month(s, curlen,tm)) <= 0)
       return -(len - curlen + (-consumed)) ;
 
   s += consumed ;
   curlen -= consumed ;
   
   while (curlen && isspace (*s)) { s++; curlen--; }
   if (! curlen) {
       DEBUGNL ("DateUtils::date3(): unexpected end of input in date after month") ;
       return -len ;
   }
   
   // consume and convert mday
   tm.tm_mday = 0 ;
   char c ;
   oldlen=curlen;
   while (curlen  &&  isdigit (c = *s)) {
      incr_int_(tm.tm_mday, c) ;
      s++ ;
      curlen-- ;
   }
   if (oldlen  == curlen) {
      DEBUGNL ("DateUtils::date3(): invalid date format: day must be digits") ;
      return -(len - curlen) ;
   }
   return len - curlen ;
}

int DateUtils :: time(const char* s, int len, tm& tm) {
   int curlen = len ;
   int oldlen = len ;
   char h ;
   tm.tm_hour = 0 ;
   while (curlen  &&  isdigit(h = *s)) {
      incr_int_(tm.tm_hour, h) ;
      s++ ;
      curlen-- ;
   }

   if (oldlen == curlen) {
      DEBUGNL ("DateUtils::time(): error before hour") ;
      return -(len - curlen) ;
   }

   if (! curlen) {
      DEBUGNL ("DateUtils::time(): unexpected end of input after hour") ;
      return -len ;
   }

   if (*s != ':') {
      DEBUGNL ("DateUtils::time(): ':' expected after hour") ;
      return -(len - curlen) ;
   }
   else {
      s++ ;
      curlen-- ;
   }

   if (! curlen) {
      DEBUGNL ("DateUtils::time() unexpected end of input after ':' after hour") ;
      return -len ;
   }

   oldlen = curlen ;
   char m ;
   tm.tm_min = 0 ;
   while (curlen  &&  isdigit (m = *s)) {
      incr_int_(tm.tm_min, m) ;
      s++ ;
      curlen-- ;
   }

   if (curlen == oldlen) {
      DEBUGNL ("DateUtils::time(): minute must be digits") ;
      return -(len - curlen) ;
   }

   if (! curlen) {
      DEBUGNL ("DateUtils::time(): unexpected end of input after minute") ;
      return -len ;
   }
   
   if (*s != ':') {
      DEBUGNL ("DateUtils::time(): ':' expected after minute") ;
      return -(len - curlen) ;
   }
   else {
      s++ ;
      curlen-- ;
   }

   if (! curlen) {
      DEBUGNL ("DateUtils::time(): unexpected end of input after ':' after minute") ;
      return -len ;
   }

   oldlen = curlen ;
   char sec ;
   tm.tm_sec = 0 ;
   while (curlen  &&  isdigit (sec = *s)) {
      incr_int_(tm.tm_sec, sec) ;
      s++ ;
      curlen-- ;
   }
   
   if (curlen == oldlen) {
      DEBUGNL ("DateUtils::time(): second must be digits") ;
      return -(len - curlen) ;
   }

   return len - curlen ;
}

int DateUtils :: month(const char* s, int len, tm& tm) {
   if (len < 3) {
      DEBUGNL ("DateUtils::month(): no more 3 characters left for month") ;
      return 0 ;
   }
   if (! strncasecmp (s, "jan", 3)) tm.tm_mon = Jan ;
   else if (! strncasecmp (s, "feb", 3)) tm.tm_mon = Feb ;
   else if (! strncasecmp (s, "mar", 3)) tm.tm_mon = Mar ;
   else if (! strncasecmp (s, "apr", 3)) tm.tm_mon = Apr ;
   else if (! strncasecmp (s, "may", 3)) tm.tm_mon = May ;
   else if (! strncasecmp (s, "jun", 3)) tm.tm_mon = Jun ;
   else if (! strncasecmp (s, "jul", 3)) tm.tm_mon = Jul ;
   else if (! strncasecmp (s, "aug", 3)) tm.tm_mon = Aug ;
   else if (! strncasecmp (s, "sep", 3)) tm.tm_mon = Sep ;
   else if (! strncasecmp (s, "oct", 3)) tm.tm_mon = Oct ;
   else if (! strncasecmp (s, "nov", 3)) tm.tm_mon = Nov ;
   else if (! strncasecmp (s, "dec", 3)) tm.tm_mon = Dec ;
   else {
      DEBUGNL ("DateUtils::month(): invalid month") ;
      return 0 ;
   }
   
   return 3 ;
}


int DateUtils :: date4(const char* s, int len,tm& tm) {
   int curlen = len ;
   int oldlen ;

   // consume and convert mday
   tm.tm_mday = 0 ;
   char c ;
   oldlen = curlen ;
   while (curlen  &&  isdigit (c = *s)) {
      incr_int_(tm.tm_year, c) ;
      s++ ;
      curlen-- ;
   }

   if (! curlen) {
      DEBUGNL ("DateUtils::date4(): unexpected end of input after day") ;
      return -len ;
   }
   if (oldlen  == curlen) {
      DEBUGNL ("DateUtils::date4(): invalid date format: day must be digits") ;
      return -(len - curlen) ;
   }
   
   
   if (*s != '/')
   {
       DEBUGNL("DateUtils::date4(): no / after year");
       return -(len - curlen);
   }
   else
   {
       s++;
       curlen--;
   }
   if (! curlen) {
      DEBUGNL ("DateUtils::date2(): unexpected end of input after first /") ;
      return -len ;
   }
       
   oldlen = curlen ;
   while (curlen  &&  isdigit (c = *s)) {
      incr_int_(tm.tm_mon, c) ;
      s++ ;
      curlen-- ;
   }
   tm.tm_mon-- ; // to acommodate the 0-11 range of tm's month

   if (! curlen) {
      DEBUGNL ("DateUtils::date4(): unexpected end of input after month") ;
      return -len ;
   }
   if (oldlen  == curlen) {
      DEBUGNL ("DateUtils::date4(): invalid date format: day must be digits") ;
      return -(len - curlen) ;
   }
   
   
   if (*s !='/')
   {
       DEBUGNL("DateUtils::date4(): no / after month");
       return - (len -curlen);
   }
   else
   {
       s++;
       curlen--;
   }
   if (! curlen) {
      DEBUGNL ("DateUtils::date2(): unexpected end of input after first -") ;
      return -len ;
   }
   
   oldlen = curlen ;
   while (curlen  &&  isdigit (c = *s)) {
      incr_int_(tm.tm_mday, c) ;
      s++ ;
      curlen-- ;
   }

   if (oldlen  == curlen) {
      DEBUGNL ("DateUtils::date4(): invalid date format: day must be digits") ;
      return -(len - curlen) ;
   }
   
   
   if (tm.tm_year >= 1900) 
   {
       tm.tm_year -= 1900 ;
   }

   return len - curlen ;
}




// --------------------------------------------------------------------
const char* RFC1123 :: version1 = "RFC1123: $Id: robustdate.C,v 1.4 1997/01/29 16:35:06 gorasche Exp $" ;
Verbose RFC1123 :: verbose ;

int RFC1123 :: parse_(const char* s, int len) {
   // rfc1123-date = wkday "," SP date1 SP time SP "GMT"
   int curlen = len ;
   int consumed ;

   if ((consumed = wkday(s, curlen,*this)) <= 0)
      return consumed ;   

   s += consumed ;
   curlen -= consumed ;

   while (curlen && isspace(*s)) { s++; curlen--; }
   if (! curlen) {
      DEBUGNL ("RFC1123::parse_(): unexpected end of input after wkday") ;
      return -len ;
   }

   if (*s != ',') {
      DEBUGNL ("RFC1123::parse_(): no ',' after wkday") ;
      return -(len - curlen) ;
   }
   else {
      s++ ;
      curlen-- ;
   }
      
   while (curlen && isspace(*s)) { s++; curlen--; }
   if (! curlen) {
      DEBUGNL ("RFC1123::parse_(): unexpected end of input after ',' after wkday") ;
      return - (len) ;
   }

   if ((consumed = date1(s, curlen,*this)) <= 0)
      return -(len - curlen + (-consumed)) ;

   s += consumed ;
   curlen -= consumed ;

   while (curlen && isspace(*s)) { s++; curlen--; }
   if (! curlen) {
      DEBUGNL ("RFC1123::parse(): unexpected end of input after date") ;
      return -(len) ;
   }
   
   if ((consumed = time(s, curlen,*this)) <= 0)
      return -(len - curlen + (-consumed)) ;

   s += consumed ;
   curlen -= consumed ;

   while (curlen && isspace(*s)) { s++; curlen--; }
   if (! curlen) {
      DEBUGNL ("RFC1123::parse(): unexpected end of input after time") ;
      return - (len) ;
   }

   if (tolower (*s) != 'g') {
      DEBUGNL ("RFC1123::parse(): no 'G'mt") ;
      return -(len - curlen) ;
   }
   else {
      s++ ;
      curlen-- ;
   }

   if (! curlen) {
      DEBUGNL ("RFC1123::parse_(): unexpected end of input after 'g' of \"gmt\"") ;
      return -len ;
   }

   if (tolower (*s) != 'm') {
      DEBUGNL ("RFC1123::parse_(): no g'M't") ;
      return -(len - curlen) ;
   }
   else {
      s++ ;
      curlen-- ;
   }

   if (! curlen) {
      DEBUGNL ("RFC1123::parse_(): unexpected end of input after 'm' of \"gmt\"") ;
      return -len ;
   }

   if (tolower (*s) != 't') {
      DEBUGNL ("RFC1123::parse_(): no gm'T'") ;
      return -(len - curlen) ;
   }
   else {
      s++ ;
      curlen-- ;
   }

   if (::mktime (this) == (time_t)-1) {
      DEBUGNL ("RFC1123::parse_(): finally, ::mktime() failed") ;
      return 0 ;
   }

   return len - curlen ;
}

// --------------------------------------------------------------------
const char* RFC1036 :: version1 = "RFC1036: $Id: robustdate.C,v 1.4 1997/01/29 16:35:06 gorasche Exp $" ;
Verbose RFC1036 :: verbose ;

int RFC1036 :: parse_(const char* s, int len) {
   // rfc1036-date = weekday "," SP date2 SP time SP "GMT"
   int curlen = len ;
   int consumed ;

   if ((consumed = weekday(s, curlen,*this)) <= 0)
      return consumed ;   

   s += consumed ;
   curlen -= consumed ;

   while (curlen && isspace(*s)) { s++; curlen--; }
   if (! curlen) {
      DEBUGNL ("RFC1036::parse_(): unexpected end of input after wkday") ;
      return -len ;
   }

   if (*s != ',') {
      DEBUGNL ("RFC1036::parse_(): no ',' after wkday") ;
      return - (len - curlen) ;
   }
   else {
      s++ ;
      curlen-- ;
   }
      
   while (curlen && isspace(*s)) { s++; curlen--; }
   if (! curlen) {
      DEBUGNL ("RFC1036::parse_(): unexpected end of input after ',' after wkday") ;
      return -len ;
   }

   if ((consumed = date2(s, curlen,*this)) <= 0)
      return -(len - curlen + (-consumed)) ;

   s += consumed ;
   curlen -= consumed ;

   while (curlen && isspace(*s)) { s++; curlen--; }
   if (! curlen) {
      DEBUGNL ("RFC1036::parse(): unexpected end of input after date") ;
      return - (len) ;
   }
   
   if ((consumed = time(s, curlen,*this)) <= 0)
      return -(len - curlen + (-consumed)) ;

   s += consumed ;
   curlen -= consumed ;

   while (curlen && isspace(*s)) { s++; curlen--; }
   if (! curlen) {
      DEBUGNL ("RFC1036::parse(): unexpected end of input after time") ;
      return -len ;
   }

   if (tolower (*s) != 'g') {
      DEBUGNL ("RFC1123::parse(): no 'G'mt") ;
      return -(len - curlen) ;
   }
   else {
      s++ ;
      curlen-- ;
   }

   if (! curlen) {
      DEBUGNL ("RFC1036::parse_(): unexpected end of input after 'g' of \"gmt\"") ;
      return -len ;
   }

   if (tolower (*s) != 'm') {
      DEBUGNL ("RFC1036::parse_(): no g'M't") ;
      return -(len - curlen) ;
   }
   else {
      s++ ;
      curlen-- ;
   }

   if (! curlen) {
      DEBUGNL ("RFC1036::parse_(): unexpected end of input after 'm' of \"gmt\"") ;
      return -len ;
   }

   if (tolower (*s) != 't') {
      DEBUGNL ("RFC1036::parse_(): no gm'T'") ;
      return -(len - curlen) ;
   }
   else {
      s++ ;
      curlen-- ;
   }

   if (::mktime (this) == (time_t)-1) {
       DEBUGNL ("RFC1036::parse_(): finally, ::mktime() failed") ;
       return 0;
   }

   return len - curlen ;
}

// --------------------------------------------------------------------
const char* AscTimeDate :: version1 = "AscTimeDate: $Id: robustdate.C,v 1.4 1997/01/29 16:35:06 gorasche Exp $" ;
Verbose AscTimeDate :: verbose ;

int AscTimeDate :: parse_(const char* s, int len) {
   // rfc1036-date = wkday SP date3 SP time SP 4digit (year)
   int curlen = len ;
   int consumed ;

   if ((consumed = wkday(s, curlen,*this)) <= 0)
      return consumed ;   

   s += consumed ;
   curlen -= consumed ;

   while (curlen && isspace(*s)) { s++; curlen--; }
   if (! curlen) {
      DEBUGNL ("AscTimeDate::parse_(): unexpected end of input after wkday") ;
      return -len ;
   }


   if ((consumed = date3(s, curlen,*this)) <= 0)
      return -(len - curlen + (-consumed)) ;

   s += consumed ;
   curlen -= consumed ;

   while (curlen && isspace(*s)) { s++; curlen--; }
   if (! curlen) {
      DEBUGNL ("AscTimeDate::parse(): unexpected end of input after date") ;
      return -len ;
   }
   
   if ((consumed = time(s, curlen,*this)) <= 0)
      return -(len - curlen + (-consumed)) ;

   s += consumed ;
   curlen -= consumed ;

   while (curlen && isspace(*s)) { s++; curlen--; }
   if (! curlen) {
      DEBUGNL ("AscTimeDate::parse(): unexpected end of input after time") ;
      return -len ;
   }
   
   
   tm_year = 0 ;
   char y ;
   int oldlen = curlen ;
   while (curlen  &&  isdigit (y = *s)) {
       incr_int_(tm_year, y) ;
      s++ ;
      curlen-- ;
   }
   
   if (oldlen  == curlen) {
      DEBUGNL ("AscTimeDate::parse_(): invalid date format: year must be digits") ;
      return -(len - curlen) ;
   }

   if (tm_year <= 1900) {
      DEBUGNL ("AscTimeDate::parse_(): invalid year, must be greater than 1900") ;
      return -(len - curlen) ;
   }
   tm_year -= 1900 ;
   
   

   if (::mktime (this) == (time_t)-1) {
       DEBUGNL ("AscTimeDate::parse_(): finally, ::mktime() failed") ;
       return 0;
   }

   return len - curlen ;
}
// -----------------------------------------------------------------------
const char* HWDate :: version1 = "HWDate: $Id: robustdate.C,v 1.4 1997/01/29 16:35:06 gorasche Exp $" ;
Verbose HWDate :: verbose ;

int HWDate :: parse_(const char* s, int len) {
// [yy]yy/mm/dd SP hh:mm:ss
   int curlen = len ;
   int consumed ;

   if ((consumed = date4(s, curlen,*this)) <= 0)
      return consumed ;   

   s += consumed ;
   curlen -= consumed ;

   while (curlen && isspace(*s)) { s++; curlen--; }
   if (! curlen) {
      DEBUGNL ("HWDate::parse_(): unexpected end of input after date") ;
      return -len ;
   }

   if ((consumed = time(s, curlen,*this)) <= 0)
      return -(len - curlen + (-consumed)) ;

   s += consumed ;
   curlen -= consumed ;

   
   if (::mktime (this) == (time_t)-1) {
       DEBUGNL ("HWDate::parse_(): finally, ::mktime() failed") ;
       return 0;
   }

   return len - curlen ;
}




// -----------------------------------------------------------------------
const char* RobustDate :: version1 = "RobustDate: $Id: robustdate.C,v 1.4 1997/01/29 16:35:06 gorasche Exp $" ;
RobustDate::RobustDate(const char* w ,int l ) : RFC1036(),RFC1123(),AscTimeDate()
{
    
    set_error_(- HWDate::parse_(w, l)) ;
    if (ok()) return ;
    set_error_(- RFC1036::parse_(w, l)) ;
    if (ok()) return ;
    set_error_(- RFC1123::parse_(w, l)) ;
    if (ok()) return ;
    set_error_(- AscTimeDate::parse_(w, l)) ;
    if (ok()) return ;
}
