C, total from unix ls function - c

I have to make ls -l function. My problem is to find the total value from ls -l. Here is how I do it.
if (l_option) {
struct stat s;
stat(dir_name, &s);
printf("total %jd\n", (intmax_t)s.st_size/512);
}
I believe that my solution is right by definition, which is:
"For each directory that is listed, preface the files with a line
`total BLOCKS', where BLOCKS is the total disk allocation for all
files in that directory. The block size currently defaults to 1024
bytes" (info ls) But my function differs from the real ls.
For example:
>ls -l
>total 60
...and in the same directory:
>./ls -l
>total 8
And if I write:
>stat .
>File: `.'
>Size: 4096 Blocks: 8 IO Block: 4096 directory
>...

I fixed it:
n = scandir(path, &namelist, filter, alphasort);
if (l_option) { // flag if -l is given
while (i < n) {
char* temp = (char *) malloc(sizeof(path)+sizeof(namelist[i]->d_name));
strcpy(temp, path); //copy path to temp
stat(strcat(temp, namelist[i]->d_name), &s); // we pass path to + name of file
total += s.st_blocks;
free(temp);
free(namelist[i++]); // optimization rules!
}
free(namelist);
printf("total %d\n", total/2);
}
So basicly, I make new char array containing the dir_name + the name of file, then I get stat structure and use it to find the total.

You should use opendir/readdir/closedir.
#include <dirent.h>
#include <stdio.h>
int main(void)
{
DIR *d;
struct dirent *dir;
d = opendir(".");
if (d)
{
while ((dir = readdir(d)) != NULL)
{
count++;
}
closedir(d);
}
printf("total %jd\n",count);
return(0);
}

Related

How to read all files in the directory and store them in the pointer in the C language program?

Mingw x86_64 v11.2.0
Windows 10 21H2
My current problem is that this function can normally read files and folders in the directory, but if it is saved in the pointer and printed on the screen, a single file will appear repeatedly.
These are the files in the directory.
.
..
main.c
main.exe
README.md
test.txt
The following is the source code I wrote:
#include "../DeleteCompletely.h"
#include <dirent.h>
#define TotalNumber 4096
#define TotalFileNameLen 4096
typedef struct dirent DIRENT;
typedef struct {
DIR *dir_ptr;
DIRENT *dirent_ptr;
char **pathSet;
size_t number;
} snDIR;
static snDIR *getAllFile(const char *path)
{
snDIR *dirSet = (snDIR *)malloc(sizeof(snDIR));
dirSet->dir_ptr = opendir(path);
size_t loopIndex;
dirSet->pathSet = (char **)malloc(sizeof(char **) * TotalNumber);
if(dirSet->dir_ptr) {
dirSet->number = 0;
loopIndex = 0;
while ((dirSet->dirent_ptr = readdir(dirSet->dir_ptr)) != NULL) {
dirSet->pathSet[loopIndex] = (char *)malloc(TotalFileNameLen);
dirSet->pathSet[loopIndex] = dirSet->dirent_ptr->d_name;
dirSet->number++;
loopIndex++;
}
closedir(dirSet->dir_ptr);
}
return dirSet;
}
int main(int argc, char **argv)
{
snDIR *set = getAllFile(".");
for(size_t x = 0; x < set->number; ++x) {
printf("%s\n", set->pathSet[x]);
}
free(set);
return 0;
}
The problem are these assignments:
dirSet->pathSet[loopIndex] = (char *)malloc(TotalFileNameLen);
dirSet->pathSet[loopIndex] = dirSet->dirent_ptr->d_name;
The first make dirSet->pathSet[loopIndex] point to one location. The second make it point somewhere completely different.
Instead of the second assignment you need to copy the string:
strcpy(dirSet->pathSet[loopIndex], dirSet->dirent_ptr->d_name);
You're also wasting quite a lot of memory by always allocating a large amount or memory. The likelihood of the string being that large are rather small. Instead use the string length as the base size:
dirSet->pathSet[loopIndex] = malloc(strlen(dirSet->dirent_ptr->d_name) + 1);
// +1 for the null terminator

How to use scandir() in C to list sorted subdirectories recursively

I'm implementing parts of the Linux ls command in C. I want to sort the contents of directories lexicographically, which I've been doing using scandir(). This is easy enough for listing single directories, but I'm having trouble doing it for listing subdirectories recursively. My current code: (results in a segmentation faults once a directory type is reached)
void recursive(char* arg){
int i;
struct dirent **file_list;
int num;
char* next_dir;
num = scandir(arg, &file_list, NULL, alphasort);
for(i = 0; i < num; i++) {
if(file_list[i]->d_type == DT_DIR) {
if(strcmp(".", file_list[i]->d_name) != 0 && strcmp("..", file_list[i]->d_name) != 0) {
// Directories are printed with a colon to distinguish them from files
printf("%s: \n", file_list[i]->d_name);
strcpy(next_dir, arg);
strcat(next_dir, "/");
strcat(next_dir, file_list[i]->d_name);
printf("\n");
recursive(next_dir);
}
} else {
if(strcmp(".", file_list[i]->d_name) != 0 && strcmp("..", file_list[i]->d_name) != 0) {
printf("%s \n", file_list[i]->d_name);
}
}
}
}
int main(void) {
recursive(".");
return 0;
}
There are two recommended methods for traversing entire filesystem trees in Linux and other POSIXy systems:
nftw(): man 3 nftw
Given an initial path, a callback function, the maximum number of descriptors to use, and a set of flags, nftw() will call the callback function once for every filesystem object in the subtree. The order in which entries in the same directory is called is not specified, however.
This is the POSIX.1 (IEEE 1003) function.
fts_open()/fts_read()/fts_children()/fts_close(): man 3 fts
The fts interface provides a way to traverse filesystem hierarchies. The fts_children() provides a linked list of filesystem entries sorted by the comparison function specified in the fts_open() call. It is rather similar to how scandir() returns an array of filesystem entries, except that the two use very different structures to describe each filesystem entry.
Prior to glibc 2.23 (released in 2016), the Linux (glibc) fts implementation had bugs when using 64-bit file sizes (so on x86-64, or when compiling with -D_FILE_OFFSET_BITS=64).
These are BSD functions (FreeBSD/OpenBSD/macOS), but are available in Linux also.
Finally, there is also the atfile version of scandir(), scandirat(), that returns the filtered and sorted filesystem entries from a specific directory, but in addition to the pathname, it takes a file descriptor to the relative root directory to be used as a parameter. (If AT_FDCWD is used instead of a file descriptor, then scandirat() behaves like scandir().)
The simplest option here is to use nftw(), store all walked paths, and finally sort the paths. For example, walk.c:
// SPDX-License-Identifier: CC0-1.0
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <locale.h>
#include <ftw.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
struct entry {
/* Insert additional properties like 'off_t size' here. */
char *name; /* Always points to name part of pathname */
char pathname[]; /* Full path and name */
};
struct listing {
size_t max; /* Number of entries allocated for */
size_t num; /* Number of entries in the array */
struct entry **ent; /* Array of pointers, one per entry */
};
#define STRUCT_LISTING_INITIALIZER { 0, 0, NULL }
/* Locale-aware sort for arrays of struct entry pointers.
*/
static int entrysort(const void *ptr1, const void *ptr2)
{
const struct entry *ent1 = *(const struct entry **)ptr1;
const struct entry *ent2 = *(const struct entry **)ptr2;
return strcoll(ent1->pathname, ent2->pathname);
}
/* Global variable used by nftw_add() to add to the listing */
static struct listing *nftw_listing = NULL;
static int nftw_add(const char *pathname, const struct stat *info, int typeflag, struct FTW *ftwbuf)
{
const char *name = pathname + ftwbuf->base;
/* These generate no code, just silences the warnings about unused parameters. */
(void)info;
(void)typeflag;
/* Ignore "." and "..". */
if (name[0] == '.' && !name[1])
return 0;
if (name[0] == '.' && name[1] == '.' && !name[2])
return 0;
/* Make sure there is room for at least one more entry in the listing. */
if (nftw_listing->num >= nftw_listing->max) {
const size_t new_max = nftw_listing->num + 1000;
struct entry **new_ent;
new_ent = realloc(nftw_listing->ent, new_max * sizeof (struct entry *));
if (!new_ent)
return -ENOMEM;
nftw_listing->max = new_max;
nftw_listing->ent = new_ent;
}
const size_t pathnamelen = strlen(pathname);
struct entry *ent;
/* Allocate memory for this entry.
Remember to account for the name, and the end-of-string terminator, '\0', at end of name. */
ent = malloc(sizeof (struct entry) + pathnamelen + 1);
if (!ent)
return -ENOMEM;
/* Copy other filesystem entry properties to ent here; say 'ent->size = info->st_size;'. */
/* Copy pathname, including the end-of-string terminator, '\0'. */
memcpy(ent->pathname, pathname, pathnamelen + 1);
/* The name pointer is always to within the pathname. */
ent->name = ent->pathname + ftwbuf->base;
/* Append. */
nftw_listing->ent[nftw_listing->num++] = ent;
return 0;
}
/* Scan directory tree starting at path, adding the entries to struct listing.
Note: the listing must already have been properly initialized!
Returns 0 if success, nonzero if error; -1 if errno is set to indicate error.
*/
int scan_tree_sorted(struct listing *list, const char *path)
{
if (!list) {
errno = EINVAL;
return -1;
}
if (!path || !*path) {
errno = ENOENT;
return -1;
}
nftw_listing = list;
int result = nftw(path, nftw_add, 64, FTW_DEPTH);
nftw_listing = NULL;
if (result < 0) {
errno = -result;
return -1;
} else
if (result > 0) {
errno = 0;
return result;
}
if (list->num > 2)
qsort(list->ent, list->num, sizeof list->ent[0], entrysort);
return 0;
}
int main(int argc, char *argv[])
{
struct listing list = STRUCT_LISTING_INITIALIZER;
setlocale(LC_ALL, "");
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *arg0 = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", arg0);
fprintf(stderr, " %s .\n", arg0);
fprintf(stderr, " %s TREE [ TREE ... ]\n", arg0);
fprintf(stderr, "\n");
fprintf(stderr, "This program lists all files and directories starting at TREE,\n");
fprintf(stderr, "in sorted order.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
for (int arg = 1; arg < argc; arg++) {
if (scan_tree_sorted(&list, argv[arg])) {
fprintf(stderr, "%s: Error scanning directory tree: %s.\n", argv[arg], strerror(errno));
return EXIT_FAILURE;
}
}
printf("Found %zu entries:\n", list.num);
for (size_t i = 0; i < list.num; i++)
printf("\t%s\t(%s)\n", list.ent[i]->pathname, list.ent[i]->name);
return EXIT_SUCCESS;
}
Compile using gcc -Wall -Wextra -O2 walk.c -o walk, and run using e.g. ./walk ...
The scan_tree_sorted() function calls nftw() for the directory specified, updating the global variable nftw_listing so that the nftw_add() callback function can add each new directory entry to it. If the listing contains more that one entry afterwards, it is sorted using qsort() and a locale-aware comparison function (based on strcoll()).
nftw_add() skips . and .., and adds every other pathname to the listing structure nftw_listing. It automatically grows the array as needed in linear fashion; the new_max = nftw_listing->num + 1000; means we allocate in units of a thousand (pointers).
The scan_tree_sorted() can be called multiple times with the same listing as the target, if one wants to list disjoint subtrees in one listing. Note, however, that it does not check for duplicates, although those could easily be filtered out after the qsort.

Is there a way to chdir() into a directory where only the beginning of the directory name is known?

So, basically, I have a program that forks off a child process and creates a directory with the name + the child processes ID. This is done in another part of the code.
So, lets say the user names the directory "TestDir#". It will be TestDir12342 or something similar in the end.
So, later on, the user could enter a search term for that directory by typing in TestDir#. I want to lope off the "#", and have chdir() search for a directory that begins with that name, "TestDir". I don't have to worry about repeat files or similarly named files for this program.
Does anyone know a simple way to do this with chdir()? I have tried many different test code, but I am at a lose.
I have also attempted to store the child process ID in the parent process, but for some reason I can never get them to match. I am aware that fork() gives the child process ID in return to the parent. Yet, for some reason, the program refuses to make them match.
So, I am trying this as a workaround (searching the beginning part of the file name). Thanks for any assistance if someone knows of a way to do this.
readdir can be used to get the entries of the directory.
The following searchFirstDir finds the first prefix-matched directory. (tested in Ubuntu Linux)
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
int searchFirstDir(const char *workingDir, const char *prefix, char *resultBuffer, int bufferLen)
{
DIR *pDir = NULL;
int found = 0;
// opendir
{
pDir = opendir(workingDir);
if (pDir == NULL) {
perror("ERROR: opendir");
return -1;
}
}
// readdir
{
int ret;
struct dirent *pEntry;
struct dirent *result;
int prefixLen = strlen(prefix);
// refer: man readdir (in Linux)
{
long name_max = pathconf(workingDir, _PC_NAME_MAX);
if (name_max == -1) /* Limit not defined, or error */
name_max = 255; /* Take a guess */
size_t len = offsetof(struct dirent, d_name) + name_max + 1;
pEntry = malloc(len);
}
do {
ret = readdir_r(pDir, pEntry, &result);
if (ret) {
perror("ERROR: readdir_r");
break;
}
if (pEntry->d_type == DT_DIR && strncmp(pEntry->d_name, prefix, prefixLen) == 0) {
strncpy(resultBuffer, pEntry->d_name, bufferLen);
found++;
break;
}
} while(ret == 0 && result != NULL);
free(pEntry);
}
// closedir
closedir(pDir);
return found > 0 ? 0 : -1;
}
int main(int argc, char *argv)
{
char resultBuffer[255];
int ret = searchFirstDir("workingdirectory", "TestDir", resultBuffer, 255);
if (ret == 0) {
printf("First matched directory: %s\n", resultBuffer);
}
}
Yes, there is a way to perform the requested type of chdir taking advantage of globbing, i.e. filename expansion using a wildcard of "*", as follows:
#include <string.h>
#include <glob.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
/* Convert a wildcard pattern into a list of blank-separated
filenames which match the wildcard. */
char * glob_pattern(char *wildcard)
{
char *gfilename;
size_t cnt, length;
glob_t glob_results;
char **p;
glob(wildcard, GLOB_NOCHECK, 0, &glob_results);
/* How much space do we need? */
for (p = glob_results.gl_pathv, cnt = glob_results.gl_pathc;
cnt; p++, cnt--)
length += strlen(*p) + 1;
/* Allocate the space and generate the list. */
gfilename = (char *) calloc(length, sizeof(char));
for (p = glob_results.gl_pathv, cnt = glob_results.gl_pathc;
cnt; p++, cnt--)
{
strcat(gfilename, *p);
if (cnt > 1)
strcat(gfilename, " ");
}
globfree(&glob_results);
return gfilename;
}
int main() {
char *directory;
int ret;
directory = glob_pattern("te*");
ret = chdir (directory);
printf("Result of chdir: %d\n",ret);
}
Note: The "globbing" portion of the code comes from here
Linux has a glob utility so if you wish to do the same in C, you have to write the code yourself as this example portrays. When the program finishes however you will be back in the directory you originally used to run this script. When the code does a successful directory change, the return result is zero. Note, this code executed in a directory containing a subdirectory named "test".

Want to fill Structure for Metadata

Here i have one directory which has number of files.
I want to fill this files all information in one structure.
I have two structures which are following.
struct files {
char *file_name;
int file_size;
};
typedef struct file_header {
int file_count;
struct files file[variable as per number of files];
} metadata;
i want to make one header which contains all information regarding these files.
like if i have 3 files than i want to make this structure like this in file_count = 3 and how can i allocate second variable value? and want to store file name and file size as per file.
i want file structure like this
file_count = 3
file[0].file_name = "a.txt"
file[0].file_size = 1024
file[1].file_name = "b.txt"
file[1].file_size = 818
file[2].file_name = "c.txt"
file[2].file_size = 452
I have all logic about file name and file size but how can i fill these things in this structure.?
Code :
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
char path[1024] = "/home/test/main/Integration/testing/package_DIR";
//int count = 5;
struct files {
char *file_name;
int file_size;
};
typedef struct file_header {
int file_count;
struct files file[5];
} metadata;
metadata *create_header();
int main() {
FILE *file = fopen("/home/test/main/Integration/testing/file.txt", "w");
metadata *header;
header = create_header();
if(header != NULL)
{
printf("size of Header is %d\n",sizeof(metadata));
}
if (file != NULL) {
if (fwrite(&header, sizeof(metadata), 1, file) < 1) {
puts("short count on fwrite");
}
fclose(file);
}
file = fopen("/home/test/main/Integration/testing/file.txt", "rb");
if (file != NULL) {
metadata header = { 0 };
if (fread(&header, sizeof(header), 1, file) < 1) {
puts("short count on fread");
}
fclose(file);
printf("File Name = %s\n", header.file[0].file_name);
printf("File count = %d\n", header.file_count);
printf("File Size = %d\n", header.file[0].file_size);
}
return 0;
}
metadata *create_header()
{
int file_count = 0;
DIR * dirp;
struct dirent * entry;
dirp = opendir(path);
metadata *header = (metadata *)malloc(sizeof(metadata));
while ((entry = readdir(dirp)) != NULL) {
if (entry->d_type == DT_REG) { /* If the entry is a regular file */
header->file[file_count].file_name = (char *)malloc(sizeof(char)*strlen(entry->d_name));
strcpy(header->file[file_count].file_name,entry->d_name);
//Put static but i have logic for this i will apply later.
header->file[file_count].file_size = 10;
file_count++;
}
}
header->file_count = file_count;
closedir(dirp);
//printf("File Count : %d\n", file_count);
return header;
}
output :
size of Header is 88
ile Name = �~8
File count = 29205120
File Size = -586425488
Its shows different output. so whats problem here?
Among other things, you are using sizeof on a pointer variable, but seem to think that gives you the size of the object being pointed to. It doesn't. To do that, use the asterisk operator to make the expression have the type that the pointer points at:
printf("size of Header is %d\n", sizeof *metadata);
As a side note, notice that sizeof is not a function, so you don't need parenthesis. When you do see parenthesis, that's when they're part of the expression (a cast).
You are not leaving enough room for the null-terminator:
header->file[file_count].file_name = (char *)malloc(sizeof(char)*strlen(entry->d_name));

Imitating ls in ansi C

I have a code to mimic ls -la in ansi C, but when I change the directory from . (current directory) to any other it keep saying No such file or directory, any ideas why?
code:
DIR * mydir;
struct dirent * mydirent;
struct stat st;
char outstr[100];
struct tm *tmp;
mydir = opendir("..");
while ((mydirent=readdir(mydir))!=NULL)
if ( stat(mydirent->d_name,&st) != -1 ) {
tmp = localtime(&st.st_mtime);
if (tmp == NULL)
perror("localtime ERROR: ");
else {
strftime(outstr, sizeof(outstr), "%d/%m/%Y", tmp);
printf("%o\t%d\t%d\t%d\t%d\t%s\t%s\n",
st.st_mode, st.st_nlink, st.st_uid, st.st_gid,
st.st_size, outstr, mydirent->d_name);
}
}
else
perror("stat ERROR: ");
closedir(mydir);
You need to concatenate the directory path and the file name.
stat(mydirent->d_name,&st) /* d_name is just the name, not the full path. */
Use s(n)printf or something like that:
sprintf(fullpath, "%s/%s", dirpath, mydirent->d_name);
stat(fullpath, &st);
The problem is, as already stated by #cnicutar, that stat wants the filename in the form dir/file. The problems are:
The / may not work on every operating system.
You need to allocate memory for the composed string, do overflow checks ...
If you don't insist on ANSI and can live with POSIX instead, then you can try fstatat and dirfd:
int dirfd(DIR *dirp); // convert DIR * from opendir() to dirhandle
int fstatat(int dirfd, const char *pathname, struct stat *buf, int flags);
With fstatat the pathname is relative to the directory handle and you can point pathname directly to struct dirent.d_name.

Resources