/**** Global functions definitions. ****/ TA_RetCode TA_GetHistoryDataFromWeb( TA_DataSourceHandle *handle, TA_CategoryHandle *categoryHandle, TA_SymbolHandle *symbolHandle, TA_Period period, const TA_Timestamp *start, const TA_Timestamp *end, TA_Field fieldToAlloc, TA_ParamForAddData *paramForAddData ) { TA_PROLOG TA_RetCode retCode; TA_StringCache *stringCache; TA_String *yahooName; TA_WebPage *webPage; TA_PrivateYahooHandle *yahooHandle; TA_DecodingParam localDecodingParam; TA_DecodingParam directYahooDecodingParam; const TA_DecodingParam *decodingParam; TA_FileHandle *fileHandle; TA_ReadOpInfo *readOpInfo; TA_UIRSuffixParsing suffixParsing; TA_Timestamp firstBarTimestamp, lastBarTimestamp, prevEndDate; TA_InfoFromAddedData infoFromAddedData; TA_DayOfWeek dayOfWeek; TA_Timestamp curAdjustYear, lastAdjustYear; const char *overideServerAddr; int nbEstimateBar; int nbField; unsigned int nbBarAdded, nbTotalBarAdded; int again, firstTime, nbBatch; int zeroBarAddedAttempt; int doAdjustment; TA_TRACE_BEGIN( TA_GetHistoryDataFromWeb ); /* Initialize some local variables. */ stringCache = TA_GetGlobalStringCache(); yahooHandle = (TA_PrivateYahooHandle *)handle->opaqueData; readOpInfo = NULL; nbEstimateBar = 0; TA_ASSERT( categoryHandle != NULL ); TA_ASSERT( symbolHandle != NULL ); TA_ASSERT( categoryHandle->string != NULL ); TA_ASSERT( symbolHandle->string != NULL ); retCode = TA_BAD_PARAM; /* Set the initial first/last timestamp */ if( start ) TA_TimestampCopy( &firstBarTimestamp, start ); else TA_SetDate( 1950, 1, 1, &firstBarTimestamp ); if( end ) TA_TimestampCopy( &lastBarTimestamp, end ); else TA_SetDateNow( &lastBarTimestamp ); /* Time component is not important for Yahoo! but all end of day * price bar use 00:00:00, so do the same here. */ TA_SetTime( 0, 0, 0, &firstBarTimestamp ); TA_SetTime( 0, 0, 0, &lastBarTimestamp ); /* Make sure that lastBarTimestamp is a week-day. */ dayOfWeek = TA_GetDayOfTheWeek( &lastBarTimestamp ); if( (dayOfWeek == TA_SUNDAY) || (dayOfWeek == TA_SATURDAY) ) TA_PrevWeekday( &lastBarTimestamp ); TA_ASSERT( yahooHandle != NULL ); if( yahooHandle->param->id == TA_YAHOO_ONE_SYMBOL ) { /* User specified Yahoo! name. */ yahooName = TA_StringDup(stringCache,yahooHandle->webSiteSymbol); TA_ASSERT( yahooName != NULL ); } else { /* Map the TA-Lib name into the Yahoo! name. */ retCode = TA_AllocStringFromLibName( categoryHandle->string, symbolHandle->string, &yahooName ); if( retCode != TA_SUCCESS ) { TA_TRACE_RETURN( retCode ); } TA_ASSERT( yahooName != NULL ); } /* Check if the user did overide the server address in the location parameter. * (as needed, convert from TA_String to char *) */ if( yahooHandle->userSpecifiedServer ) overideServerAddr = TA_StringToChar(yahooHandle->userSpecifiedServer); else overideServerAddr = NULL; /* Get the decoding parameter for the CSV web page. */ if( yahooHandle->param->id == TA_YAHOO_ONE_SYMBOL ) { retCode = TA_YahooIdxDataDecoding( yahooHandle->webSiteCountry, TA_YAHOOIDX_CSV_PAGE, &directYahooDecodingParam ); if( retCode != TA_SUCCESS ) { TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( retCode ); } decodingParam = &directYahooDecodingParam; } else { decodingParam = TA_YahooIdxDecodingParam( yahooHandle->index, TA_YAHOOIDX_CSV_PAGE ); if( !decodingParam ) { TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( TA_INTERNAL_ERROR(103) ); } } localDecodingParam = *decodingParam; /* Check if split/value adjustment are necessary. */ if( (yahooHandle->param->flags & TA_DO_NOT_SPLIT_ADJUST) && (yahooHandle->param->flags & TA_DO_NOT_VALUE_ADJUST) ) { doAdjustment = 0; } else doAdjustment = 1; /* Parse the uirSuffix so the start/end date can be changed. */ if( !setUIRSuffixParsing( decodingParam->uirSuffix, &suffixParsing ) ) { /* This should never happen unless the * Yahoo! index protocol has been broken. */ /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( TA_INTERNAL_ERROR(104) ); } /* Use a local copy of the decoding param. * This is because the uirSuffix is replaced with * an allocated buffer (so the date field can be * manipulated). */ /* Replace the uirSuffix with a large local buffer. */ localDecodingParam.uirSuffix = TA_Malloc( suffixParsing.maxTotalLength ); if( !localDecodingParam.uirSuffix ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } /* Change the dates in the uirSuffix. */ buildUIRSuffix( &suffixParsing, &firstBarTimestamp, &lastBarTimestamp, (char *)localDecodingParam.uirSuffix ); /* nbBatch is a safety net to make sure that * TA-Lib won't stay forever in the while loop * in case Yahoo! changes their protocol. */ nbBatch = 0; /* Sometime Yahoo! return an empty csv file. Make * multiple attempts in that case. */ zeroBarAddedAttempt = 0; again = 1; firstTime = 1; nbTotalBarAdded = 0; while( again && (++nbBatch < 100) && (zeroBarAddedAttempt < 10) ) { retCode = TA_DriverShouldContinue(paramForAddData); if( retCode != TA_SUCCESS ) { TA_StringFree( stringCache, yahooName ); TA_Free( (char *)localDecodingParam.uirSuffix ); #if !defined( TA_SINGLE_THREAD ) if( readOpInfo ) TA_ReadOpInfoFree( readOpInfo ); #endif TA_TRACE_RETURN( retCode ); } if( TA_TimestampLess(&lastBarTimestamp,&firstBarTimestamp) ) { /* Get out of this loop if all the requested data * has been retreived already. */ again = 0; break; } retCode = TA_WebPageAllocFromYahooName( &localDecodingParam, TA_StringToChar(yahooName), overideServerAddr, &webPage, paramForAddData ); if( retCode != TA_SUCCESS ) { TA_StringFree( stringCache, yahooName ); TA_Free( (char *)localDecodingParam.uirSuffix ); #if !defined( TA_SINGLE_THREAD ) if( readOpInfo ) TA_ReadOpInfoFree( readOpInfo ); #endif TA_TRACE_RETURN( retCode ); } /* Disguise the webPage stream into a "file". That way the speed * optimized ASCII decoder can be re-used (TA_ReadOp stuff). */ retCode = TA_FileSeqOpenFromStream( webPage->content, &fileHandle ); if( retCode != TA_SUCCESS ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_WebPageFree( webPage ); TA_Free( (char *)localDecodingParam.uirSuffix ); #if !defined( TA_SINGLE_THREAD ) if( readOpInfo ) TA_ReadOpInfoFree( readOpInfo ); #endif TA_TRACE_RETURN( retCode ); } if( firstTime ) { /* Make assumption of the data provided * base on the number of fields in the CSV file. */ nbField = nbCommaField( webPage->content ); switch( nbField ) { case 2: readOpInfo = yahooHandle->readOp2Fields; break; case 5: readOpInfo = yahooHandle->readOp5Fields; break; default: readOpInfo = yahooHandle->readOp6Fields; } #if !defined( TA_SINGLE_THREAD ) /* Must use a local copy if multi-threaded */ retCode = TA_ReadOpInfoClone(&readOpInfo, readOpInfo); if( retCode != TA_SUCCESS ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_WebPageFree( webPage ); TA_Free( (char *)localDecodingParam.uirSuffix ); TA_TRACE_RETURN( retCode ); } #endif /* User asking for all the fields? */ if( fieldToAlloc == TA_ALL ) { switch( nbField ) { case 2: fieldToAlloc = TA_CLOSE|TA_TIMESTAMP; break; case 5: fieldToAlloc = TA_OPEN|TA_HIGH|TA_LOW|TA_CLOSE|TA_TIMESTAMP; break; default: fieldToAlloc = TA_OPEN|TA_HIGH|TA_LOW|TA_CLOSE|TA_VOLUME|TA_TIMESTAMP; } } /* Optimize the read op for the requested data. */ retCode = TA_ReadOp_Optimize( readOpInfo, period, fieldToAlloc ); if( retCode != TA_SUCCESS ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_WebPageFree( webPage ); TA_Free( (char *)localDecodingParam.uirSuffix ); #if !defined( TA_SINGLE_THREAD ) if( readOpInfo ) TA_ReadOpInfoFree( readOpInfo ); #endif TA_TRACE_RETURN( retCode ); } /* Make an estimation of the number of price bar. */ nbEstimateBar = TA_StreamCountChar( webPage->content, '\n' ) + 1; if( nbEstimateBar < 100 ) nbEstimateBar = 100; } /* Interpret the CSV data. */ retCode = TA_ReadOp_Do( fileHandle, readOpInfo, period, &firstBarTimestamp, &lastBarTimestamp, nbEstimateBar, fieldToAlloc, paramForAddData, &nbBarAdded ); TA_FileSeqClose( fileHandle ); TA_WebPageFree( webPage ); nbTotalBarAdded += nbBarAdded; if( retCode != TA_SUCCESS ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_Free( (char *)localDecodingParam.uirSuffix ); #if !defined( TA_SINGLE_THREAD ) if( readOpInfo ) TA_ReadOpInfoFree( readOpInfo ); #endif TA_TRACE_RETURN( retCode ); } /* Yahoo! does not always return all the data it could, up to * the requested end date. It is important to detect these occurence * and cancel the usage of all data accumulated up to now. */ TA_GetInfoFromAddedData( paramForAddData, &infoFromAddedData ); if( infoFromAddedData.barAddedSinceLastCall ) { /* Do some more checking by considering holidays, week-end etc... */ if( !isGapAcceptable(&infoFromAddedData.highestTimestampAddedSinceLastCall, &lastBarTimestamp) ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_Free( (char *)localDecodingParam.uirSuffix ); #if !defined( TA_SINGLE_THREAD ) if( readOpInfo ) TA_ReadOpInfoFree( readOpInfo ); #endif TA_TRACE_RETURN( TA_DATA_GAP ); } TA_TimestampCopy( &lastBarTimestamp, &infoFromAddedData.lowestTimestamp ); } #if DEBUG_PRINTF printf( "NB BAR ADDED=%d, TOTAL=%d\n", nbBarAdded, nbTotalBarAdded ); #endif /* Verify if more data should be processed. * Yahoo! sometimes slice their data, in * batch of 200 price bars. */ if( firstTime && (nbBarAdded > 200) ) { again = 0; /* Assume all the data extracted... exit the loop. */ } else if( nbBarAdded == 0 ) { /* Make multiple attempts when retreiving data succeed, * but somehow there is zero bar returned. * * Sometimes this might be correct when there is truly no * more data available, so choosing an algorithm before * giving up is a comprimise between reliability and * usability. The data source is free... and you get * what you pay for after all ;) */ if( (nbTotalBarAdded < 1000) && (zeroBarAddedAttempt >= 1) && (zeroBarAddedAttempt < 7) ) { /* I did choose to add a delay when insufficient total data is returned. When * there is already ~5 years of data, most likely there is "Zero" returned * because there is NO more data available, so just do the retry without delay. */ TA_Sleep(zeroBarAddedAttempt*2); } #if DEBUG_PRINTF printf( "Retry %d", zeroBarAddedAttempt ); #endif zeroBarAddedAttempt++; } else { zeroBarAddedAttempt = 0; if( TA_TimestampEqual( &lastBarTimestamp, &prevEndDate ) ) { /* prevEndDate is a "safety net" to * exit the loop early in case Yahoo! starts * to return always the same batch of data. * Just ignore the repetitive data and exit. */ TA_Free( (char *)localDecodingParam.uirSuffix ); TA_StringFree( stringCache, yahooName ); #if !defined( TA_SINGLE_THREAD ) if( readOpInfo ) TA_ReadOpInfoFree( readOpInfo ); #endif TA_TRACE_RETURN( TA_SUCCESS ); } TA_TimestampCopy( &prevEndDate, &lastBarTimestamp ); /* Request the data up to the day BEFORE * the last batch of data received. */ TA_PrevDay( &lastBarTimestamp ); /* Make sure that lastBarTimestamp is a week-day. */ dayOfWeek = TA_GetDayOfTheWeek( &lastBarTimestamp ); if( (dayOfWeek == TA_SUNDAY) || (dayOfWeek == TA_SATURDAY) ) TA_PrevWeekday( &lastBarTimestamp ); /* Change the dates in the uirSuffix. */ buildUIRSuffix( &suffixParsing, &firstBarTimestamp, &lastBarTimestamp, (char *)localDecodingParam.uirSuffix ); /* From that point, data is expected to be most likely * sent in batch of 200. */ nbEstimateBar = 200; } firstTime = 0; } /* Get rid of some memory not used anymore. */ TA_Free( (char *)localDecodingParam.uirSuffix ); #if !defined( TA_SINGLE_THREAD ) if( readOpInfo ) TA_ReadOpInfoFree( readOpInfo ); #endif /* If adjusted data is requested, use splits and dividend info from Yahoo!. */ if( doAdjustment && (nbTotalBarAdded >= 1) ) { /* Get the decoding parameter for the adjustment page. */ if( yahooHandle->param->id == TA_YAHOO_ONE_SYMBOL ) { retCode = TA_YahooIdxDataDecoding( yahooHandle->webSiteCountry, TA_YAHOOIDX_ADJUSTMENT, &directYahooDecodingParam ); if( retCode != TA_SUCCESS ) { TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( retCode ); } decodingParam = &directYahooDecodingParam; } else { decodingParam = TA_YahooIdxDecodingParam( yahooHandle->index, TA_YAHOOIDX_ADJUSTMENT ); if( !decodingParam ) { TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( TA_INTERNAL_ERROR(140) ); } } localDecodingParam = *decodingParam; if( !setUIRSuffixParsing( decodingParam->uirSuffix, &suffixParsing ) ) { /* This should never happen unless the * Yahoo! index protocol has been broken. */ /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( TA_INTERNAL_ERROR(141) ); } /* Use a local copy of the decoding param. * This is because the uirSuffix is replaced with * an allocated buffer (so the date field can be * manipulated). */ /* Replace the uirSuffix with a large local buffer. */ localDecodingParam.uirSuffix = TA_Malloc( suffixParsing.maxTotalLength ); if( !localDecodingParam.uirSuffix ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } /* curAdjustYear indicates for which year the download is * taking place. */ TA_SetDefault( &curAdjustYear ); TA_SetDateNow( &curAdjustYear ); /* Identify the oldest year for which data was downloaded */ TA_GetInfoFromAddedData( paramForAddData, &infoFromAddedData ); TA_TimestampCopy( &lastAdjustYear, &infoFromAddedData.lowestTimestamp ); TA_PrevYear( &lastAdjustYear ); /* Get one more year to be on the safe side. */ while( TA_TimestampLess( &lastAdjustYear, &curAdjustYear ) ) { /* Set prevEndDate to two years earlier. */ TA_TimestampCopy( &prevEndDate, &curAdjustYear ); TA_PrevYear( &prevEndDate ); TA_PrevYear( &prevEndDate ); /* Change the dates in the uirSuffix. */ TA_SetDate( TA_GetYear(&curAdjustYear), 12, 31, &curAdjustYear ); TA_SetDate( TA_GetYear(&prevEndDate), 1, 1, &prevEndDate ); buildUIRSuffix( &suffixParsing, &prevEndDate, &curAdjustYear, (char *)localDecodingParam.uirSuffix ); retCode = doAdjustments( &localDecodingParam, yahooName, yahooHandle, NULL, paramForAddData ); if( retCode != TA_SUCCESS ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_Free( (char *)localDecodingParam.uirSuffix ); TA_TRACE_RETURN( retCode ); } /* Move 3 years earlier. */ TA_PrevYear( &prevEndDate ); TA_TimestampCopy( &curAdjustYear, &prevEndDate ); } /* Clean-up what was allocated for the adjustment logic. */ TA_Free( (char *)localDecodingParam.uirSuffix ); } /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( retCode ); }
TA_RetCode TA_ReadOpInfoAlloc( const char *sourceInfo, TA_ReadOpInfo **allocatedInfo, unsigned int readOpFlags ) { TA_PROLOG TA_RetCode retCode; TA_ReadOp readOp; TA_ReadOp *arrayReadOp; TA_ReadOpInfo *newReadOpInfo; TA_Field fieldMask, fieldProvided; unsigned int timeframeIdx; unsigned int intraDayIncPeriod; TA_TokenId intraDayIncToken; TA_TokenId tempToken; unsigned int tempInt; unsigned int period; unsigned int errorOccurred; const char *pos; unsigned int inField; unsigned int nbField; unsigned int nbCharInField; unsigned int skipNonDigitLine; const char *ptrFirstCarInField; unsigned char localBuf[10]; unsigned int bufIdx, opIdx, i; register unsigned int flagSet; register TA_ReadOp *ptrReadOp; TA_TRACE_BEGIN( TA_BuildArrayReadOp ); newReadOpInfo = (TA_ReadOpInfo *)TA_Malloc( sizeof( TA_ReadOpInfo ) ); /* These variables are resolved within this function. */ memset( newReadOpInfo, 0, sizeof( TA_ReadOpInfo ) ); /* At this point, TA_ReadOpInfoFree can be safely called. */ /* Keep track of some user provided parameter. */ newReadOpInfo->sourceInfo = sourceInfo; newReadOpInfo->readOpFlags = readOpFlags; /* Initialize some defaults. */ newReadOpInfo->openInterestMult = 100; newReadOpInfo->volumeMult = 100; nbField = 0; intraDayIncPeriod = 0; intraDayIncToken = TA_TOK_END; pos = newReadOpInfo->sourceInfo; if( !pos || (*pos == '\0') ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_MISSING_FIELD ); } /* Find how many fields are defined and check some syntax * at the same time. */ if( *pos != '[' ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } inField = 0; nbCharInField = 0; skipNonDigitLine = 0; ptrFirstCarInField = NULL; while( *pos != '\0' ) { switch( *pos ) { case '[': if( inField ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } inField = 1; break; case ']': if( (!inField) || (nbCharInField == 0) ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } nbField++; /* Exclude fields not generating a TA_ReadOp. * For the time being that means only the -H and -NDL field. */ if( nbCharInField >= 2 ) { TA_ASSERT( ptrFirstCarInField != NULL ); if( ptrFirstCarInField[0] == '-' ) { if( toupper(ptrFirstCarInField[1]) == 'H' ) nbField--; else if( (toupper(ptrFirstCarInField[1]) == 'N') && (toupper(ptrFirstCarInField[2]) == 'D') && (toupper(ptrFirstCarInField[3]) == 'L') ) { skipNonDigitLine = 1; nbField--; } } } inField = 0; nbCharInField = 0; ptrFirstCarInField = NULL; break; default: if( !inField ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } if( nbCharInField == 0 ) ptrFirstCarInField = pos; nbCharInField++; break; } pos++; } if( inField || *(pos-1) != ']' ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } /* Build the TA_ReadOp array */ arrayReadOp = (TA_ReadOp *)TA_Malloc( sizeof( TA_ReadOp ) * nbField ); if( !arrayReadOp ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } newReadOpInfo->arrayReadOp = arrayReadOp; pos = TA_StringToChar(newReadOpInfo->sourceInfo); bufIdx = 0; opIdx = 0; while( *pos != '\0' && (opIdx < nbField) ) { switch( *pos ) { case '[': break; case ']': localBuf[bufIdx] ='\0'; bufIdx = 0; /* Identify the field and build the TA_ReadOp. */ tempInt = 0; retCode = buildReadOp( newReadOpInfo, (const char *)&localBuf[0], &arrayReadOp[opIdx], &tempToken, &tempInt ); if( retCode != TA_SUCCESS ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( retCode ); } if( arrayReadOp[opIdx] != 0 ) { /* Set the replace zero flag as needed */ if( TA_IS_REPLACE_ZERO(readOpFlags) && TA_IS_REAL_CMD(arrayReadOp[opIdx]) ) { TA_SET_REPLACE_ZERO(arrayReadOp[opIdx]); } /* Set the skipNonDigitLine flag as needed. */ if( skipNonDigitLine == 1 ) { TA_SET_SKIP_NDL_FLAG(arrayReadOp[opIdx]); } /* Ooof... this readOp is now all build! */ opIdx++; } /* If this is a time token, make sure this * is not in contradiction with an already * specified increment. */ if( intraDayIncPeriod ) { errorOccurred = 0; switch( tempToken ) { case TA_TOK_SEC: case TA_TOK_SS: if( (intraDayIncToken == TA_TOK_MIN) || (intraDayIncToken == TA_TOK_MN) ) errorOccurred = 1; /* no break */ case TA_TOK_MIN: case TA_TOK_MN: if( (intraDayIncToken == TA_TOK_HOUR) || (intraDayIncToken == TA_TOK_HH) ) errorOccurred = 1; break; case TA_TOK_HOUR: case TA_TOK_HH: errorOccurred = 1; break; default: /* Do nothing */ break; } if( errorOccurred ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } } /* Check if a period increment is specified. */ if( (tempInt != 0) && (tempInt != 1) ) { if( intraDayIncPeriod != 0 ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } intraDayIncPeriod = tempInt; intraDayIncToken = tempToken; } break; default: if( bufIdx >= sizeof(localBuf)-1 ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } localBuf[bufIdx++] = *pos; break; } pos++; } if( opIdx != nbField ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INTERNAL_ERROR(89) ); } arrayReadOp[opIdx-1] |= TA_CMD_LAST_FLAG; /* Build the mask representing the fields provided. */ fieldProvided = 0; timeframeIdx = TA_INTEGER_ARRAY_SIZE; for( opIdx=0; opIdx < nbField; opIdx++ ) { readOp = arrayReadOp[opIdx]; TA_ASSERT( readOp != 0 ); /* Parano test */ if( !TA_IS_PERMANENT_SKIP_SET(readOp) ) { /* Make sure this field was not specified twice. */ for( i=opIdx+1; i < nbField; i++ ) { if( (TA_IS_REAL_CMD(readOp) && TA_IS_REAL_CMD(arrayReadOp[i])) || (TA_IS_INTEGER_CMD(readOp) && TA_IS_INTEGER_CMD(arrayReadOp[i])) ) { if( (TA_GET_IDX(readOp) == TA_GET_IDX(arrayReadOp[i])) && !TA_IS_PERMANENT_SKIP_SET(arrayReadOp[i]) ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_REDUNDANT_FIELD ); } } } /* Parano test: Double-check redundant field in a different way. */ fieldMask = TA_ReadOpToField( readOp ); TA_ASSERT( fieldMask != 0 ); if( !(fieldMask & TA_TIMESTAMP) && (fieldProvided & fieldMask) ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_REDUNDANT_FIELD ); } /* Set the field. */ fieldProvided |= fieldMask; /* Keep track of the smallest granularity of the timestamp. */ if( fieldMask & TA_TIMESTAMP ) { if( (timeframeIdx == TA_INTEGER_ARRAY_SIZE) || (TA_GET_IDX(readOp) < timeframeIdx) ) timeframeIdx = TA_GET_IDX(readOp); } } } /* No date/time reference provided!? This is considered an error * in the current implementation. */ if( timeframeIdx == TA_INTEGER_ARRAY_SIZE ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_MISSING_DATE_OR_TIME_FIELD ); } /* Determine at which point the timestamp is completed. */ flagSet = 0; for( opIdx=nbField; opIdx > 0; opIdx-- ) { ptrReadOp = &arrayReadOp[opIdx-1]; readOp = *ptrReadOp; if( !flagSet && TA_IS_INTEGER_CMD(readOp) && (TA_GET_IDX(readOp)<=TA_YEAR_IDX) && !TA_IS_PERMANENT_SKIP_SET(readOp) ) { TA_SET_TIMESTAMP_COMPLETE(*ptrReadOp); flagSet = 1; } else { TA_CLR_TIMESTAMP_COMPLETE(*ptrReadOp); } } /* Validate and identify the period. */ period = 0; if( intraDayIncPeriod ) { errorOccurred = 0; switch( timeframeIdx ) { case TA_YEAR_IDX: case TA_MONTH_IDX: case TA_DAY_IDX: errorOccurred = 1; break; case TA_HOUR_IDX: if( (intraDayIncPeriod < TA_1HOUR) || (intraDayIncPeriod >= TA_DAILY) ) errorOccurred = 1; break; case TA_MIN_IDX: if( (intraDayIncPeriod < TA_1MIN) || (intraDayIncPeriod >= TA_1HOUR) ) errorOccurred = 1; break; case TA_SEC_IDX: if( (intraDayIncPeriod < TA_1SEC) || (intraDayIncPeriod >= TA_1MIN) ) errorOccurred = 1; break; default: TA_ReadOpInfoFree( newReadOpInfo ); TA_FATAL( NULL, timeframeIdx, fieldProvided ); } if( errorOccurred ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } period = intraDayIncPeriod; } else { switch( timeframeIdx ) { case TA_YEAR_IDX: period = TA_YEARLY; break; case TA_MONTH_IDX: period = TA_MONTHLY; break; case TA_DAY_IDX: period = TA_DAILY; break; case TA_HOUR_IDX: period = TA_1HOUR; break; case TA_MIN_IDX: period = TA_1MIN; break; case TA_SEC_IDX: period = TA_1SEC; break; default: TA_FATAL( NULL, timeframeIdx, fieldProvided ); } } /* A last check... */ if( period == 0 ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } /* Everything is fine, let's return the information. */ newReadOpInfo->arrayReadOp = arrayReadOp; newReadOpInfo->fieldProvided = fieldProvided; newReadOpInfo->nbReadOp = nbField; newReadOpInfo->period = period; *allocatedInfo = newReadOpInfo; TA_TRACE_RETURN( TA_SUCCESS ); }