For the minification scripts mentioned here, I had to write a program that would go through every file of a certain type in a directory and process (in this case, minify) that file. At the heart of this problem was the need to get a directory listing.
The obvious way to get a directory listing in C is to use the system call, as in system("ls");. But this is not only lame, it does not work on all systems, and the result cannot be used very easily. The better way to get a directory listing is to use system calls to the filesystem. A complete example is shown at the bottom of this post, as usual.
The functions used in getting a directory listing are opendir and readdir (note: these functions also exist in php, and the code to get a directory listing is in fact nearly the same in php and c). opendir() can be seen as kind of like a fopen() for directories. Following this analogy, readdir() is like a fgetc() for directories. To get a directory listing, we first open the directory with opendir("/dir/name/here"), and then read the name of each file in the directory with a loop of readdir(directory_handle)s. For example:
- #include <stdio.h>
- #include <sys/types.h>
- #include <dirent.h>
- int main() {
- DIR *dh;//directory handle
- struct dirent *file;//a 'directory entity' AKA file
- dh=opendir("/home/scott");
- while(file=readdir(dp))
- printf("%s\n",file->d_name);
- return 0;
- }
The code sample above is a simple form of the ‘ls’ unix command – it prints out all files in a directory. Note, however, that this includes ‘files’ like ‘.’ and ‘..’, as well as other directories. This script could be improved by only printing out the name of the file if it does not begin with a ‘.’, and is not a directory:
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/param.h>
- #include <sys/stat.h>
- #include <dirent.h>
- int main() {
- DIR *dh;//directory handle
- struct dirent *file;//a 'directory entity' AKA file
- struct stat info;//info about the file.
- dh=opendir("/home/scott");
- while(file=readdir(dp))
- if(file->d_name[0]!='.') {
- stat(dir->d_name,&info);
- if(!S_ISDIR(info.st_mode))
- printf("%s\n",file->d_name);
- }
- closedir(dh);
- return 0;
- }
In the above code listing, we call stat with both the filename and a reference to the stat struct as arguments, so as to collect information about the file (like the unix ’stat’ command). Then, we can ask the stat object if this file is a function with the S_ISDIR macro. If it is not, then we print out its name; otherwise, we just ignore it and move on. Note that the above code listing will also filter out all ‘hidden’ unix files which begin with a ‘.’, whether or not they are directories. We also remembered to close the directory when we were done this time, using closedir() (analogous to fclose()).
Below is a slightly more complete code sample, of how directory listings can be used in a more complicated application. Instead of just ignoring directories, we call minifyAllIn() on them recursively, thereby hitting every file in a directory or its subdirectories. This technique can be used to do things like find the total size of a directory (the unix command ‘du’).
- /*
- Excerpts from minification script by Scott Lawrence and Frederic Khoeler
- */
- #include <string>
- #include <stdlib.h>
- #include <string.h>
- #include <dirent.h>
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/param.h>
- #include <sys/stat.h>
- void minifyAllIn(string);
- void minifyFile(char *);
- string minify(string, bool css);
- int main() {
- //note to self (self): if fork()==0, I am the child
- minifyAllIn("/var/www/robot/js");
- minifyAllIn("/var/www/robot/style");
- return 0;
- }
- void minifyAllIn(string dirname_s) {
- const char *dirname=dirname_s.data();
- chdir(dirname);
- DIR *d=opendir(".");
- struct dirent *dir;
- struct stat info;
- char fp[PATH_MAX*5];
- while(dir=readdir(d)) {
- if(strcmp(dir->d_name,".")==0) continue;
- if(strcmp(dir->d_name,"..")==0) continue;
- stat(dir->d_name,&info);
- string name="";
- name+=dir->d_name;
- if(S_ISDIR(info.st_mode)) {
- chdir(dir->d_name);
- minifyAllIn(dir->d_name);
- chdir("..");
- } else if(name.length()>3 && name.substr(name.length()-3,3)==".js") {
- if(name.substr(name.length()-7,7)==".min.js") continue;
- getcwd(fp,PATH_MAX+1);
- strcat(fp,"/");
- strcat(fp,name.data());
- minifyFile(fp);
- } else if(name.length()>4 && name.substr(name.length()-4,4)==".css") {
- if(name.substr(name.length()-8,8)==".min.css") continue;
- getcwd(fp,PATH_MAX+1);
- strcat(fp,"/");
- strcat(fp,name.data());
- minifyFile(fp);
- } else continue;
- }
- closedir(d);
- }
- char* outputFilename(string originalFilename, int len_copy, string extension) {
- char* name = (char*) calloc(PATH_MAX*5, sizeof(char));
- strncpy(name, originalFilename.c_str(), len_copy);
- strcat(name, extension.c_str());
- return name;
- }
Related posts: