-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
psh: implement 'du' command #213
Open
gerard5
wants to merge
1
commit into
master
Choose a base branch
from
gerard5/RTOS-745
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,323 @@ | ||||
/* | ||||
* Phoenix-RTOS | ||||
* | ||||
* du - estimates file space usage | ||||
* | ||||
* Copyright 2024 Phoenix Systems | ||||
* Author: Gerard Swiderski | ||||
* | ||||
* This file is part of Phoenix-RTOS. | ||||
* | ||||
* %LICENSE% | ||||
*/ | ||||
|
||||
#include <errno.h> | ||||
#include <stdio.h> | ||||
#include <stdbool.h> | ||||
#include <stdlib.h> | ||||
#include <string.h> /* memset */ | ||||
#include <unistd.h> /* getopt */ | ||||
#include <limits.h> /* PATH_MAX */ | ||||
#include <dirent.h> /* readdir */ | ||||
#include <sys/stat.h> /* lstat */ | ||||
|
||||
#include "../psh.h" | ||||
|
||||
#define ALREADY_MAX 64 | ||||
#define LEVELS_MAX 64 | ||||
|
||||
|
||||
struct already { | ||||
struct already *next; | ||||
nlink_t nlink; | ||||
ino_t inum; | ||||
int dev; | ||||
}; | ||||
|
||||
|
||||
static struct { | ||||
struct already *already[ALREADY_MAX]; | ||||
char *dent; | ||||
/* options */ | ||||
int levels; | ||||
int8_t ki; | ||||
bool silent; | ||||
bool all; | ||||
bool tot; | ||||
bool crosschk; | ||||
} du_common; | ||||
|
||||
|
||||
/* | ||||
* makePath - make the pathname from the directory name, and the directory entry, | ||||
* placing it in out. If this would overflow return 0, otherwise length of path. | ||||
*/ | ||||
static size_t makePath(size_t dlen, const char *f) | ||||
{ | ||||
char *cp = &du_common.dent[dlen]; | ||||
size_t length = strlen(f); | ||||
|
||||
if ((dlen + length) > (PATH_MAX - 2u)) { | ||||
return 0; | ||||
} | ||||
|
||||
if ((dlen != 0u) && (*(cp - 1) != '/')) { | ||||
*(cp++) = '/'; | ||||
} | ||||
|
||||
memcpy(cp, f, length); | ||||
cp += length; | ||||
*cp = '\0'; | ||||
|
||||
return cp - du_common.dent; | ||||
} | ||||
|
||||
|
||||
/* | ||||
* isDone - have we encountered (dev, inum) before? Returns 1 for yes, 0 for no | ||||
* and remembers (dev, inum, nlink). | ||||
*/ | ||||
static int isDone(dev_t dev, ino_t inum, nlink_t nlink) | ||||
{ | ||||
struct already *ap; | ||||
struct already **pap = &du_common.already[(int)inum % ALREADY_MAX]; | ||||
|
||||
for (;;) { | ||||
ap = *pap; | ||||
if (ap == NULL) { | ||||
break; | ||||
} | ||||
|
||||
if ((ap->inum == inum) && (ap->dev == dev)) { | ||||
ap->nlink--; | ||||
if (ap->nlink == (nlink_t)0) { | ||||
*pap = ap->next; | ||||
free(ap); | ||||
} | ||||
return 1; | ||||
} | ||||
|
||||
pap = &ap->next; | ||||
} | ||||
|
||||
ap = (struct already *)malloc(sizeof(*ap)); | ||||
if (ap == NULL) { | ||||
fprintf(stderr, "du: out of memory\n"); | ||||
return -1; | ||||
} | ||||
|
||||
ap->next = NULL; | ||||
ap->inum = inum; | ||||
ap->dev = dev; | ||||
ap->nlink = nlink - 1; | ||||
*pap = ap; | ||||
|
||||
return 0; | ||||
} | ||||
|
||||
|
||||
static void freeAlreadyList(void) | ||||
{ | ||||
for (int i = 0; i < ALREADY_MAX; ++i) { | ||||
struct already *list = du_common.already[i]; | ||||
while (list != NULL) { | ||||
struct already *temp = list; | ||||
list = list->next; | ||||
free(temp); | ||||
} | ||||
} | ||||
} | ||||
|
||||
|
||||
static size_t roundPrefix(size_t v) | ||||
{ | ||||
switch (du_common.ki) { | ||||
case 1: return (v + (size_t)999) / (size_t)1000; /* kilo */ | ||||
case 2: return (v + (size_t)1023) / (size_t)1024; /* Kibi */ | ||||
default: break; | ||||
} | ||||
return v; | ||||
} | ||||
|
||||
|
||||
/* | ||||
* doDirectory - process the directory. | ||||
* Return the size (in bytes) of the directory and its descendants. | ||||
*/ | ||||
static size_t doDirectory(size_t startPos, int curLevel, dev_t dev) | ||||
{ | ||||
DIR *dp; | ||||
bool maybePrint; | ||||
struct stat stbuf; | ||||
size_t total = 0; | ||||
struct dirent *entry; | ||||
char *d = du_common.dent; | ||||
|
||||
if (lstat(d, &stbuf) < 0) { | ||||
fprintf(stderr, "du: %s: %s\n", d, strerror(errno)); | ||||
return (size_t)0; | ||||
} | ||||
|
||||
if ((stbuf.st_dev != dev) && (dev != 0) && (du_common.crosschk)) { | ||||
return (size_t)0; | ||||
} | ||||
|
||||
if (stbuf.st_size > (off_t)0) { | ||||
total = (size_t)stbuf.st_size; | ||||
} | ||||
|
||||
switch (stbuf.st_mode & S_IFMT) { | ||||
case S_IFDIR: | ||||
/* | ||||
* Directories should not be linked except to "." and "..", so this | ||||
* directory should not already have been done. | ||||
*/ | ||||
maybePrint = !du_common.silent; | ||||
dp = opendir(d); | ||||
if (dp == NULL) { | ||||
break; | ||||
} | ||||
|
||||
for (;;) { | ||||
du_common.dent[startPos] = '\0'; | ||||
entry = readdir(dp); | ||||
if (entry == NULL) { | ||||
break; | ||||
} | ||||
|
||||
if (!((entry->d_name[0] == '.') && ((entry->d_name[1] == '\0') || ((entry->d_name[1] == '.') && (entry->d_name[2] == '\0'))))) { | ||||
const size_t nextPos = makePath(startPos, entry->d_name); | ||||
if (nextPos > 0u) { | ||||
total += doDirectory(nextPos, curLevel - 1, stbuf.st_dev); | ||||
} | ||||
} | ||||
} | ||||
|
||||
closedir(dp); | ||||
break; | ||||
|
||||
case S_IFBLK: | ||||
case S_IFCHR: | ||||
/* st_size for special files is not related to blocks used. */ | ||||
total = (size_t)0; | ||||
/* fall-through */ | ||||
|
||||
default: | ||||
if ((stbuf.st_nlink > (nlink_t)1) && (isDone(stbuf.st_dev, stbuf.st_ino, stbuf.st_nlink) != 0)) { | ||||
return (size_t)0; | ||||
} | ||||
maybePrint = du_common.all; | ||||
break; | ||||
} | ||||
|
||||
|
||||
if ((curLevel >= du_common.levels) || ((maybePrint) && (curLevel >= 0))) { | ||||
printf("%zu\t%s\n", roundPrefix(total), d); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps this can be used: phoenix-rtos-utils/psh/pshapp/pshapp.c Line 210 in 7b6b834
To not reimplement existing functionality |
||||
} | ||||
|
||||
return total; | ||||
} | ||||
|
||||
|
||||
static void psh_du_info(void) | ||||
{ | ||||
printf("estimates file space usage"); | ||||
} | ||||
|
||||
|
||||
static void psh_du_usage(void) | ||||
{ | ||||
fprintf(stderr, "Usage: du [-acsxkKh] [-d depth] [startdir]\n"); | ||||
} | ||||
|
||||
|
||||
static int psh_du(int argc, char **argv) | ||||
{ | ||||
int opt; | ||||
size_t total = 0; | ||||
const char *startdir = "."; | ||||
long int tmp = 0; | ||||
|
||||
memset(&du_common, 0, sizeof(du_common)); | ||||
du_common.levels = LEVELS_MAX; | ||||
du_common.dent = calloc(1, PATH_MAX); | ||||
if (du_common.dent == NULL) { | ||||
fprintf(stderr, "du: out of memory\n"); | ||||
return EXIT_FAILURE; | ||||
} | ||||
|
||||
for (;;) { | ||||
opt = getopt(argc, argv, "acsxd:kKh"); | ||||
if (opt < 0) { | ||||
break; | ||||
} | ||||
|
||||
char *endp = optarg; | ||||
switch (opt) { | ||||
case 'a': | ||||
du_common.all = 1; | ||||
break; | ||||
|
||||
case 'c': | ||||
du_common.tot = 1; | ||||
break; | ||||
|
||||
case 's': | ||||
du_common.silent = 1; | ||||
break; | ||||
|
||||
case 'x': | ||||
du_common.crosschk = 1; | ||||
break; | ||||
|
||||
case 'd': | ||||
tmp = strtol(optarg, &endp, 10); | ||||
if ((endp == NULL) || (*endp != '\0') || (tmp < 0) || (tmp > LEVELS_MAX)) { | ||||
fprintf(stderr, "du: invalid depth value\n"); | ||||
return EXIT_FAILURE; | ||||
} | ||||
du_common.levels = (int)tmp; | ||||
break; | ||||
|
||||
case 'k': | ||||
du_common.ki = 1; /* kilo */ | ||||
break; | ||||
|
||||
case 'K': | ||||
du_common.ki = 2; /* kibi */ | ||||
break; | ||||
|
||||
case 'h': /* fall-through */ | ||||
default: | ||||
psh_du_usage(); | ||||
return EXIT_FAILURE; | ||||
} | ||||
} | ||||
|
||||
do { | ||||
if (optind < argc) { | ||||
startdir = argv[optind++]; | ||||
} | ||||
const size_t startPos = makePath(0, startdir); | ||||
if (startPos > 0u) { | ||||
total += doDirectory(startPos, du_common.levels, 0); | ||||
} | ||||
} while (optind < argc); | ||||
|
||||
freeAlreadyList(); | ||||
|
||||
free(du_common.dent); | ||||
|
||||
if (du_common.tot) { | ||||
printf("%zu\ttotal\n", roundPrefix(total)); | ||||
} | ||||
|
||||
return EXIT_SUCCESS; | ||||
} | ||||
|
||||
|
||||
static void __attribute__((constructor)) du_registerapp(void) | ||||
{ | ||||
static psh_appentry_t app = { .name = "du", .run = psh_du, .info = psh_du_info }; | ||||
psh_registerapp(&app); | ||||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please avoid recursion