0
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <tgmath.h>
#include <limits.h>
#include <stdbool.h>
#include <errno.h>
#define NEGATIVE -1
#define POSITIVE 1
#define OCTAL 8
#define DECIMAL 10
#define HEXADECIMAL 16
#define BASE_MIN 2
#define BASE_MAX 36
long int strtol (const char * str, char ** endPtr, int base)
{
    if(base < 0 ||  base == 1 || base > BASE_MAX)
    {
        errno = EINVAL;
        return 0L;
    }
    else
    {
        bool conversion = true;
        int i = 0, sign = POSITIVE, save;
        while(isspace(*(str + i)))
            i++;
        if(*(str + i) == '\0')
        {
            conversion = false;
            save = i;
        }
        if(*(str + i) == '-')
        {
            sign = NEGATIVE;
            i++;
        }
        else if(*(str + i) == '+')
            i++;
        if(base == 0) // find out base
        {
            if(*(str + i) == '0')
            {
                if(toupper(*(str + i + 1)) == 'X')
                {
                    base = HEXADECIMAL;
                    i++;
                }
                else
                    base = OCTAL;
                i++;
            }
            else
                base = DECIMAL;
        }
        else if(base == OCTAL)
        {
            if(*(str + i) == '0')
                i++;
        }
        else if(base == HEXADECIMAL)
        {
            if(*(str + i) == '0')
                if(*(str + i + 1) == 'x' || *(str + i + 1) == 'X')
                    i += 2;
        }
        int start = i, end, exp, check = i;
        long int long_int, sum, multiplier;
        if(conversion) // find out the correct part of the string corresponding to the number
        {
            if(base < DECIMAL)
            {
                while(*(str + i) >= '0' && *(str + i) < base + '0') // numbers from 0 to base - 1
                    i++;
            }
            else if(base == DECIMAL)
            {
                while(*(str + i) >= '0' && *(str + i) <= '9') // numbers from 0 to 9
                    i++;
            }
            else
            {
                while((*(str + i) >= '0' && *(str + i) <= '9') || (toupper(*(str + i)) >= 'A' && toupper(*(str + i)) < 'A' + base - 10))
                        i++;// numbers from 0 to 9 and uper and lowercase letters from a to a + base - 11
            }
        }
        if(i == check && conversion) //no digits at all
        {
            conversion = false;
            save = i;
        }
        else if(endPtr != NULL && conversion) // assign pointer
            *endPtr = (char *) (str + i);
        if(conversion)
        {
            for(end = i - 1, exp = 0, long_int = 0L; end >= start; end--, exp++)
            {
                multiplier = pow(base, exp);
                sum = 0L;
                if(*(str + end) >= '0' && *(str + end) <= '9') 
                    sum = (*(str + end) - '0') * multiplier;
                else if(*(str + end) >= 'A' && *(str + i) <= (base == BASE_MAX ? 'Z' : 'F')) 
                    sum = (*(str + end) - 'A' + 10) * multiplier;
                else if(*(str + end) >= 'a' && *(str + i) <= (base == BASE_MAX ? 'z' : 'f'))
                    sum = (*(str + end) - 'a' + 10) * multiplier;
                if(long_int <= LONG_MIN + sum)
                {
                    errno = ERANGE;
                    return LONG_MIN;
                }
                if(long_int >= LONG_MAX - sum)
                {
                    errno = ERANGE;
                    return LONG_MAX;
                }
                else
                    long_int += sum;
            }
            return sign * long_int;
        }
        else
        {
            if(endPtr != NULL)
            {// if base is 16 we check if the string given is not in the form 0xIncorrect string in that way we need to return xIncorrect part of the string
                if(base == HEXADECIMAL && save >= 2 && toupper(*(str + save - 1)) == 'X' && *(str + save - 2) == '0')
                    *endPtr = (char *) str + save - 1;
                else if(base == OCTAL && save >= 1 && *(str + save - 1) == '0')
                    *endPtr = (char *) str + save;// if the string is of base 8 and in the form 0incorrect string
                else                            //then we return everything after the 0 as the endptr string
                    *endPtr = (char *) str;//in other cases no conversion was done so we return original pointer
            }
            return 0L;
        }
    }
}

I've got problem with writing implementation of strtol() function. The thing is i compiled it on 64 bit machine and the output was correct but today i checked it on another machine that is 32-bit and something got wrong. 32-bit machine showed the result that for example string "7FFFFFFF" is out of range when on 64-bits the results is that strtol succeded which is the same as for th standard function. I also checked errno value and for 32-bit machine it's set to ERANGE which shouldn't be and it's not not on 64-bit. I have program that checks if your implementation gives the same output as the standard one for different strings. I spent few hours looking for possible bug but i'm out of ideas? Any tips?

Abbas
  • 14,186
  • 6
  • 41
  • 72
  • 1
    If you look at `/usr/include/limits.h`, `LONG_MAX` is defined as `9223372036854775807L` for `__WORDSIZE==64` but `2147483647L` otherwise. Have you checked if your string input has exceeded `2147483647L`? – alvits Apr 24 '14 at 19:07
  • I fixed it. It wasn't different behaviour at all. I have bug at code in conditions and just by luck it worked on 64 bits. But thanks for your help :) – user3119781 Apr 25 '14 at 13:25
  • @user3119781 don't remove the content of your question, it might be of help for others! – Abbas Apr 25 '14 at 13:30
  • You should post your answer and accept it. Others may run into the same problem and this could easily help them. – alvits Apr 25 '14 at 19:49

0 Answers0