-1

Is there a way to open a file with the open() without knowing its full name?
The linux shell provides an easy way to do that (in some sense), by accepting regular expressions as input.
For example, if you have a folder containing the files:

a.out  file1  file2  file3  file4  file.txt  test

and you want to list only the files with the prefix file you can do so by:

$ ls file*
file1  file2  file3  file4  file.txt

Or:

$ ls file[1-9]
file1  file2  file3  file4

To list only numbered files and so on...

I need to open the same file whenever my program launches.
The problem is, the file it needs to open is of the form: X*Y, meaning it starts with an X and ends with Y, but it could be anything in between.
For example, it could be X-Toshiba_12.45y9-Y, or it might be X-Dell-5.44s-Y.
I want to be able to open this file without having to consider the model.
The file may reside with some other files in that folder, but the X prefix and Y postfix are unique.
I could iterate the files in that folder and try to find my file by matching strings, but I'd rather avoid it.
Is there a way to provide open() with a regular expression somehow?

so.very.tired
  • 2,958
  • 4
  • 41
  • 69
  • 3
    *The linux shell provides an easy way to do that (in some sense), by accepting regular expressions as input.* Absolutely false, it's called **wildcard**, and it should be easy to implement, the `open()` function would have nothing to do with it at all. And alos, if you use the pattern with a `*` from the shell, your program will recieve all the matching arguments. – Iharob Al Asimi Mar 30 '15 at 22:14
  • The shell was just an example of what I'm trying to do. You can ignore it if you want. – so.very.tired Mar 30 '15 at 22:16
  • Ok so please explain exactly what you want, you want to pass a string such as `filename*` and then process all the matching files in a directory? – Iharob Al Asimi Mar 30 '15 at 22:20
  • You'll need to find the file yourself by searching the directory. See [here](http://stackoverflow.com/questions/11140483/how-to-get-list-of-files-with-a-specific-extension-in-a-given-folder) or [here](http://stackoverflow.com/questions/612097/how-can-i-get-the-list-of-files-in-a-directory-using-c-or-c) – ash Mar 30 '15 at 22:20
  • @iharob, I just need to open this particular file. A file that start with this particular prefix, and ends with this particular postfix. – so.very.tired Mar 30 '15 at 22:23
  • If you run `cat X*Y` what's actually happening is the shell is finding all the files that match that, and then running `cat` with a list of those files as if you had typed them. `cat` in turn is then running through a loop to print each of the files in its argument list, opening each in turn. – teppic Mar 30 '15 at 22:31
  • What if more than a single file matches your pattern? – Jongware Mar 30 '15 at 22:55
  • @Jongware, just a single file will match my pattern. – so.very.tired Mar 30 '15 at 23:28
  • 1
    You are looking for the C library function called [`glob`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/glob.html). (This function is specified by POSIX, not ISO C, and therefore may not be available on Windows.) "glob" is also the name of the type of pattern you can use to match groups of filenames in the shell. It is important to understand that these are NOT regular expressions; the syntax is superficially similar, but regexes are considerably more powerful. – zwol Mar 31 '15 at 00:01

2 Answers2

5

These are not regular expressions! You are talking about glob patterns.

You can use the POSIX.1-2001 glob() function to expand a glob pattern (like *.* or foo-*.?a* or *.[a-z]* and so on) to an array of filenames/pathnames that match the given pattern (starting at the current working directory, unless the pattern specifies an absolute path). This is basically what most shells use when they expand file name patterns.

If you were hell-bent on using regular expressions to specify file names (say, you need find-type behaviour, but with regular expressions), use SUSv4 nftw() function to traverse a directory tree -- it even works with the corner cases, like fewer descriptors than tree depth, or files modified, renamed, or moved while tree traversal --, and POSIX regex functions to filter on the file names. Note: regcomp() and regexec() etc. are built-in to POSIX.1-2001 -supporting C libraries, and that includes just about all current C library implementations for Linux. No external libraries are needed at all.

It makes me very sad to see example code using opendir()/readdir() to traverse a directory tree, when nftw() is available and much smarter and more robust. Just define _XOPEN_SOURCE 700 and _POSIX_C_SOURCE 200809L to get all these nice features in Linux and many *BSD variants, too.

Nominal Animal
  • 38,216
  • 5
  • 59
  • 86
1

Check this example

#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <dirent.h>

int
startswith(const char *const haystack, const char *const needle)
 {
    size_t haystackLength;
    size_t needleLength;   

    if ((haystack == NULL) || (needle == NULL))
        return 0;

    haystackLength = strlen(haystack);
    needleLength   = strlen(needle);

    if (haystackLength < needleLength)
        return 0;

    return (memcmp(haystack, needle, needleLength) == 0);
 }

int
endswith(const char *const haystack, const char *const needle)
 {
    size_t haystackLength;
    size_t needleLength;

    if ((haystack == NULL) || (needle == NULL))
        return 0;

    haystackLength = strlen(haystack);
    needleLength   = strlen(needle);

    if (haystackLength < needleLength)
        return 0;

    return (memcmp(haystack + haystackLength - needleLength, needle, needleLength) == 0);
 }

void
searchdir(const char *const directory, const char *const starts, const char *const ends)
 {
    DIR           *dir;
    struct dirent *entry;

    dir = opendir(directory);
    if (dir == NULL)
        return;
    while ((entry = readdir(dir)) != NULL)
     {
        struct stat statbuf;
        char        filepath[PATH_MAX];
        size_t      length;
        const char *name;

        name = entry->d_name;
        if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0))
            continue;      

        length = snprintf(filepath, sizeof(filepath), "%s/%s", directory, name);
        if (length >= sizeof(filepath))
         {
            fprintf(stderr, "unexpected error\n");
            closedir(dir);

            return;
         }

        if (stat(filepath, &statbuf) == -1)
         {
             fprintf(stderr, "cannot stat `%s'\n", filepath);
             continue;
         }

        /* if the entry is a directory, probably recures */
        if (S_ISDIR(statbuf.st_mode) != 0)
            saerchdir(filepath, starts, ends);
        /* or just, continue? */

        /* The file name does not match */
        if ((startswith(name, starts) == 0) || (endswith(name, ends) == 0))
            continue;
        /* Do whatever you want with the file */     
        fprintf(stdout, "%s\n", filepath);
     }
    closedir(dir);    
 }

int
main(int argc, char **argv)
{
    if (argc < 4)
     {
        fprintf(stderr, "usage: %s directory startpattern endpattern\n", argv[0]);
        fprintf(stderr, "\tex. %s /home/${USER} X Y\n", argv[0]);

        return -1;
     }
    searchdir(argv[1], argv[2], argv[3]);

    return 0;
}

Do whatever you want with the file, can go from pushing it into a char * array, to passing a function pointer to the function and executing that function on the file path.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
  • Thanks. I thought there is some magical shortcut, but this will also do. – so.very.tired Mar 30 '15 at 23:30
  • @so.very.tired, note that this code recursively searches the directory, for which it also needs to `stat` the file. Its `startswith` and `endswith` functions also recalculate the length of `X` and `Y` (your prefix and suffix) many times as well as at least recalculating the name of each file twice. For your case, e.g., match one or all the `X*Y` files in the current directory only, assuming they cannot be directories themselves, the solution is much much shorter. The idea is right on though; use `opendir` and loop through the files. – Shahbaz Mar 31 '15 at 09:06