#1
  1. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Apr 2013
    Posts
    48
    Rep Power
    2

    Smile *FILE in functions problem


    To me at least this is strange. I was creating a program and it was reading some info from a configuration file. Because there were several options which took several arguments I created a function to fetch the arguments. I passed it a *FILE argument. When it returned I discovered that the position in the stream had not been advanced.
    Now, if I were passing a FILE object I would understand that since it makes a copy of every argument but it's a pointer. So, what's going on? Does the same thing happen with descriptors?
  2. #2
  3. Contributed User
    Devshed Specialist (4000 - 4499 posts)

    Join Date
    Jun 2005
    Posts
    4,396
    Rep Power
    1871
    The short answer is you should never be passing around a *FILE to begin with.

    FILE* is an opaque type, which means you might not even know what the contents are, and then attempting to pass around a *FILE would fail to compile.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper
  4. #3
  5. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,184
    Rep Power
    2222
    Show us what you did.

    You could pass a FILE* to a function, because it's a pointer; a copy of a pointer still points to the same place.

    If you did something to change the value of that FILE* pointer -- eg, did an fopen -- inside a function and expected that to get "returned" through the argument list, then that would not work. You would have had to have passed a pointer to a pointer to FILE.

    But I'm speaking out of total ignorance of what you had actually tried to do. Please show us.
  6. #4
  7. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Apr 2013
    Posts
    48
    Rep Power
    2
    Here's the code you asked for:

    Code:
    unsigned long long int fetch_numb(signed int *got_it, FILE *theconfigfile, \
    const unsigned char *option)
    {
        static unsigned long long int your_returned_numb;
        static signed int part_of_file;
        static char **tail_pointer = NULL;
        static unsigned short int index;
        unsigned char about_to_be_returned_numb[82];
        
        part_of_file = getc_unlocked(theconfigfile);
        
        /*
         * This is a little tricky.
         * To prevent the strtoull from reading from the config file, which it
         * won't, I copy all the information into a separate string. Finally, I call
         * strtoull and return the number if it succeeded, of course.
         *
         * It checks if a new line occored. Is space can retrun true for white space
         * therefore we say:
         * While it is not white space or it is a hexadecimal charecter or it is an
         * X or a x (because they can prefix a hexadecimal number) continue looping.
         */
        for(index = 0; part_of_file != EOF && \
        (isblank(part_of_file) || isxdigit(part_of_file) || \
        'X' == (unsigned char)part_of_file || \
        'x' == (unsigned char)part_of_file); index++)
        {
            about_to_be_returned_numb[index] = (unsigned char)part_of_file;
            part_of_file = getc_unlocked(theconfigfile);
            
            if(index == 80)
            {
                 fprintf(stderr, LITL, option, about_to_be_returned_numb) ? : \
                 raise(SIGHUP);
            }
        }
        
        about_to_be_returned_numb[index] = (unsigned char)'\0';
        
        errno = 0;
        your_returned_numb = strtoull(about_to_be_returned_numb, tail_pointer, 0);
        *got_it = errno;
        
        errno = 0;
        
        /*
         * In case that no number exists return the last charecter.
         * If a number does exist then we return the last number anyway beacuse
         * it's the beginning quote of the next string. If it's a new line or white
         * space it does not hurt to also be returned.
         */
        
        (void)ungetc(part_of_file, theconfigfile);
        
        return(your_returned_numb);
    }
    And chere's a small part of the calling code (I substituted the swich statement for an if one,)

    Code:
    int main (void)
    {
        //These should be issued by main and will be removed in a future release.
        char configfilea[201] = "config";
        char * const configfile = configfilea;
        static struct sigaction theaction;
        obstack_alloc_failed_handler = &my_obstack_alloc_failed;
        static struct obstack some_name;
        struct obstack *the_global_obstack = &some_name;
        
        
        (void)obstack_init(the_global_obstack);
        
        unsigned char tmparg[100];
        FILE *theconfigfile;
        static unsigned char read_file_no_ptr = 'r';
        unsigned char *read_file = &read_file_no_ptr;
        signed int part_of_file = 0;
        unsigned short int tmp_vars_i;
        unsigned short int tmp_vars_j;
        unsigned long long int test_int;
        unsigned char *tmp_string_ptr;
        bool yes_r_no;
        signed int got_it_no_ptr;
        signed int *got_it = &got_it_no_ptr;
        
        theconfigfile = fopen(configfile, read_file);
        
        if(NULL == theconfigfile)
        {
            fprintf(stderr, CNOCF, configfile) ? \
            raise(SIGABRT) : raise(SIGHUP);
        }
        
        part_of_file = getc_unlocked(theconfigfile);
        
        switch(part_of_file == EOF)
        {
            case true:
                    fprintf(stderr, CRFCF) ? : raise(SIGHUP);
                    break;
            default:
                    break;
        }
        
        while(part_of_file != EOF)
        {
            tmparg[0] = (unsigned char)part_of_file;
            
            while(tmparg[0] == '#' || isspace(part_of_file))
            {
                        /* 
                         * This is a quite simple but highly useful operation, first
                         * I getc_unlocked until I reach EOF or '\n' then, to get
                         * past all the new lines and space I change it so that I
                         * getc_unlocked until I reach EOF or I find an end to the
                         * '/n's and space characters. It then goes back to the
                         * start of my while loop (above,) and checks if the line
                         * it's now on contains a comment or white space.
                         */
                        
                        while(part_of_file != EOF && \
                                     (unsigned char)part_of_file != '\n' &&
                                     (unsigned char)part_of_file != '\f' &&
                                     (unsigned char)part_of_file != '\r')
                        {
                            part_of_file = getc_unlocked(theconfigfile);
                        }
                        while(part_of_file != EOF && \
                                     isspace(part_of_file))
                        {
                            part_of_file = getc_unlocked(theconfigfile);
                        }
                        tmparg[0] = (unsigned char)part_of_file;
            }
            
            /* 
             * We'll read the option in and then parse it next.
             * because the = sign is punct and the : is also punct I use ispunct
             * but if I were to do that I would need to eliminate the _ so I replace
             * it with a U because none of the options are upper case at all.
             */
            
            
    
            for(tmp_vars_i = 0; part_of_file != EOF && \
                                           !isspace(part_of_file) && \
                                           !ispunct(part_of_file); tmp_vars_i++)
            {
                    if(tmp_vars_i == 98)
                    {
                        tmparg[98] = '\n';
                        tmparg[99] = '\0';
                        fprintf(stderr, OITL, tmparg) ? \
                        raise(SIGABRT) : raise(SIGHUP);
                    }
                    
                    tmparg[tmp_vars_i] = (unsigned char)part_of_file;
                    part_of_file = getc_unlocked(theconfigfile);
                    
                    if((unsigned char)part_of_file == '_')
                    {
                        /*
                         * U is for underline so that ispunct returns false.
                         * Now in order to fix this we need all the "U"s to be
                         * changed out with "_"s.
                         */
                        
                        part_of_file = (int)'U';
                    }
                    
                    if(2 < (arguments.debug))
                    {
                        fprintf(stderr, "%c\n", (char)part_of_file) ? : \
                        raise(SIGHUP);
                    }
            }
            
            if(1 < (arguments.debug))
            {
                fprintf(stderr, "%s\n%c\n", tmparg, part_of_file) ? : raise(SIGHUP);
            }
            
            tmparg[tmp_vars_i] = '\0';
            
            //We need to get past the '=' sign.
            part_of_file = getc_unlocked(theconfigfile);
            
            /*
             * We have the complete option, now we get the value and assign
             * it to the appropriet variable. We also need to replace the "U"s.
             */
            
            for(tmp_vars_j = 0; tmp_vars_j != tmp_vars_i; tmp_vars_j++)
            {
                    if(tmparg[tmp_vars_j] == 'U' )
                    {
                        tmparg[tmp_vars_j] = '_';
                    }
            }
            
            
            if(false == (bool)strcmp( "wait", tmparg))
            {
                test_int = fetch_numb(got_it, theconfigfile, tmparg );
    
                if(*got_it)
                {
                    fprintf(stderr, ATB, test_int, \
                    tmparg, strerror(*got_it)) ? \
                    raise(SIGABRT): raise(SIGHUP);
                }
    
                if(test_int > USHRT_MAX)
                {
                    fprintf(stderr, ATB, test_int, \
                    tmparg, strerror(*got_it)) ? \
                    raise(SIGABRT): raise(SIGHUP);
                }
    
                if(false == (used_options & 0x1))
                {
                    used_options |= 0x1;
                    GGGconfiguration_file.wait = \
                    (unsigned short int)test_int;
                }
                else
                {
                    fprintf(stderr, OAG, tmparg) ? \
                    raise(SIGABRT) : raise(SIGHUP);
                }
            }
        }
    }
  8. #5
  9. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,184
    Rep Power
    2222
    That's some difficult code to read. Does your compiler really require you to escape newlines like that? In more than 20 years of programming in C/C++, I've yet to encounter such a requirement. In macros, yes, but who writes functions as macros? Escaping your lines like that adds to the clutter.

    That appears to be C99, so I cannot even try to compile it to see what warnings there are. However, this looks suspicious to me (lines of interest in red):
    Code:
        FILE *theconfigfile;
        static unsigned char read_file_no_ptr = 'r';
        unsigned char *read_file = &read_file_no_ptr;
        signed int part_of_file = 0;
        unsigned short int tmp_vars_i;
        unsigned short int tmp_vars_j;
        unsigned long long int test_int;
        unsigned char *tmp_string_ptr;
        bool yes_r_no;
        signed int got_it_no_ptr;
        signed int *got_it = &got_it_no_ptr;
        
        theconfigfile = fopen(configfile, read_file);
    fopen expects a C-style string as its second argument, not merely a pointer to a char. A C-style string containing a single character of 'r' would in fact be two characters long: the 'r' followed by the null-terminator, '\0'. IOW, fopen expects a "r", not a pointer to a measly 'r'. There are file_mode strings that are longer than one character, so fopen does indeed expect a string, not a pointer to a single character.

    I don't know whether that would have any bearing on your problem. For that matter, since you screwed up the file_mode argument, I would have expected fopen to have failed, which it apparently did not -- you did after all test for failure.

    Secondary question:
    Code:
        if(NULL == theconfigfile)
        {
            fprintf(stderr, CNOCF, configfile) ? \
            raise(SIGABRT) : raise(SIGHUP);
        }
    Would raising either of those two signals also be guaranteed to terminate the program right then and there?
  10. #6
  11. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Apr 2013
    Posts
    48
    Rep Power
    2
    Actually gcc accepted all that code with no problem and -Wall was on plus a few more assorted ones I got from a gcc tutorial.
    I use / on the lines that need to be pulled together into one as good form. It also allows me to find missiong ';' easier.
    Yes, it is C 99.
    Yes, the signals terminate the program immediatly.
    I had not noticed that the argument type was off, thanks. :)
    But that still should not change the fact that a pointer to something is a pointer to something not a something. :confused:
  12. #7
  13. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,184
    Rep Power
    2222
    Originally Posted by ballsystemlord
    I had not noticed that the argument type was off, thanks. :)
    But that still should not change the fact that a pointer to something is a pointer to something not a something. :confused:
    In a small test program, declare a char array and initialize it with a string. Remove the null-terminator by overwriting it with any printable character, such as a space. printf that string. Enjoy the garbage that ensues.

    Here, I'll do it for you:
    Code:
    #include <stdio.h>
    #include <strings.h>
    
    int main()
    {
        char str [] = "Pull my finger.";
        
        // the null-terminator is at str[strlen(str)]
        // eliminate it by overwriting it with a space
        str[strlen(str)] = ' ';
    
        printf("%s\n", str);
        
        return 0;
    }
    Now here's what happens when I run it:
    C:TEST>gcc -Wall finger.c

    C:TEST>a
    Pull my finger. *"

    C:TEST>
    Where did those extra characters come from? Simple, those are in memory after the string. If I hadn't removed the null-terminator, then they would not have printed. Why? Because the null-terminator marks the end of the string. Every single C function that uses C-style strings expects that null-terminator to be there and depends on it being there. If the null-terminator is not there, then the function will just continue to plow through and process all those subsequent garbage characters until, just out of blind dumb luck, it just happens to hit a byte of zero, AKA '\0', AKA "a null-terminator".

    A pointer is a pointer is a pointer, in that they are all memory addresses. What is important is what they point to. Does an int* point to the same thing as a FILE* ? Of course not! So why would you think that a char* that points to a single character that is followed by garbage would be the same as a char* that points to a C-style string?

    Does it make sense now?

    Did correcting that make any difference?
    Last edited by dwise1_aol; November 15th, 2013 at 03:38 PM.
  14. #8
  15. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Apr 2013
    Posts
    48
    Rep Power
    2
    I've done that before.
    Yes, that was indeed the problem and thanks for the help.

IMN logo majestic logo threadwatch logo seochat tools logo