#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main (void)
{
DIR *dp;
struct dirent *ep;
dp = opendir ("./");
if (dp != NULL)
{
while (ep = readdir (dp))
puts (ep->d_name);
(void) closedir (dp);
}
else
perror ("Couldn't open the directory");
return 0;
}
This code gives me all things in that directory. It works like "ls" command. For example let's say i have one folder which is name is "folder" and one .txt file which is name is "input" (names are could be different btw) , in that directory. I want to decide if it is folder or txt file.If it is txt file how can i read it?
]You can use scandir() function to open and scan the entries in a directory.
The example comes from man 3 scandir
#define _SVID_SOURCE
/* print files in current directory in reverse order */
#include <dirent.h>
int
main(void)
{
struct dirent **namelist;
int n;
n = scandir(".", &namelist, NULL, alphasort);
if (n < 0)
perror("scandir");
else {
while (n--) {
printf("%s\n", namelist[n]->d_name);
free(namelist[n]);
}
free(namelist);
}
}
Note the struct dirent:
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* offset to the next dirent */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported
by all file system types */
char d_name[256]; /* filename */
};
you can check if current entry is a regular file, directory or others by d_type field.
Available file types are :
#define DT_UNKNOWN 0
#define DT_FIFO 1
#define DT_CHR 2
#define DT_DIR 4 // directory type
#define DT_BLK 6
#define DT_REG 8 // regular file, txt file is a regular file
#define DT_LNK 10
#define DT_SOCK 12
#define DT_WHT 14
After checking the name and type of file, you can safely open it with open()(system call) or fopen()(glib function) and read the contents by read()(if you opened file via open() or fread()(fopen() counterpart).
DON'T forget to close file after read.
Besides, if you just want to check the existence and accessibility of a directory, access() is handle.
The code below tests if the dir exist.
int exist_dir (const char *dir)
{
DIR *dirptr;
if (access(dir, F_OK) != -1) {
// file exists
if ((dirptr = opendir(dir)) != NULL) {
// do something here
closedir (dirptr);
} else {
// dir exists, but not a directory
return -1;
}
} else {
// dir does not exist
return -1;
}
return 0;
}
All information about a file except the contents can be get by calling function stat. stat has signature
int stat(const char *pathname, struct stat *buf);
and returns information about the file in the buffer pointed to by buf.
struct stat encodes information of file type and file permission in the field st_mode.
Some additional macros are defined to test the file type:
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.)
Here is a piece of sample code:
stat(pathname, &sb);
if (S_ISREG(sb.st_mode)) {
/* Handle regular file */
}
As for reading, concatenate directory path and filename to get file path, using function sprintf, like this:
sprintf(file_path, "%s/%s", dir_path, file_name);
Related
With this code I'm able to print recursively all files and subdirectories from a given path.
What I want is to ignore (not print) all the subdirectorynames, and only print the file names.
This is the code:
#include <stdio.h>
#include <dirent.h>
#include <string.h>
void listFilesRecursively(char *basePath)
{
char path[1000];
struct dirent *dp;
DIR *dir = opendir(basePath);
if (!dir)
return;
while ((dp = readdir(dir)) != NULL)
{
if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
{
strcpy(path, basePath);
strcat(path, "/");
strcat(path, dp->d_name);
listFilesRecursively(path);
printf("%s\n", path);
}
}
closedir(dir);
}
int main()
{
char path[100];
printf("Enter path to list files: ");
scanf("%s", path);
listFilesRecursively(path);
return 0;
}
There are macros that tells you what type of file it is:
S_ISREG(): regular file
S_ISDIR(): directory file
S_ISCHR(): character special file
S_ISBLK(): block special file
S_ISFIFO(): pipe or FIFO
-S_ISLNK(): symbolic
S_ISSOCK(): link socket
First of all you can use one of the following functions to get informaration:
#include <sys/stat.h>
int stat(const char *restrict pathname, struct stat *restrict buf );
int fstat(int fd, struct stat *buf);
int lstat(const char *restrict pathname, struct stat *restrict buf );
int fstatat(int fd, const char *restrict pathname, struct stat *restrict buf, int flag);
From the Advanced Programming in Unix Environment book:
Given a pathname, the stat function returns a structure of information
about the named file. The fstat function obtains information about the
file that is already open on the descriptor fd. The lstat function is
similar to stat, but when the named file is a symbolic link, lstat
returns information about the symbolic link, not the file referenced
by the symbolic link.
You can try something like the following:
struct stat statbuf;
struct dirent *dirp;
DIR *dp;
int ret, n;
/* fullpath contains full pathname for every file */
if (lstat(fullpath, &statbuf) < 0)
{
printf("error\n");
//return if you want
}
if (S_ISDIR(statbuf.st_mode) == 0)
{
/* not a directory */
}
else
{
//a directory
}
Historically, early versions of the UNIX System didn’t provide the S_ISxxx macros. Instead, we had to logically AND the st_mode value with the mask S_IFMT and then compare the result with the constants whose names are S_IFxxx. Most systems define this mask and the related constants in the file .
For example:
struct stat *statptr;
if (lstat(fullpath, statptr) < 0)
{
printf("error\n");
//return if you want
}
switch (statptr->st_mode & S_IFMT) {
case S_IFREG: ...
case S_IFBLK: ...
case S_IFCHR: ...
case S_IFIFO: ...
case S_IFLNK: ...
case S_IFSOCK: ...
case S_IFDIR: ...
}
Trying to make a pwd for the c shell. This is what I found on a website and wanted to learn more about it.
I have use debugging printf statements all the way through the program already and it returns the "." instead of the actual dir name all the way through. What am I missing? Why would this be happening?
#include <dirent.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main()
{
struct stat stat_buf;
struct dirent *file_info;
ino_t itself_ino; /* holds current folder inode */
ino_t parent_ino; /* holds parent folder inode */
char Current[PATH_MAX]; /* folder name */
char Path[PATH_MAX]; /* holds the full path */
char Slash[PATH_MAX]; /* add / before the folder name */
DIR *dir;
while (1)
{
dir = opendir(".");
if(dir == NULL) {
fprintf(stderr, "cannot get current directory.\n");
exit(-1);
}
/* read the information about the current folder */
file_info = readdir(dir);
lstat(file_info->d_name, &stat_buf);
itself_ino = stat_buf.st_ino;
closedir(dir);
chdir(".."); /* go to parent directory */
dir = opendir(".");
file_info = readdir(dir);
lstat(file_info->d_name, &stat_buf);
parent_ino = stat_buf.st_ino;
if(itself_ino == parent_ino) {
/*closedir(dir);*/
break;
} else {
strcpy(Slash, "/");
strcpy(Current, file_info->d_name);
strcat(Slash, Current); /* add "/" as the first */
strcat(Slash, Path); /* charcter of the directory */
/* check the length of the pathname */
if(strlen(Slash) >= PATH_MAX) {
fprintf(stderr, "Error! Path too long!\n");
exit(0);
}
/* save the full pathname */
strcpy(Path, Slash);
}
closedir(dir);
}
/* print the full path of the current working directory */
printf("%s\n", Path);
return 0;
}
It's just realpath:
if (realpath(".", &Path) == NULL) {
// handle error
}
However maybe you aim at getcwd or get_current_dir_name.
printf("%s\n", get_current_dir_name());
I'm trying to detect text file is empty or not in C.
(values are initialized in NULL)
Whenever read first in value(using fscanf), it always returns file has zero,
even if it has value "0" or "empty".
How can I know the target text file is empty or not?
(it should be distinguished even it has "0" in first letter)
If the file was successfully open for read, as per fopen(filename, "r"), you can verify if it is empty before any read operation this way:
int is_empty_file(FILE *fp) {
int c = getc(fp);
if (c == EOF)
return 1;
ungetc(c, fp);
return 0;
}
ungetc() is guaranteed to work for at least one character. The above function will return 1 if the file is empty or if it cannot be read, due to an I/O error. You can tell which by testing ferr(fp) or feof(fp).
If the file is a stream associated to a device or a terminal, the test code will block until at least one byte can be read, or end of file is signaled.
If the file is a regular file, you could also use a system specific API to determine the file size, such as stat, lstat, fstat (on Posix systems).
If you're under Linux, you can use stat or fstat:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);
Will give you this information:
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for file system I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};
To check for the size using stat:
struct stat info;
if(stat(filepath, &info) < 0) {
/* error */
}
if(info.st_size == 0) {
/* file is empty */
}
Or using fstat:
int fd = open(filepath, O_RDONLY);
struct stat info;
if(fstat(fd, &info) < 0) {
/* error */
}
if(info.st_size == 0) {
/* file is empty */
}
you can do using ftell
#include <stdio.h>
int get_file_size (char *filename) {
FILE *fp;
int len;
fp = fopen(filename, "r");
if( fp == NULL ) {
perror ("Error opening file");
return(-1);
}
fseek(fp, 0, SEEK_END);
len = ftell(fp);
fclose(fp);
return(len);
}
I'm trying to write a function that mimics the output of the ls command in Unix. I was originally trying to perform this using scandir and alphasort, and this did indeed print the files in the directory, and it did sort them, but for some reason, this sorted list does not seem to match the same "sorted list" of filenames that ls gives.
For example, if I have a directory that contains file.c, FILE.c, and ls.c.
ls displays them in the order: file.c FILE.c ls.c
But when I sort it using alphasort/scandir, it sorts them as: FILE.c file.c ls.c
How does ls sort the files in the directory such that it gives such a differently ordered result?
To emulate default ls -1 behaviour, make your program locale-aware by calling
setlocale(LC_ALL, "");
near the beginning of your main(), and use
count = scandir(dir, &array, my_filter, alphasort);
where my_filter() is a function that returns 0 for names that begin with a dot ., and 1 for all others. alphasort() is a POSIX function that uses the locale collation order, same order as strcoll().
The basic implementation is something along the lines of
#define _POSIX_C_SOURCE 200809L
#define _ATFILE_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <locale.h>
#include <string.h>
#include <dirent.h>
#include <stdio.h>
#include <errno.h>
static void my_print(const char *name, const struct stat *info)
{
/* TODO: Better output; use info too, for 'ls -l' -style output? */
printf("%s\n", name);
}
static int my_filter(const struct dirent *ent)
{
/* Skip entries that begin with '.' */
if (ent->d_name[0] == '.')
return 0;
/* Include all others */
return 1;
}
static int my_ls(const char *dir)
{
struct dirent **list = NULL;
struct stat info;
DIR *dirhandle;
int size, i, fd;
size = scandir(dir, &list, my_filter, alphasort);
if (size == -1) {
const int cause = errno;
/* Is dir not a directory, but a single entry perhaps? */
if (cause == ENOTDIR && lstat(dir, &info) == 0) {
my_print(dir, &info);
return 0;
}
/* Print out the original error and fail. */
fprintf(stderr, "%s: %s.\n", dir, strerror(cause));
return -1;
}
/* We need the directory handle for fstatat(). */
dirhandle = opendir(dir);
if (!dirhandle) {
/* Print a warning, but continue. */
fprintf(stderr, "%s: %s\n", dir, strerror(errno));
fd = AT_FDCWD;
} else {
fd = dirfd(dirhandle);
}
for (i = 0; i < size; i++) {
struct dirent *ent = list[i];
/* Try to get information on ent. If fails, clear the structure. */
if (fstatat(fd, ent->d_name, &info, AT_SYMLINK_NOFOLLOW) == -1) {
/* Print a warning about it. */
fprintf(stderr, "%s: %s.\n", ent->d_name, strerror(errno));
memset(&info, 0, sizeof info);
}
/* Describe 'ent'. */
my_print(ent->d_name, &info);
}
/* Release the directory handle. */
if (dirhandle)
closedir(dirhandle);
/* Discard list. */
for (i = 0; i < size; i++)
free(list[i]);
free(list);
return 0;
}
int main(int argc, char *argv[])
{
int arg;
setlocale(LC_ALL, "");
if (argc > 1) {
for (arg = 1; arg < argc; arg++) {
if (my_ls(argv[arg])) {
return EXIT_FAILURE;
}
}
} else {
if (my_ls(".")) {
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
Note that I deliberately made this more complex than strictly needed for your purposes, because I did not want you to just copy and paste the code. It will be easier for you to compile, run, and investigate this program, then port the needed changes -- possibly just the one setlocale("", LC_ALL); line! -- to your own program, than try and explain to your teacher/lecturer/TA why the code looks like it was copied verbatim from somewhere else.
The above code works even for files specified on the command line (the cause == ENOTDIR part). It also uses a single function, my_print(const char *name, const struct stat *info) to print each directory entry; and to do that, it does call stat for each entry.
Instead of constructing a path to the directory entry and calling lstat(), my_ls() opens a directory handle, and uses fstatat(descriptor, name, struct stat *, AT_SYMLINK_NOFOLLOW) to gather the information in basically the same manner as lstat() would, but name being a relative path starting at the directory specified by descriptor (dirfd(handle), if handle is an open DIR *).
It is true that calling one of the stat functions for each directory entry is "slow" (especially if you do /bin/ls -1 style output). However, the output of ls is intended for human consumption; and very often piped through more or less to let the human view it at leisure. This is why I would personally do not think the "extra" stat() call (even when not really needed) is a problem here. Most human users I know of tend to use ls -l or (my favourite) ls -laF --color=auto anyway. (auto meaning ANSI colors are used only if standard output is a terminal; i.e. when isatty(fileno(stdout)) == 1.)
In other words, now that you have the ls -1 order, I would suggest you modify the output to be similar to ls -l (dash ell, not dash one). You only need to modify my_print() for that.
In alphanumeric (dictionary) order.
That changes with language, of course. Try:
$ LANG=C ls -1
FILE.c
file.c
ls.c
And:
$ LANG=en_US.utf8 ls -1
file.c
FILE.c
ls.c
That is related to the "collating order". Not a simple issue by any measure.
I try to write program where part of it is listing all directories (especially starting from /), but I have a problem with /proc/self which is infinitely recursive (I get /proc/self/task/4300/fd/3/proc/self/task/4300/fd/3/proc/self/task/4300/fd/3/proc/... and so on). What is nice way to deal with it?
EDIT: Program is written in C language and I use opendir(), readdir()
You can use the S_ISLNK macro to test the st_mode field returned by a call to lstat. If the file is a symbolic link, do not try to follow it.
[user#machine:~]:./list | grep link
/proc/mounts is a symbolic link
/proc/self is a symbolic link
Example code
#include <stdio.h> // For perror
#include <stdlib.h>
#include <sys/types.h> // For stat, opendir, readdir
#include <sys/stat.h> // For stat
#include <unistd.h> // For stat
#include <dirent.h> // For opendir, readdir
const char *prefix = "/proc";
int main(void)
{
DIR *dir;
struct dirent *entry;
int result;
struct stat status;
char path[PATH_MAX];
dir = opendir(prefix);
if (!dir)
{
perror("opendir");
exit(1);
}
entry = readdir(dir);
while (entry)
{
result = snprintf(path, sizeof(path), "%s", prefix);
snprintf(&path[result], sizeof(path) - result, "/%s", entry->d_name);
printf("%s", path);
result = lstat(path, &status);
if (-1 == result)
{
printf("\n");
perror("stat");
exit(2);
}
if (S_ISLNK(status.st_mode))
{
printf("%s", " is a symbolic link");
}
printf("\n");
entry = readdir(dir);
}
return(0);
}
From path_resolution(7):
Length limit
There is a maximum length for pathnames. If the pathname (or some intermediate pathname obtained while resolving symbolic links) is too long, an ENAMETOOLONG error
is returned ("File name too long").
I think you should employ similar behaviour: check for too long pathnames.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/param.h>
/* Short & sweet recursive directory scan, finds regular files only.
Good starting point, should work on Linux OS.
Pass the root path, and returns number of dirs and number of files
found.
*/
char *tree_scan( const char *path, int *ndirs, int *nfiles){
DIR *dir;
struct dirent *entry;
char spath[MAXPATHLEN] = "";
if( !(dir = opendir( path))){ perror("opendir"); exit(1);}
for( entry = readdir( dir); entry; entry = readdir( dir)){
sprintf( spath, "%s/%s", path, entry->d_name);
if( entry->d_type == DT_REG){ (*nfiles)++; printf( "%s\n", spath);}
if( entry->d_type == DT_DIR &&
(strcmp( ".", entry->d_name)) &&
(strcmp( "..", entry->d_name))){
(*ndirs)++; tree_scan( spath, ndirs, nfiles);
}
}
closedir( dir);
return(0);
}
/* Call it like so */
int i = 0, l = 0;
tree_scan( "/path", &i, &l);
printf("Scanned %d directories, %d files.\n", i, l);
I don't have a *nix terminal handy, but you could always take a look at the source for ls.c and see how it's done.
The source as part of the gnu core utils can be found here.
I created a ls clone a few years ago in school, and I think I got around it by watching the pathname size as ulidtko mentioned.