How to check if a dir exists? - c

How would I go about checking if a FILE is a directory? I have
if (file == NULL) {
fprintf(stderr, "%s: No such file\n", argv[1]);
return 1;
}
and that checks if the node exists at all, but I want to know if it's a dir or a file.

Filenames themselves don't carry any information about whether they exist or not or whether they are a directory with them - someone could change it out from under you. What you want to do is run a library call, namely stat(2), which reports back if the file exists or not and what it is. From the man page,
[ENOENT] The named file does not exist.
So there's an error code which reports (in errno) that the file does not exist. If it does exist, you may wish to check that it is actually a directory and not a regular file. You do this by checking st_mode in the struct returned:
The status information word st_mode has the following bits:
...
#define S_IFDIR 0040000 /* directory */
Check the manpage for further information.

struct stat st;
if(stat("/directory",&st) == 0)
printf(" /directory is present\n");

use opendir to try and open it as a directory. If that returns a null pointer it's clearly not a directory :)
Here's a snippet for your question:
#include <stdio.h>
#include <dirent.h>
...
DIR *dip;
if ((dip = opendir(argv[1])) == NULL)
{
printf("not a directory");
}
else closedir(dip);

If you're using *nix, stat().

Related

check if something exists and is executable in C, using stat function

For example, I have directory a and b under my current working directory. I'm trying to locate file X, how can I modify the stat() command so that it checks both directory a and b instead of just current working directory? would stat(a/file, &buf) work? also, to check if it's executable, I know the code is buf.S_IXUSR, does if (buf.S_IXUSR) work?
thanks!
I suggest you consult the stat(2) man page.
Here's an example of how to use stat:
struct stat buf;
if (stat("a/file", &buf) != 0) {
// handle failure
}
// Check the `st_mode` field to see if the `S_IXUSR` bit is set
if (buf.st_mode & S_IXUSR) {
// Executable by user
}
However, for your use case, you might consider access(2) instead:
if (access("/path/to/file", X_OK) == 0) {
// File exists and is executable by the calling process's
// _real_ UID / GID.
}

Check if input file is a valid file in C

I am trying to open a file in c using open() and I need to check that the file is a regular file (it can't be a directory or a block file). Every time I run open() my returned file discriptor is 3 - even when I don't enter a valid filename!
Here's what I have
/*
* Checks to see if the given filename is
* a valid file
*/
int isValidFile(char *filename) {
// We assume argv[1] is a filename to open
int fd;
fd = open(filename,O_RDWR|O_CREAT,0644);
printf("fd = %d\n", fd);
/* fopen returns 0, the NULL pointer, on failure */
}
Can anyone tell me how to validate input files?
Thanks!
Try this:
int file_isreg(const char *path) {
struct stat st;
if (stat(path, &st) < 0)
return -1;
return S_ISREG(st.st_mode);
}
This code will return 1 if regular, 0 if not, -1 on error (with errno set).
If you want to check the file via its file descriptor returned by open(2), then try:
int fd_isreg(int fd) {
struct stat st;
if (fstat(fd, &st) < 0)
return -1;
return S_ISREG(st.st_mode);
}
You can find more examples here, (specifically in the path.c file).
You should also include the following headers in your code (as stated on stat(2) manual page):
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
For future reference, here is an excerpt of the stat(2) manpage regarding the POSIX macros available for st_mode field validations:
S_ISREG(m) is it a regular file?
S_ISDIR(m) directory?
S_ISCHR(m) character device?
S_ISBLK(m) block device?
S_ISFIFO(m) FIFO (named pipe)?
S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.)
S_ISSOCK(m) socket? (Not in POSIX.1-1996.)
int isValidFile(char *filename) {
// We assume argv[1] is a filename to open
int fd;
fd = open(filename,O_RDWR|***O_CREAT***,0644);
printf("fd = %d\n", fd);
/* fopen returns 0, the NULL pointer, on failure */
}
you are using 0_CREAT which prompts the function to create if the file doesn't exist.this in the table its number is 3 (0,1,2 being std input std output and std error)
Wrong: check if the file is OK, then if it is, go open it and use it.
Right: go open it. If you can't, report the problem and bail out. Otherwise, use it (checking and reporting errors after each opetation).
Why: you have just checked that a file is OK. That's fine, but you cannot assume it will be OK in 0.000000017 seconds from now. Perhaps the disk wil overheat and break down. Perhaps some other process will mass-delete your entire file collection. Perhaps your cat will trip over the network cable. So let's just check if it's OK again, and then go open it. Wow, what a great idea! No wait...

How to determine files and directories in parent/other directories

I found the answer to another question here to be very helpful.
There seems to be a limitation of the sys/stat.h library as when I tried to look in other directories everything was seen as a directory.
I was wondering if anyone knew of another system function or why it sees anything outside the current working directory as only a directory.
I appreciate any help anyone has to offer as this is perplexing me and various searches have turned up no help.
The code I made to test this is:
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
int main(void) {
int status;
struct stat st_buf;
struct dirent *dirInfo;
DIR *selDir;
selDir = opendir("../");
// ^ or wherever you want to look
while ((dirInfo = readdir(selDir))) {
status = stat (dirInfo->d_name, &st_buf);
if (S_ISREG (st_buf.st_mode)) {
printf ("%s is a regular file.\n", dirInfo->d_name);
}
if (S_ISDIR (st_buf.st_mode)) {
printf ("%s is a directory.\n", dirInfo->d_name);
}
}
return 0;
}
You need to check the status of the stat call; it is failing.
The trouble is that you're looking for a file the_file in the current directory when it is actually only found in ../the_file. The readdir() function gives you the name relative to the other directory, but stat() works w.r.t the current directory.
To make it work, you'd have to do the equivalent of:
char fullname[1024];
snprintf(fullname, sizeof(fullname), "%s/%s", "..", dirInfo->d_name);
if (stat(fullname, &st_buf) == 0)
...report on success...
else
...report on failure...
If you printed out stat, you'll notice there's an error (File not found).
This is because stat takes the path to the file, but you're just providing the file name.
You then call IS_REG on garbage values.
So, suppose you have a file ../test.txt
You call stat on test.txt...That isn't in directory ./test.txt, but you still print out the results from IS_REG.

Print out file names and its' sizes in C

I'm not sure if C can do this, but I'm hoping that I can make a program that will look into a directory, and print out all of the contents of the directory along with the file size of each file. As in I wanted it to look like this (possibly):
filename.txt -- 300 bytes
filename2.txt -- 400 bytes
filename3.txt -- 500 bytes
And so on.
So far, I created a program that can open a file, and it will print the bytes, but it does not read the entire directory, and I have to be specific with which file I want to read.. (which is not what I want).
Here is what I have so far:
#include <stdio.h>
int main(){
FILE *fp; // file pointer
long fileSize;
int size;
// opens specified file and reads
fp = fopen( "importantcommands.txt", "rw" );
if( fp == NULL ){
printf( "Opening file error\n" );
return 0;
}
// uses fileLength function and prints here
size = fileLength(fp);
printf( "\n Size of file: %d bytes", size );
fclose(fp);
return 0;
}
int fileLength( FILE *f ){
int pos;
int end;
// seeks the beginning of the file to the end and counts
// it and returns into variable end
pos = ftell(f);
fseek (f, 0, SEEK_END);
end = ftell(f);
fseek (f, pos, SEEK_SET);
return end;
}
Please help.
C can certainly do it - the ls(1) command can, for example, and it's written in C.
To iterate over a directory, you can use the opendir(3) and readdir(3) functions. It's probably easier to just let the shell do it for you, though.
As far as getting the filename, you can just take it as a command line parameter by defining main as:
int main(int argc, char **argv)
Command line parameters will begin at argv[1].
See opendir() / fdopendir() and readdir() if you are using linux in dirent.h
man page
Simple example from a : SO Post
DIR *dir;
struct dirent *ent;
if ((dir = opendir ("c:\\src\\")) != NULL) {
/* print all the files and directories within directory */
while ((ent = readdir (dir)) != NULL) {
printf ("%s\n", ent->d_name);
}
closedir (dir);
}
else {
/* could not open directory */
perror ("Could not open directory");
return EXIT_FAILURE;
}
Also You can use the fstat() system call which can fill in the struct stat for any file you want. From that stat you can access that file's size.
Please use the man pages to help you out. (Almost) Everything related to Linux is insanely well documented.
To read a list of files in a directory look at opendir, readdir, closedir for Linux
use stat to get the length of the file.
These are of Linux
For winodws see http://msdn.microsoft.com/en-gb/library/windows/desktop/aa365200%28v=vs.85%29.asp and the link http://blog.kowalczyk.info/article/8f/Get-file-size-under-windows.html will show you how to do this.
To get the list of files in a directory look for "libc opendir". To get the size of a file without opening it you can use fstat.
This seems strangely similar to another question I saw recently. Anyway, here's my strangely similar answer (for Linux, not sure how it'll fare on Windows 7):
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
int main(int argc, char *argv[]) {
struct stat file_stats;
DIR *dirp;
struct dirent* dent;
dirp=opendir("."); // specify directory here: "." is the "current directory"
do {
dent = readdir(dirp);
if (dent)
{
printf("%s -- ", dent->d_name);
if (!stat(dent->d_name, &file_stats))
{
printf("%u bytes\n", (unsigned int)file_stats.st_size);
}
else
{
printf("(stat() failed for this file)\n");
}
}
} while (dent);
closedir(dirp);
}
There are little things need to be taken care for the given examples (under Linux or other UNIX).
You properly only want to print out the file name and size of a regular file only. Use S_ISREG() to test the st_mode field
If you want to recursively print out all files under sub directories also, you then need to use S_ISDIR() to test for direcotry and be carefull of special directory '.' and '..'.

How to know which file failed during rename?

I have a simple example:
#include <stdio.h>
#include <errno.h>
int main() {
int result = rename("filea", "doesntexist/fileb");
if (result != 0) {
printf("NOOOO %d\n", errno);
}
return 0;
}
and I want to distinguish between 2 of the possible failures:
filea doesn't exist
directory for fileb doesn't exist
but it always returns errno = 2 when either doesn't exist... uhm
Any ideas how can I approach this?
Thanks
EDIT: If possible without manually checking if the files exist.
EDIT2: Not checking if the file exists is a stupid constraint ;) so, I've accepted one of the answers already. Thanks!
I don't know how you're going to check if a file exists without checking if a file exists, but hopefully this function will help you out:
#include <sys/stat.h>
if (!fileExists("foo")) { /* foo does not exist */ }
int fileExists (const char *fn)
{
struct stat buf;
int i = stat(fn, &buf);
if (i == 0)
return 1; /* file found */
return 0;
}
If your goal is to keep the code clean, then just use functions:
int main()
{
if (! renameFiles("fileA", "fileB")) {
fprintf(stderr, "rename failed...\n");
exit EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int renameFiles(const char *source, const char *destination)
{
int result = -1;
if ( (fileExists(source)) && (!fileExists(destination)) )
result = rename(source, destination);
if (result == 0)
return 1; /* rename succeeded */
/*
Either `source` does not exist, or `destination`
already exists, or there is some other error (take
a look at `errno` and handle appropriately)
*/
return 0;
}
You could return custom error codes from renameFiles() and conditionally handle errors based on which file does or does not exist, or if there is some other problem with the rename() call.
Call access() (unistd.h) first. Or stat(). And you are probably getting an ENOENT error when filea does not exist. Some ways you can get an error on fileB:
path cannot be found
no permissions on the path
fileB exists and you do not have permissions
you have a too long or malformed name
There are others but they are not very common.
There is no case where you should get an error when fileB is not there. You execute a mv filea fileb (what rename does) and all of the errors for mv apply here. Missing destination file is not one of them.
You should also have
#include <errno.h>
since you reference errno.
The ISO C standard does not even require the library function rename to set errno in case of error. All that is guaranteed is a non-zero return value on error (7.19.4.2, ยง3).
So whether this is possible or not depends on your platform (and it is not portable).
E.g. in Linux there is no way to distinguish which of them is missing by just looking at errno after rename (according to this man page).
If the errno is always 2 ENOENT "No such file or directory" on your system you are going to HAVE to check for the existence of something. On my system I get errno of 2 if old does not existent or if the directory path of new does not exist.
However there is much more then 2 possible errors. The link http://man.chinaunix.net/unix/susv3/functions/rename.html has 20 distinct errno values specified.
I would suggest that if the rename fails and the errno is 2 then check for the existence of old. If found then the problem is that the directory specified in the new doesn't exist.

Resources