Thread: check path validity in C99?

  1. #1
    Registered User
    Join Date
    Sep 2014
    Location
    SE Washington State
    Posts
    65

    check path validity in C99?

    I need to check to see if a path exists before saving a file to it.
    My function has a "filespec" variable which contains the path and file name. Is there a way to check if the path exists?

    Here is the function:
    Code:
    int saveprnfile(void)
    { // saves formatted output to a text file to be printed
        FILE *fptr;
        char *filename = "filename";
        char filespec[79];
        input(filename,79); // get path and file name
        strcpy(filespec, lin); // copy path and file name to 'filespec'
        lin[0] = '\0';
        fptr = fopen(filespec, "a");
        fprintf(fptr,"\t\t\t%s\n\n",filespec);
        fprintf(fptr,"Header Design Suite\n");
        fprintf(fptr,"Open Source Software\n\n\n");
        fprintf(fptr,"\n\n======= Header Design Suite Input =======\n\n");
        fprintf(fptr," Bore __________________ %2.3f\n", info.bore);
        fprintf(fptr," Stroke ________________ %1.3f\n", info.stroke);
        fprintf(fptr," Comp __________________ %2.1f to 1\n", info.compratio);
        fprintf(fptr," RPM ___________________ %4.1f\n", info.maxrpm);
        fprintf(fptr," Exhaust valve opens ___ %3.1f before BDC\n\n", info.exhaustopens);
        fprintf(fptr,"==========================================\n\n");
        fprintf(fptr,"======= Header Design Suite Output =======\n");
        fprintf(fptr,"\n\nHeader: Four single primary tubes into common collector\n\n");
        fprintf(fptr,"-------------------------------------------------------\n\n");
        fprintf(fptr," Primary tube inside diameter: _ %1.3f\n\n", info.primarydia);
        fprintf(fptr," Primary tube length: __________ %2.1f\n\n", info.primarylength);     
        fprintf(fptr," Collector diameter: ___________ %1.3f\n\n", info.collectordia);
        fprintf(fptr," Collector length ______________ %2.3f\n\n\n", info.collectorlength);
        fclose(fptr);
        return(0);
    }

  2. #2
    Registered User
    Join Date
    Dec 2007
    Posts
    2,675
    Use stat.

  3. #3
    Registered User
    Join Date
    Sep 2014
    Location
    SE Washington State
    Posts
    65
    Is stat C99 compliant? As I understand it stat is a system call and therefore not C99 compliant. Am I wrong/

  4. #4
    Registered User
    Join Date
    May 2009
    Posts
    4,183
    Read about fopen and use it to check.

    fopen - C++ Reference

    Tim S.
    "...a computer is a stupid machine with the ability to do incredibly smart things, while computer programmers are smart people with the ability to do incredibly stupid things. They are,in short, a perfect match.." Bill Bryson

  5. #5
    Registered User
    Join Date
    Sep 2014
    Location
    SE Washington State
    Posts
    65
    Quote Originally Posted by stahta01 View Post
    Read about fopen and use it to check.

    fopen - C++ Reference

    Tim S.
    Tim,
    If the path exists but the file doesn't I get the same value as if the path doesn't exist. Since I am appending a non-existant file (hopefully) how would I go about separating the existance of the path from the file name?
    I have used 'fopen(filespec, "r")' to check the existance of a file before writing it but is it possible to check the existance of a path while using 'fopen(filespec, "a")' ?

    I guess what I am trying to understand is since the file should not exist but the path should exist how do I differentiate the two?

  6. #6
    Registered User rstanley's Avatar
    Join Date
    Jun 2014
    Location
    New York, NY
    Posts
    1,111
    May I suggest mkdir()? If the directory exists, it will fail and you can check errno, otherwise the directory will be created.

  7. #7
    Registered User
    Join Date
    Sep 2014
    Location
    SE Washington State
    Posts
    65
    'mkdir()' is another system call. I am keeping this software C99 compliant.
    This is how I checked for existing files before I save an unformated file:
    Code:
    int savefile(void)
    {
        FILE *fptr;
        int x;
        char ch;
        char *filename = "filename";
        char filespec[79];
        do
        {
            x = 2;
            input(filename, 79); // get the path and filename
            strcpy(filespec, lin); // copy the entered path and filename
            lin[0] = '\0';
            clearscreen();
            printf("\t\n\n checking copy of %s on disk\n\n\n", filespec);
            fptr = fopen(filespec, "r"); // open file to read
            if(fptr != NULL) // there is a file
            {
                printf("file already exists; Rename your file or Overwrite?");
                printf("\n\nEnter R to rename or O to overwrite\t");
                ch = getchar();
                getchar(); // to strip out the newline character left
                switch(ch)
                {
                    case 'R':
                    case 'r':
                    x = 1;
                    fclose(fptr); // to rename close the pointer and loop
                    break;
                    case 'O': // 'O' as in Overwrite
                    case 'o': // 'o' as in overwrite
                    fclose(fptr); // to overwrite close the read pointer and continue the remainung code
                    x = 2;
                    break;
                }
            }
        }
        while(x == 1);
        fptr = fopen(filespec, "w"); // open the write pointer
        fwrite(&info, sizeof(info), 1, fptr); // write the file
        fclose(fptr); // close the pointer because we are done
        return 0;
    }
    I just don't see how it would work with the appending to a file.
    Last edited by PaulS; 12-07-2014 at 05:36 PM.

  8. #8
    Registered User
    Join Date
    May 2009
    Posts
    4,183
    Sorry did not get you wanted to check the path to the file.
    The only standard C way I can think of is to write/create to a file with a strange name after verify it does not exist using my prior suggestion method.
    So first try reading the strange file name.
    "r" read: Open file for input operations. The file must exist.
    If should return failed to read.
    Then try to create the file with strange name.
    If it is created then you can say the path is good.
    Then you need a portable way to delete the "strange file name" file.
    But, I do NOT know of a portable way to delete a file.

    So, my suggestion just results in looking for another standard way to do something.

    Me, I would just write a function with conditional for the OSes I am supporting.

    But, I think remove is standard; but, not sure it is.
    remove - C++ Reference

    Tim S.
    "...a computer is a stupid machine with the ability to do incredibly smart things, while computer programmers are smart people with the ability to do incredibly stupid things. They are,in short, a perfect match.." Bill Bryson

  9. #9
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Unfortunately, because C99 only defines EDOM, ERANGE, and EILSEQ errno macros, with all others defined outside the ISO C standard, in pure C99 you can only determine whether you can or cannot open a file. (Determined by fopen() returning a non-NULL file handle.)

    When fopen() returns NULL, you can obtain the relevant error message using strerror(errno) (which is C99), or print an error message to standard error using perror() (also defined in C99).

    Basically, in C99, all you can do is
    Code:
    /** savefile() - Save a struct info to a file
     * @path - File name or path
     * @info - struct info to save to file
     * This creates or truncates the file to zero bytes,
     * then writes the struct info to it.
     * Returns 0 if successful, nonzero if an error occurs.
    */
    int savefile(const char *const path, const struct info *const info)
    {
        FILE *out;
    
        if (path == NULL) {
            fprintf(stderr, "savefile(): No path specified.\n");
            return -1;
        }
        if (*path == '\0') {
            fprintf(stderr, "savefile(): Empty path specified.\n");
            return -2;
        }
        if (info == NULL) {
            fprintf(stderr, "savefile(): No data to save to '%s'.\n", path);
            return -3;
        }
    
        out = fopen(path, "wb");
        if (out == NULL) {
            fprintf(stderr, "savefile(): '%s': %s.\n", path, strerror(errno));
            return -4;
        }
    
        if (fwrite(info, sizeof *info, (size_t)1, out) != (size_t)1) {
            fprintf(stderr, "savefile(): Error writing to file '%s'.\n", path);
            return -5;
        }
    
        if (fflush(out)) {
            fprintf(stderr, "savefile(): A delayed error writing to file '%s'.\n", path);
            return -6;
        }
    
        if (fclose(out)) {
            fprintf(stderr, "savefile(): Error closing file '%s'.\n", path);
            return -7;
        }
    
        /* Success. */
        return 0;
    }
    If you want to append to the file instead of replacing the comments, use out = fopen(path, "ab"); instead. C99 defines modes r w a rb wb ab r+ w+ a+ r+b w+b a+b (and aliases rb+ wb+ ab+ for the last three).

    In my opinion, especially considering that Microsoft will not implement full C99 support in its compilers, relying on the C99 standard features is rather stupid. The compilers that support C99, also have POSIX.1-2001 or POSIX.1-2008 C libraries. The existence of standardized errno values, getline(), stat(), readdir(), glob(), regcomp() and regex(), and iconv() character set conversion facilities alone make it much more useful than ISO C. Heck, I can use the above to write a simple grep example in a couple of dozen lines of C99+POSIX.1-2008 code -- without any arbitrary line length limits, either.

    I really don't understand why one would rely on C99 but not on POSIX.1. Makes no sense to me.

  10. #10
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    > I guess what I am trying to understand is since the file should not exist but the path should exist how do I differentiate the two?
    Since there are no "path" functions in standard C, it's out of scope for your solution.

    All you can do is assume that your program will be run such that the "current path" is pointing to a valid place where a file can be created. If a file cannot be created, you have no option but to complain and then exit. The user has to figure out the rest, then run the program properly.
    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.

  11. #11
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by PaulS View Post
    Is stat C99 compliant? As I understand it stat is a system call and therefore not C99 compliant. Am I wrong/
    No, you're not wrong. But you're trying to do can't be done in C99 either, unless you write code that is specific to some operating system (or some set of operating systems). The C standards don't require a host system to support a file system, let alone paths and other such features.

    The handling of paths varies between operating systems (or, more accurately between file systems and file system drivers, but most file systems are only supported by some set or family of operating systems). To handle paths you therefore either need to limit your code to some specific operating systems or rely on "system" calls.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  12. #12
    Registered User
    Join Date
    Sep 2014
    Location
    SE Washington State
    Posts
    65
    Well, I thank you all for your time and thoughts on this issue. It seems that this is just another of C99 limitations that I will have to find a work-around for. At the very least I might be able to keep it from crashing the program.

    Nominal Animal,
    I am restricting myself to C99 for portability issues. Granted that C99 universal portability is a dream, it is still a good tool with wide application.
    I will study your sample code and thank you for offering it.

    Salem,
    Thanks for your input. I will place comments in the user manual document to explain the potential hazard and how to prevent it.

    Grumpy,
    I was happy to see you adding to this. You and Salem are two of the folks here that I respect above some others. (I don't understand the nym 'Grumpy' because you are always factual if not kind)

  13. #13
    Registered User
    Join Date
    Sep 2014
    Location
    SE Washington State
    Posts
    65
    Is there a way to mark the thread as solved?

  14. #14
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by PaulS View Post
    I am restricting myself to C99 for portability issues.
    My point was that you'll get wider portability relying on POSIX-1.2001 or POSIX.1-2008 C library features, than relying on C99.

    Too bad there is no POSIX C board on this forum; it would clear up a lot of the confusion. (I personally would not be here at all if the C++ and C forums were not separated.)

    As it is, a lot of Windows-specific C is discussed in the C forum, and a lot of non-Linux-specific POSIX C code is shuffled to the Linux forum, leading to either confused readers or some pedantic discussions on which set of standards/requirements a thread should abide by. No wonder most of those new to C are confused.

  15. #15
    Registered User
    Join Date
    Sep 2014
    Location
    SE Washington State
    Posts
    65
    Quote Originally Posted by Nominal Animal View Post
    My point was that you'll get wider portability relying on POSIX-1.2001 or POSIX.1-2008 C library features, than relying on C99.

    Too bad there is no POSIX C board on this forum; it would clear up a lot of the confusion. (I personally would not be here at all if the C++ and C forums were not separated.)

    As it is, a lot of Windows-specific C is discussed in the C forum, and a lot of non-Linux-specific POSIX C code is shuffled to the Linux forum, leading to either confused readers or some pedantic discussions on which set of standards/requirements a thread should abide by. No wonder most of those new to C are confused.
    While I can see your point, at 64 years old, one new language is more than enough to keep me busy right now
    I know nothing about POSIX. Is there a stable standard for it as there is in C? Is it an addition to C like so many "standard" C platforms in the past? - I will look it up so there is no need to answer the questions for me.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Validity Check.
    By Milo Juak MuTou in forum C++ Programming
    Replies: 3
    Last Post: 04-24-2013, 05:15 AM
  2. Replies: 6
    Last Post: 09-25-2012, 02:57 PM
  3. Check for validity of IP address
    By krishnampkkm in forum Linux Programming
    Replies: 1
    Last Post: 03-25-2010, 05:38 AM
  4. Help! with character validity check
    By Tdankert in forum C Programming
    Replies: 19
    Last Post: 07-30-2007, 08:41 PM
  5. Pointer validity check
    By Carlos in forum Windows Programming
    Replies: 6
    Last Post: 12-11-2003, 03:40 AM