Coffee Space


Listen:

Search File Pair

Preview Image

I had a weird requirement for a program I wrote recently in C, where I want to locate the first two files in a given directory that differ only by specified extensions. Obviously, this is quite bespoke, so there wasn’t anything really out there.

I came up with a nice solution and thought I would share it here for others to benefit from.

0001 #include <dirent.h>
0002 
0003 /**
0004  * get_extension()
0005  *
0006  * Get a pointer to the extension of a filename string.
0007  *
0008  * @param fn The filename to be searched. Must be non-NULL.
0009  * @return The location of the file extension, otherwise the start of the
0010  * string.
0011  **/
0012 char* get_extension(char* fn){
0013   char* last = strrchr(fn, '.');
0014   return last != NULL ? last + 1 : fn;
0015 }

The above is a helper function for locating an extension for a given filename string.

0016 /**
0017  * search_file_pair()
0018  *
0019  * Find the first two files with the same name, that have the given extensions
0020  * for a requested path.
0021  *
0022  * @param path The path to be searched.
0023  * @param ext_a The first extension.
0024  * @param ext_b The second extensions.
0025  * @return The name of the file found, otherwise NULL. You must free() this
0026  * after use.
0027  **/
0028 char* search_file_pair(char* path, char* ext_a, char* ext_b){
0029   /* Make sure ext_a is longer than ext_b */
0030   if(strlen(ext_a) < strlen(ext_b)){
0031     char* t = ext_a;
0032     ext_a = ext_b;
0033     ext_b = t;
0034   }
0035   DIR* d;
0036   struct dirent* dir;
0037   d = opendir(path);
0038   /* Check we opened the directory */
0039   if(d){
0040     /* Loop over all entries in the directory */
0041     while((dir = readdir(d)) != NULL){
0042       /* Ensure this is not a directory */
0043       if(dir->d_type != DT_DIR){
0044         /* Do we have a match on the first extension? */
0045         char* ext = get_extension(dir->d_name);
0046         if(strcmp(ext_a, ext) == 0 && dir->d_name != ext){
0047           /* Build expected pair file with path used to find other file */
0048           /* Length: Path + / + file + extension + NULL terminator */
0049           char* fn = (char*)malloc(sizeof(char) * (strlen(path) + 1 + strlen(dir->d_name) + 1));
0050           strcpy(fn, path);
0051           strcpy(fn + strlen(fn), "/");
0052           strcpy(fn + strlen(fn), dir->d_name);
0053           strcpy(get_extension(fn), ext_b);
0054           /* Check if we found a pair */
0055           if(access(fn, F_OK) == 0){
0056             /* Remove the extension we just added */
0057             *(get_extension(fn) - 1) = '\0';
0058             return fn;
0059           }else free(fn);
0060         }
0061       }
0062     }
0063     /* Close the file */
0064     close(d);
0065   }
0066   return NULL;
0067 }

And that was the magic function. It could in theory be a little faster, but it also needs to be memory efficient too for this use-case.

Anyway, I hope to be able to share what this is about soon…