Split calc.c into separate components (dir_(mem|scan|common).c)
The architecture is explained in dir.h. The reasons for these changes is two-fold: - calc.c was too complex, it simply did too many things.399ccdeb
is a nice example of that: Should have been an easy fix, but it introduced a segfault (fixed in0b49021a
), and added a small memory leak. - This architecture features a pluggable input/output system, which should make a file export/import feature relatively simple. The current commit does not feature any user interface, so there's no feedback yet when scanning a directory. I'll get to that in a bit. I've also not tested the new scanning code very well yet, so I might have introduced some bugs.
Этот коммит содержится в:
родитель
0b49021a6c
Коммит
0fd7dec7b0
@ -2,9 +2,11 @@ bin_PROGRAMS=ncdu
|
|||||||
|
|
||||||
ncdu_SOURCES=\
|
ncdu_SOURCES=\
|
||||||
src/browser.c\
|
src/browser.c\
|
||||||
src/calc.c\
|
|
||||||
src/delete.c\
|
src/delete.c\
|
||||||
src/dirlist.c\
|
src/dirlist.c\
|
||||||
|
src/dir_common.c\
|
||||||
|
src/dir_mem.c\
|
||||||
|
src/dir_scan.c\
|
||||||
src/exclude.c\
|
src/exclude.c\
|
||||||
src/help.c\
|
src/help.c\
|
||||||
src/main.c\
|
src/main.c\
|
||||||
@ -13,8 +15,8 @@ ncdu_SOURCES=\
|
|||||||
|
|
||||||
noinst_HEADERS=\
|
noinst_HEADERS=\
|
||||||
src/browser.h\
|
src/browser.h\
|
||||||
src/calc.h\
|
|
||||||
src/delete.h\
|
src/delete.h\
|
||||||
|
src/dir.h\
|
||||||
src/dirlist.h\
|
src/dirlist.h\
|
||||||
src/exclude.h\
|
src/exclude.h\
|
||||||
src/global.h\
|
src/global.h\
|
||||||
|
@ -345,8 +345,10 @@ int browse_key(int ch) {
|
|||||||
|
|
||||||
/* and other stuff */
|
/* and other stuff */
|
||||||
case 'r':
|
case 'r':
|
||||||
if(sel != NULL)
|
if(sel != NULL) {
|
||||||
calc_init(getpath(sel->parent), sel->parent);
|
dir_mem_init(sel->parent);
|
||||||
|
dir_scan_init(getpath(sel->parent));
|
||||||
|
}
|
||||||
info_show = 0;
|
info_show = 0;
|
||||||
break;
|
break;
|
||||||
case 'q':
|
case 'q':
|
||||||
|
530
src/calc.c
530
src/calc.c
@ -1,530 +0,0 @@
|
|||||||
/* ncdu - NCurses Disk Usage
|
|
||||||
|
|
||||||
Copyright (c) 2007-2012 Yoran Heling
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included
|
|
||||||
in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
||||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
||||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
||||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "global.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
|
|
||||||
#include "khash.h"
|
|
||||||
|
|
||||||
/* set S_BLKSIZE if not defined already in sys/stat.h */
|
|
||||||
#ifndef S_BLKSIZE
|
|
||||||
# define S_BLKSIZE 512
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* external vars */
|
|
||||||
char calc_smfs = 0;
|
|
||||||
|
|
||||||
/* global vars for internal use */
|
|
||||||
static char failed; /* 1 on fatal error */
|
|
||||||
static char *curpath; /* last lstat()'ed item, used for excludes matching and display */
|
|
||||||
static char *lasterr; /* last unreadable dir/item */
|
|
||||||
static char errmsg[128]; /* error message, when failed=1 */
|
|
||||||
static struct dir *root; /* root directory struct we're calculating */
|
|
||||||
static struct dir *orig; /* original directory, when recalculating */
|
|
||||||
static dev_t curdev; /* current device we're calculating on */
|
|
||||||
static int anpos; /* position of the animation string */
|
|
||||||
static int curpathl = 0, lasterrl = 0;
|
|
||||||
|
|
||||||
|
|
||||||
// Table of struct dir items with more than one link (in order to detect hard links)
|
|
||||||
#define links_dir_hash(d) (kh_int64_hash_func((khint64_t)d->dev) ^ kh_int64_hash_func((khint64_t)d->ino))
|
|
||||||
#define links_dir_equal(a, b) ((a)->dev == (b)->dev && (a)->ino == (b)->ino)
|
|
||||||
KHASH_INIT(hl, struct dir *, char, 0, links_dir_hash, links_dir_equal);
|
|
||||||
static khash_t(hl) *links = NULL;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* adds name to curpath */
|
|
||||||
static void calc_enterpath(char *name) {
|
|
||||||
int n;
|
|
||||||
|
|
||||||
n = strlen(curpath)+strlen(name)+2;
|
|
||||||
if(n > curpathl) {
|
|
||||||
curpathl = n;
|
|
||||||
curpath = realloc(curpath, curpathl);
|
|
||||||
}
|
|
||||||
if(curpath[1])
|
|
||||||
strcat(curpath, "/");
|
|
||||||
strcat(curpath, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* removes last component from curpath */
|
|
||||||
static void calc_leavepath() {
|
|
||||||
char *tmp;
|
|
||||||
if((tmp = strrchr(curpath, '/')) == NULL)
|
|
||||||
strcpy(curpath, "/");
|
|
||||||
else if(tmp != curpath)
|
|
||||||
tmp[0] = 0;
|
|
||||||
else
|
|
||||||
tmp[1] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* recursively checks a dir structure for hard links and fills the lookup array */
|
|
||||||
static void calc_hlink_init(struct dir *d) {
|
|
||||||
struct dir *t;
|
|
||||||
|
|
||||||
for(t=d->sub; t!=NULL; t=t->next)
|
|
||||||
calc_hlink_init(t);
|
|
||||||
|
|
||||||
if(!(d->flags & FF_HLNKC))
|
|
||||||
return;
|
|
||||||
int r;
|
|
||||||
kh_put(hl, links, d, &r);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* checks an individual file and updates the flags and cicrular linked list,
|
|
||||||
* also updates the sizes of the parent dirs */
|
|
||||||
static void calc_hlink_check(struct dir *d) {
|
|
||||||
struct dir *t, *pt, *par;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
d->flags |= FF_HLNKC;
|
|
||||||
|
|
||||||
// add to links table
|
|
||||||
khiter_t k = kh_put(hl, links, d, &i);
|
|
||||||
|
|
||||||
/* found in the table? update hlnk */
|
|
||||||
if(!i) {
|
|
||||||
t = d->hlnk = kh_key(links, k);
|
|
||||||
if(t->hlnk != NULL)
|
|
||||||
for(t=t->hlnk; t->hlnk!=d->hlnk; t=t->hlnk)
|
|
||||||
;
|
|
||||||
t->hlnk = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* now update the sizes of the parent directories,
|
|
||||||
* This works by only counting this file in the parent directories where this
|
|
||||||
* file hasn't been counted yet, which can be determined from the hlnk list.
|
|
||||||
* XXX: This may not be the most efficient algorithm to do this */
|
|
||||||
for(i=1,par=d->parent; i&∥ par=par->parent) {
|
|
||||||
if(d->hlnk)
|
|
||||||
for(t=d->hlnk; i&&t!=d; t=t->hlnk)
|
|
||||||
for(pt=t->parent; i&&pt; pt=pt->parent)
|
|
||||||
if(pt==par)
|
|
||||||
i=0;
|
|
||||||
if(i) {
|
|
||||||
par->size += d->size;
|
|
||||||
par->asize += d->asize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int calc_item(struct dir *par, char *name) {
|
|
||||||
struct dir *t, *d;
|
|
||||||
struct stat fs;
|
|
||||||
|
|
||||||
if(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* allocate dir and fix references */
|
|
||||||
d = calloc(SDIRSIZE+strlen(name), 1);
|
|
||||||
d->parent = par;
|
|
||||||
d->next = par->sub;
|
|
||||||
par->sub = d;
|
|
||||||
if(d->next)
|
|
||||||
d->next->prev = d;
|
|
||||||
strcpy(d->name, name);
|
|
||||||
|
|
||||||
#ifdef __CYGWIN__
|
|
||||||
/* /proc/registry names may contain slashes */
|
|
||||||
if(strchr(d->name, '/') || strchr(d->name, '\\')) {
|
|
||||||
d->flags |= FF_ERR;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* lstat */
|
|
||||||
if(lstat(name, &fs)) {
|
|
||||||
d->flags |= FF_ERR;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check for excludes and same filesystem */
|
|
||||||
if(exclude_match(curpath))
|
|
||||||
d->flags |= FF_EXL;
|
|
||||||
|
|
||||||
if(calc_smfs && curdev != fs.st_dev)
|
|
||||||
d->flags |= FF_OTHFS;
|
|
||||||
|
|
||||||
/* determine type of this item */
|
|
||||||
if(S_ISREG(fs.st_mode))
|
|
||||||
d->flags |= FF_FILE;
|
|
||||||
else if(S_ISDIR(fs.st_mode))
|
|
||||||
d->flags |= FF_DIR;
|
|
||||||
|
|
||||||
/* update the items count of the parent dirs */
|
|
||||||
if(!(d->flags & FF_EXL))
|
|
||||||
for(t=d->parent; t!=NULL; t=t->parent)
|
|
||||||
t->items++;
|
|
||||||
|
|
||||||
/* count the size */
|
|
||||||
if(!(d->flags & FF_EXL || d->flags & FF_OTHFS)) {
|
|
||||||
d->size = fs.st_blocks * S_BLKSIZE;
|
|
||||||
d->asize = fs.st_size;
|
|
||||||
/* only update the sizes of the parents if it's not a hard link */
|
|
||||||
if(S_ISDIR(fs.st_mode) || fs.st_nlink <= 1)
|
|
||||||
for(t=d->parent; t!=NULL; t=t->parent) {
|
|
||||||
t->size += d->size;
|
|
||||||
t->asize += d->asize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hard link checking (also takes care of updating the sizes of the parents) */
|
|
||||||
d->ino = fs.st_ino;
|
|
||||||
d->dev = fs.st_dev;
|
|
||||||
if(!S_ISDIR(fs.st_mode) && fs.st_nlink > 1)
|
|
||||||
calc_hlink_check(d);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* recursively walk through the directory tree,
|
|
||||||
assumes path resides in the cwd */
|
|
||||||
static int calc_dir(struct dir *dest, char *name) {
|
|
||||||
struct dir *t;
|
|
||||||
DIR *dir;
|
|
||||||
struct dirent *dr;
|
|
||||||
|
|
||||||
if(input_handle(1))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
/* curpath */
|
|
||||||
calc_enterpath(name);
|
|
||||||
|
|
||||||
/* open & chdir into directory */
|
|
||||||
if((dir = opendir(name)) == NULL || chdir(name) < 0) {
|
|
||||||
if(lasterrl <= (int)strlen(curpath)) {
|
|
||||||
lasterrl = strlen(curpath)+1;
|
|
||||||
lasterr = realloc(lasterr, lasterrl);
|
|
||||||
}
|
|
||||||
strcpy(lasterr, curpath);
|
|
||||||
dest->flags |= FF_ERR;
|
|
||||||
t = dest;
|
|
||||||
while((t = t->parent) != NULL)
|
|
||||||
t->flags |= FF_SERR;
|
|
||||||
calc_leavepath();
|
|
||||||
if(dir != NULL)
|
|
||||||
closedir(dir);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read directory */
|
|
||||||
while((dr = readdir(dir)) != NULL) {
|
|
||||||
calc_enterpath(dr->d_name);
|
|
||||||
if(calc_item(dest, dr->d_name))
|
|
||||||
dest->flags |= FF_ERR;
|
|
||||||
if(input_handle(1)) {
|
|
||||||
calc_leavepath();
|
|
||||||
closedir(dir);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
calc_leavepath();
|
|
||||||
errno = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(errno) {
|
|
||||||
if(dest->flags & FF_SERR)
|
|
||||||
dest->flags -= FF_SERR;
|
|
||||||
dest->flags |= FF_ERR;
|
|
||||||
}
|
|
||||||
closedir(dir);
|
|
||||||
|
|
||||||
/* error occured while reading this dir, update parent dirs */
|
|
||||||
for(t=dest->sub; t!=NULL; t=t->next)
|
|
||||||
if(t->flags & FF_ERR || t->flags & FF_SERR)
|
|
||||||
dest->flags |= FF_SERR;
|
|
||||||
if(dest->flags & FF_ERR || dest->flags & FF_SERR) {
|
|
||||||
for(t = dest; (t = t->parent) != NULL; )
|
|
||||||
t->flags |= FF_SERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* calculate subdirectories */
|
|
||||||
for(t=dest->sub; t!=NULL; t=t->next)
|
|
||||||
if(t->flags & FF_DIR && !(t->flags & FF_EXL || t->flags & FF_OTHFS))
|
|
||||||
if(calc_dir(t, t->name)) {
|
|
||||||
calc_leavepath();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* chdir back */
|
|
||||||
if(chdir("..") < 0) {
|
|
||||||
failed = 1;
|
|
||||||
strcpy(errmsg, "Couldn't chdir to previous directory");
|
|
||||||
calc_leavepath();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
calc_leavepath();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void calc_draw_progress() {
|
|
||||||
static char antext[15] = "Calculating...";
|
|
||||||
char ani[15];
|
|
||||||
int i;
|
|
||||||
int width = wincols-5;
|
|
||||||
|
|
||||||
nccreate(10, width, !orig ? "Calculating..." : "Recalculating...");
|
|
||||||
|
|
||||||
ncprint(2, 2, "Total items: %-8d size: %s",
|
|
||||||
root->items, formatsize(root->size));
|
|
||||||
ncprint(3, 2, "Current dir: %s", cropstr(curpath, width-17));
|
|
||||||
ncaddstr(8, width-17, "Press q to quit");
|
|
||||||
|
|
||||||
/* show warning if we couldn't open a dir */
|
|
||||||
if(lasterr[0] != '\0') {
|
|
||||||
attron(A_BOLD);
|
|
||||||
ncaddstr(5, 2, "Warning:");
|
|
||||||
attroff(A_BOLD);
|
|
||||||
ncprint(5, 11, "could not open %-32s", cropstr(lasterr, width-28));
|
|
||||||
ncaddstr(6, 3, "some directory sizes may not be correct");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* animation - but only if the screen refreshes more than or once every second */
|
|
||||||
if(update_delay <= 1000) {
|
|
||||||
if(++anpos == 28)
|
|
||||||
anpos = 0;
|
|
||||||
strcpy(ani, " ");
|
|
||||||
if(anpos < 14)
|
|
||||||
for(i=0; i<=anpos; i++)
|
|
||||||
ani[i] = antext[i];
|
|
||||||
else
|
|
||||||
for(i=13; i>anpos-14; i--)
|
|
||||||
ani[i] = antext[i];
|
|
||||||
} else
|
|
||||||
strcpy(ani, antext);
|
|
||||||
ncaddstr(8, 3, ani);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void calc_draw_error(char *cur, char *msg) {
|
|
||||||
int width = wincols-5;
|
|
||||||
nccreate(7, width, "Error!");
|
|
||||||
|
|
||||||
attron(A_BOLD);
|
|
||||||
ncaddstr(2, 2, "Error:");
|
|
||||||
attroff(A_BOLD);
|
|
||||||
|
|
||||||
ncprint(2, 9, "could not open %s", cropstr(cur, width-26));
|
|
||||||
ncprint(3, 4, "%s", cropstr(msg, width-8));
|
|
||||||
ncaddstr(5, width-30, "press any key to continue...");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void calc_draw() {
|
|
||||||
browse_draw();
|
|
||||||
if(failed)
|
|
||||||
calc_draw_error(curpath, errmsg);
|
|
||||||
else
|
|
||||||
calc_draw_progress();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int calc_key(int ch) {
|
|
||||||
if(failed)
|
|
||||||
return 1;
|
|
||||||
if(ch == 'q')
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int calc_process() {
|
|
||||||
char *path = NULL, *name = NULL;
|
|
||||||
struct stat fs;
|
|
||||||
struct dir *t;
|
|
||||||
int n;
|
|
||||||
|
|
||||||
/* create initial links set */
|
|
||||||
links = kh_init(hl);
|
|
||||||
if(orig) {
|
|
||||||
for(t=orig; t->parent!=NULL; t=t->parent)
|
|
||||||
;
|
|
||||||
calc_hlink_init(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check root directory */
|
|
||||||
if((path = path_real(curpath)) == NULL) {
|
|
||||||
failed = 1;
|
|
||||||
strcpy(errmsg, "Directory not found");
|
|
||||||
goto calc_fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* split into path and last component */
|
|
||||||
name = strrchr(path, '/');
|
|
||||||
if(name == path) {
|
|
||||||
if(!path[1])
|
|
||||||
name = ".";
|
|
||||||
else {
|
|
||||||
name = malloc(strlen(path));
|
|
||||||
strcpy(name, path+1);
|
|
||||||
path[1] = 0;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
*(name++) = 0;
|
|
||||||
|
|
||||||
/* we need to chdir so we can provide relative paths for lstat() and opendir(),
|
|
||||||
* this to prevent creating path names longer than PATH_MAX */
|
|
||||||
if(path_chdir(path) < 0) {
|
|
||||||
failed = 1;
|
|
||||||
strcpy(errmsg, "Couldn't chdir into directory");
|
|
||||||
free(path);
|
|
||||||
goto calc_fail;
|
|
||||||
}
|
|
||||||
/* would be strange for this to fail, but oh well... */
|
|
||||||
if(lstat(name, &fs) != 0 || !S_ISDIR(fs.st_mode)) {
|
|
||||||
failed = 1;
|
|
||||||
strcpy(errmsg, "Couldn't stat directory");
|
|
||||||
free(path);
|
|
||||||
goto calc_fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* initialize parent dir */
|
|
||||||
n = orig ? strlen(orig->name) : strlen(path)+strlen(name)+1;
|
|
||||||
t = (struct dir *) calloc(1, SDIRSIZE+n);
|
|
||||||
t->size = fs.st_blocks * S_BLKSIZE;
|
|
||||||
t->asize = fs.st_size;
|
|
||||||
t->flags |= FF_DIR;
|
|
||||||
if(orig) {
|
|
||||||
strcpy(t->name, orig->name);
|
|
||||||
t->parent = orig->parent;
|
|
||||||
} else {
|
|
||||||
t->name[0] = 0;
|
|
||||||
if(strcmp(path, "/"))
|
|
||||||
strcpy(t->name, path);
|
|
||||||
if(strcmp(name, ".")) {
|
|
||||||
strcat(t->name, "/");
|
|
||||||
strcat(t->name, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
root = t;
|
|
||||||
curdev = fs.st_dev;
|
|
||||||
|
|
||||||
/* make sure to count this directory entry in its parents at this point */
|
|
||||||
if(orig)
|
|
||||||
for(t=root->parent; t!=NULL; t=t->parent) {
|
|
||||||
t->size += root->size;
|
|
||||||
t->asize += root->asize;
|
|
||||||
t->items += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* update curpath */
|
|
||||||
if(strcmp(name, ".")) {
|
|
||||||
if(curpathl <= (int)strlen(path)) {
|
|
||||||
curpathl = strlen(path)+1;
|
|
||||||
curpath = realloc(curpath, curpathl);
|
|
||||||
}
|
|
||||||
strcpy(curpath, path);
|
|
||||||
} else
|
|
||||||
curpath[0] = 0;
|
|
||||||
|
|
||||||
/* calculate */
|
|
||||||
n = calc_dir(root, name);
|
|
||||||
|
|
||||||
if(links) {
|
|
||||||
kh_destroy(hl, links);
|
|
||||||
links = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* success */
|
|
||||||
if(!n && !failed) {
|
|
||||||
if(root->sub == NULL) {
|
|
||||||
freedir(root);
|
|
||||||
failed = 1;
|
|
||||||
calc_enterpath(name);
|
|
||||||
strcpy(errmsg, "Directory empty.");
|
|
||||||
goto calc_fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* update references and free original item */
|
|
||||||
if(orig) {
|
|
||||||
root->next = orig->next;
|
|
||||||
root->prev = orig->prev;
|
|
||||||
if(root->parent && root->parent->sub == orig)
|
|
||||||
root->parent->sub = root;
|
|
||||||
if(root->prev)
|
|
||||||
root->prev->next = root;
|
|
||||||
if(root->next)
|
|
||||||
root->next->prev = root;
|
|
||||||
orig->next = orig->prev = NULL;
|
|
||||||
freedir(orig);
|
|
||||||
}
|
|
||||||
browse_init(root->sub);
|
|
||||||
dirlist_top(-3);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* something went wrong... */
|
|
||||||
freedir(root);
|
|
||||||
calc_fail:
|
|
||||||
if(name && path && !path[1] && strcmp(name, "."))
|
|
||||||
free(name);
|
|
||||||
free(path);
|
|
||||||
|
|
||||||
while(failed && !input_handle(0))
|
|
||||||
;
|
|
||||||
if(orig == NULL)
|
|
||||||
return 1;
|
|
||||||
else {
|
|
||||||
browse_init(orig);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void calc_init(char *dir, struct dir *org) {
|
|
||||||
failed = anpos = 0;
|
|
||||||
orig = org;
|
|
||||||
if(curpathl == 0) {
|
|
||||||
curpathl = strlen(dir)+1;
|
|
||||||
curpath = malloc(curpathl);
|
|
||||||
} else if(curpathl <= (int)strlen(dir)) {
|
|
||||||
curpathl = strlen(dir)+1;
|
|
||||||
curpath = realloc(curpath, curpathl);
|
|
||||||
}
|
|
||||||
strcpy(curpath, dir);
|
|
||||||
if(lasterrl == 0) {
|
|
||||||
lasterrl = 1;
|
|
||||||
lasterr = malloc(lasterrl);
|
|
||||||
}
|
|
||||||
lasterr[0] = 0;
|
|
||||||
pstate = ST_CALC;
|
|
||||||
}
|
|
||||||
|
|
39
src/calc.h
39
src/calc.h
@ -1,39 +0,0 @@
|
|||||||
/* ncdu - NCurses Disk Usage
|
|
||||||
|
|
||||||
Copyright (c) 2007-2012 Yoran Heling
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included
|
|
||||||
in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
||||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
||||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
||||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _calc_h
|
|
||||||
#define _calc_h
|
|
||||||
|
|
||||||
#include "global.h"
|
|
||||||
|
|
||||||
extern char calc_smfs; /* stay on the same filesystem */
|
|
||||||
|
|
||||||
int calc_process(void);
|
|
||||||
int calc_key(int);
|
|
||||||
void calc_draw(void);
|
|
||||||
void calc_init(char *, struct dir *);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
116
src/dir.h
Обычный файл
116
src/dir.h
Обычный файл
@ -0,0 +1,116 @@
|
|||||||
|
/* ncdu - NCurses Disk Usage
|
||||||
|
|
||||||
|
Copyright (c) 2007-2012 Yoran Heling
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _dir_h
|
||||||
|
#define _dir_h
|
||||||
|
|
||||||
|
/* The dir_* functions and files implement the SCAN state and are organized as
|
||||||
|
* follows:
|
||||||
|
*
|
||||||
|
* Input:
|
||||||
|
* Responsible for getting a directory structure into ncdu. Will call the
|
||||||
|
* Output functions for data and the UI functions for feedback. Currently
|
||||||
|
* there is only one input implementation: dir_scan.c
|
||||||
|
* Output:
|
||||||
|
* Called by the Input handling code when there's some new file/directory
|
||||||
|
* information. The Output code is responsible for doing something with it
|
||||||
|
* and determines what action should follow after the Input is done.
|
||||||
|
* Currently there is only one output implementation: dir_mem.c.
|
||||||
|
* Common:
|
||||||
|
* Utility functions and UI code for use by the Input handling code to draw
|
||||||
|
* progress/error information on the screen, handle any user input and misc.
|
||||||
|
* stuff.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* "Interface" that Input code should call and Output code should implement. */
|
||||||
|
struct dir_output {
|
||||||
|
/* Called when there is new file/dir info. Call stack for an example
|
||||||
|
* directory structure:
|
||||||
|
* / item('/')
|
||||||
|
* /subdir item('subdir')
|
||||||
|
* /subdir/f item('f')
|
||||||
|
* .. item(NULL)
|
||||||
|
* /abc item('abc')
|
||||||
|
* .. item(NULL)
|
||||||
|
* Every opened dir is followed by a call to NULL. There is only one top-level
|
||||||
|
* dir item. The name of the top-level dir item is the absolute path to the
|
||||||
|
* scanned directory.
|
||||||
|
*
|
||||||
|
* The *item struct has the following fields set when item() is called:
|
||||||
|
* size, asize, ino, dev, flags (only DIR,FILE,ERR,OTHFS,EXL,HLNKC) and name.
|
||||||
|
* All other fields/flags should be initialzed to NULL or 0.
|
||||||
|
* *item may be overwritten or freed in subsequent calls, so this function
|
||||||
|
* should make a copy if necessary.
|
||||||
|
*/
|
||||||
|
void (*item)(struct dir *);
|
||||||
|
|
||||||
|
/* Finalizes the output to go to the next program state or exit ncdu. Called
|
||||||
|
* after item(NULL) has been called for the root item or before any item()
|
||||||
|
* has been called at all.
|
||||||
|
* Argument indicates success (0) or failure (1).
|
||||||
|
* Failure happens when the root directory couldn't be opened, chdir, lstat,
|
||||||
|
* read, when it is empty, or when the user aborted the operation.
|
||||||
|
* Return value should be 0 to continue running ncdu, 1 to exit.
|
||||||
|
*/
|
||||||
|
int (*final)(int);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Initializes the SCAN state and dir_output for immediate browsing.
|
||||||
|
* On success:
|
||||||
|
* If a dir item is given, overwrites it with the new dir struct.
|
||||||
|
* Then calls browse_init(new_dir_struct->sub).
|
||||||
|
* On failure:
|
||||||
|
* If a dir item is given, will just call browse_init(orig).
|
||||||
|
* Otherwise, will exit ncdu.
|
||||||
|
*/
|
||||||
|
void dir_mem_init(struct dir *);
|
||||||
|
|
||||||
|
|
||||||
|
/* Scanning a live directory */
|
||||||
|
extern int dir_scan_smfs;
|
||||||
|
void dir_scan_init(const char *path);
|
||||||
|
int dir_scan_process();
|
||||||
|
|
||||||
|
|
||||||
|
/* The currently configured output functions. */
|
||||||
|
extern struct dir_output dir_output;
|
||||||
|
|
||||||
|
/* Current path that we're working with. These are defined in dir_common.c. */
|
||||||
|
extern char *dir_curpath;
|
||||||
|
void dir_curpath_set(const char *);
|
||||||
|
void dir_curpath_enter(const char *);
|
||||||
|
void dir_curpath_leave();
|
||||||
|
|
||||||
|
/* Sets the path where the last error occured, or reset on NULL. */
|
||||||
|
void dir_setlasterr(const char *);
|
||||||
|
|
||||||
|
/* Return an empty struct dir with the given name, for use with
|
||||||
|
* dir_output.item(). Returned memory may be freed/overwritten on a subsequent
|
||||||
|
* call. */
|
||||||
|
struct dir *dir_createstruct(const char *);
|
||||||
|
|
||||||
|
#endif
|
100
src/dir_common.c
Обычный файл
100
src/dir_common.c
Обычный файл
@ -0,0 +1,100 @@
|
|||||||
|
/* ncdu - NCurses Disk Usage
|
||||||
|
|
||||||
|
Copyright (c) 2007-2012 Yoran Heling
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
char *dir_curpath; /* Full path of the last seen item. */
|
||||||
|
struct dir_output dir_output;
|
||||||
|
static char *lasterr; /* Path where the last error occured. */
|
||||||
|
static int curpathl; /* Allocated length of dir_curpath */
|
||||||
|
static int lasterrl; /* ^ of lasterr */
|
||||||
|
|
||||||
|
|
||||||
|
static void curpath_resize(int s) {
|
||||||
|
if(curpathl < s) {
|
||||||
|
curpathl = s < 128 ? 128 : s < curpathl*2 ? curpathl*2 : s;
|
||||||
|
dir_curpath = realloc(dir_curpath, curpathl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void dir_curpath_set(const char *path) {
|
||||||
|
curpath_resize(strlen(path)+1);
|
||||||
|
strcpy(dir_curpath, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void dir_curpath_enter(const char *name) {
|
||||||
|
curpath_resize(strlen(dir_curpath)+strlen(name)+2);
|
||||||
|
if(dir_curpath[1])
|
||||||
|
strcat(dir_curpath, "/");
|
||||||
|
strcat(dir_curpath, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* removes last component from dir_curpath */
|
||||||
|
void dir_curpath_leave() {
|
||||||
|
char *tmp;
|
||||||
|
if((tmp = strrchr(dir_curpath, '/')) == NULL)
|
||||||
|
strcpy(dir_curpath, "/");
|
||||||
|
else if(tmp != dir_curpath)
|
||||||
|
tmp[0] = 0;
|
||||||
|
else
|
||||||
|
tmp[1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void dir_setlasterr(const char *path) {
|
||||||
|
if(!path) {
|
||||||
|
free(lasterr);
|
||||||
|
lasterr = NULL;
|
||||||
|
lasterrl = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int req = strlen(path)+1;
|
||||||
|
if(lasterrl < req) {
|
||||||
|
lasterrl = req;
|
||||||
|
lasterr = realloc(lasterr, lasterrl);
|
||||||
|
}
|
||||||
|
strcpy(lasterr, dir_curpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct dir *dir_createstruct(const char *name) {
|
||||||
|
static struct dir *d = NULL;
|
||||||
|
static size_t len = 0;
|
||||||
|
size_t req = SDIRSIZE+strlen(name);
|
||||||
|
if(len < req) {
|
||||||
|
len = req < SDIRSIZE+256 ? SDIRSIZE+256 : req < len*2 ? len*2 : req;
|
||||||
|
d = realloc(d, len);
|
||||||
|
}
|
||||||
|
memset(d, 0, SDIRSIZE);
|
||||||
|
strcpy(d->name, name);
|
||||||
|
return d;
|
||||||
|
}
|
209
src/dir_mem.c
Обычный файл
209
src/dir_mem.c
Обычный файл
@ -0,0 +1,209 @@
|
|||||||
|
/* ncdu - NCurses Disk Usage
|
||||||
|
|
||||||
|
Copyright (c) 2007-2012 Yoran Heling
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
#include "khash.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
static struct dir *root; /* root directory struct we're scanning */
|
||||||
|
static struct dir *curdir; /* directory item that we're currently adding items to */
|
||||||
|
static struct dir *orig; /* original directory, when refreshing an already scanned dir */
|
||||||
|
|
||||||
|
/* Table of struct dir items with more than one link (in order to detect hard links) */
|
||||||
|
#define hlink_hash(d) (kh_int64_hash_func((khint64_t)d->dev) ^ kh_int64_hash_func((khint64_t)d->ino))
|
||||||
|
#define hlink_equal(a, b) ((a)->dev == (b)->dev && (a)->ino == (b)->ino)
|
||||||
|
KHASH_INIT(hl, struct dir *, char, 0, hlink_hash, hlink_equal);
|
||||||
|
static khash_t(hl) *links = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
/* recursively checks a dir structure for hard links and fills the lookup array */
|
||||||
|
static void hlink_init(struct dir *d) {
|
||||||
|
struct dir *t;
|
||||||
|
|
||||||
|
for(t=d->sub; t!=NULL; t=t->next)
|
||||||
|
hlink_init(t);
|
||||||
|
|
||||||
|
if(!(d->flags & FF_HLNKC))
|
||||||
|
return;
|
||||||
|
int r;
|
||||||
|
kh_put(hl, links, d, &r);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* checks an individual file for hard links and updates its cicrular linked
|
||||||
|
* list, also updates the sizes of the parent dirs */
|
||||||
|
static void hlink_check(struct dir *d) {
|
||||||
|
struct dir *t, *pt, *par;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* add to links table */
|
||||||
|
khiter_t k = kh_put(hl, links, d, &i);
|
||||||
|
|
||||||
|
/* found in the table? update hlnk */
|
||||||
|
if(!i) {
|
||||||
|
t = d->hlnk = kh_key(links, k);
|
||||||
|
if(t->hlnk != NULL)
|
||||||
|
for(t=t->hlnk; t->hlnk!=d->hlnk; t=t->hlnk)
|
||||||
|
;
|
||||||
|
t->hlnk = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now update the sizes of the parent directories,
|
||||||
|
* This works by only counting this file in the parent directories where this
|
||||||
|
* file hasn't been counted yet, which can be determined from the hlnk list.
|
||||||
|
* XXX: This may not be the most efficient algorithm to do this */
|
||||||
|
for(i=1,par=d->parent; i&∥ par=par->parent) {
|
||||||
|
if(d->hlnk)
|
||||||
|
for(t=d->hlnk; i&&t!=d; t=t->hlnk)
|
||||||
|
for(pt=t->parent; i&&pt; pt=pt->parent)
|
||||||
|
if(pt==par)
|
||||||
|
i=0;
|
||||||
|
if(i) {
|
||||||
|
par->size += d->size;
|
||||||
|
par->asize += d->asize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Make a copy of *item so that we'll keep it in memory. In the special case
|
||||||
|
* of !root && orig, we need to copy over the name of *orig instead of *item.
|
||||||
|
*/
|
||||||
|
static struct dir *item_copy(struct dir *item) {
|
||||||
|
struct dir *t;
|
||||||
|
|
||||||
|
if(!root && orig) {
|
||||||
|
t = malloc(SDIRSIZE+strlen(orig->name));
|
||||||
|
memcpy(t, item, SDIRSIZE);
|
||||||
|
strcpy(t->name, orig->name);
|
||||||
|
} else {
|
||||||
|
t = malloc(SDIRSIZE+strlen(item->name));
|
||||||
|
memcpy(t, item, SDIRSIZE+strlen(item->name));
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Add item to the correct place in the memory structure */
|
||||||
|
static void item_add(struct dir *item) {
|
||||||
|
if(!root) {
|
||||||
|
root = item;
|
||||||
|
/* Make sure that the *root appears to be part of the same dir structure as
|
||||||
|
* *orig, otherwise the directory size calculation will be incorrect in the
|
||||||
|
* case of hard links. */
|
||||||
|
if(orig)
|
||||||
|
root->parent = orig->parent;
|
||||||
|
} else {
|
||||||
|
item->parent = curdir;
|
||||||
|
item->next = curdir->sub;
|
||||||
|
if(item->next)
|
||||||
|
item->next->prev = item;
|
||||||
|
curdir->sub = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void item(struct dir *item) {
|
||||||
|
struct dir *t;
|
||||||
|
|
||||||
|
/* Go back to parent dir */
|
||||||
|
if(!item) {
|
||||||
|
curdir = curdir->parent;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
item = item_copy(item);
|
||||||
|
item_add(item);
|
||||||
|
|
||||||
|
/* Ensure that any next items will go to this directory */
|
||||||
|
if(item->flags & FF_DIR)
|
||||||
|
curdir = item;
|
||||||
|
|
||||||
|
/* Update stats of parents. Don't update the size/asize fields if this is a
|
||||||
|
* possible hard link, because hlnk_check() will take care of it in that
|
||||||
|
* case. */
|
||||||
|
if(item->flags & FF_HLNKC) {
|
||||||
|
addparentstats(item->parent, 0, 0, 1);
|
||||||
|
hlink_check(item);
|
||||||
|
} else
|
||||||
|
addparentstats(item->parent, item->size, item->asize, 1);
|
||||||
|
|
||||||
|
/* propagate ERR and SERR back up to the root */
|
||||||
|
if(item->flags & FF_SERR || item->flags & FF_ERR)
|
||||||
|
for(t=item->parent; t; t=t->parent)
|
||||||
|
t->flags |= FF_SERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int final(int fail) {
|
||||||
|
kh_destroy(hl, links);
|
||||||
|
links = NULL;
|
||||||
|
|
||||||
|
if(fail) {
|
||||||
|
freedir(root);
|
||||||
|
if(orig) {
|
||||||
|
browse_init(orig);
|
||||||
|
return 0;
|
||||||
|
} else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success, update references and free original item */
|
||||||
|
if(orig) {
|
||||||
|
root->next = orig->next;
|
||||||
|
root->prev = orig->prev;
|
||||||
|
if(root->parent && root->parent->sub == orig)
|
||||||
|
root->parent->sub = root;
|
||||||
|
if(root->prev)
|
||||||
|
root->prev->next = root;
|
||||||
|
if(root->next)
|
||||||
|
root->next->prev = root;
|
||||||
|
orig->next = orig->prev = NULL;
|
||||||
|
freedir(orig);
|
||||||
|
}
|
||||||
|
|
||||||
|
browse_init(root->sub);
|
||||||
|
dirlist_top(-3);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void dir_mem_init(struct dir *_orig) {
|
||||||
|
orig = _orig;
|
||||||
|
root = curdir = NULL;
|
||||||
|
pstate = ST_CALC;
|
||||||
|
|
||||||
|
dir_output.item = item;
|
||||||
|
dir_output.final = final;
|
||||||
|
|
||||||
|
/* Init hash table for hard link detection */
|
||||||
|
links = kh_init(hl);
|
||||||
|
if(orig)
|
||||||
|
hlink_init(getroot(orig));
|
||||||
|
}
|
||||||
|
|
263
src/dir_scan.c
Обычный файл
263
src/dir_scan.c
Обычный файл
@ -0,0 +1,263 @@
|
|||||||
|
/* ncdu - NCurses Disk Usage
|
||||||
|
|
||||||
|
Copyright (c) 2007-2012 Yoran Heling
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* set S_BLKSIZE if not defined already in sys/stat.h */
|
||||||
|
#ifndef S_BLKSIZE
|
||||||
|
# define S_BLKSIZE 512
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
int dir_scan_smfs; /* Stay on the same filesystem */
|
||||||
|
|
||||||
|
static dev_t curdev; /* current device we're scanning on */
|
||||||
|
|
||||||
|
|
||||||
|
/* Populates the struct dir item with information from the stat struct. Sets
|
||||||
|
* everything necessary for output_dir.item() except FF_ERR and FF_EXL. */
|
||||||
|
static void stat_to_dir(struct dir *d, struct stat *fs) {
|
||||||
|
d->ino = fs->st_ino;
|
||||||
|
d->dev = fs->st_dev;
|
||||||
|
|
||||||
|
if(S_ISREG(fs->st_mode))
|
||||||
|
d->flags |= FF_FILE;
|
||||||
|
else if(S_ISDIR(fs->st_mode))
|
||||||
|
d->flags |= FF_DIR;
|
||||||
|
|
||||||
|
if(!S_ISDIR(fs->st_mode) && fs->st_nlink > 1)
|
||||||
|
d->flags |= FF_HLNKC;
|
||||||
|
|
||||||
|
if(dir_scan_smfs && curdev != fs->st_dev)
|
||||||
|
d->flags |= FF_OTHFS;
|
||||||
|
|
||||||
|
if(!(d->flags & (FF_OTHFS|FF_EXL))) {
|
||||||
|
d->size = fs->st_blocks * S_BLKSIZE;
|
||||||
|
d->asize = fs->st_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Reads all filenames in the currently chdir'ed directory and stores it as a
|
||||||
|
* nul-separated list of filenames. The list ends with an empty filename (i.e.
|
||||||
|
* two nuls). . and .. are not included. Returned memory should be freed. *err
|
||||||
|
* is set to 1 if some error occured. Returns NULL if that error was fatal.
|
||||||
|
* The reason for reading everything in memory first and then walking through
|
||||||
|
* the list is to avoid eating too many file descriptors in a deeply recursive
|
||||||
|
* directory. */
|
||||||
|
static char *dir_read(int *err) {
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *item;
|
||||||
|
char *buf = NULL;
|
||||||
|
int buflen = 512;
|
||||||
|
int off = 0;
|
||||||
|
|
||||||
|
if((dir = opendir(".")) == NULL) {
|
||||||
|
*err = 1;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = malloc(buflen);
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
while((item = readdir(dir)) != NULL) {
|
||||||
|
if(item->d_name[0] == '.' && (item->d_name[1] == 0 || (item->d_name[1] == '.' && item->d_name[2] == 0)))
|
||||||
|
continue;
|
||||||
|
int req = off+3+strlen(item->d_name);
|
||||||
|
if(req > buflen) {
|
||||||
|
buflen = req < buflen*2 ? buflen*2 : req;
|
||||||
|
buf = realloc(buf, buflen);
|
||||||
|
}
|
||||||
|
strcpy(buf+off, item->d_name);
|
||||||
|
off += strlen(item->d_name)+1;
|
||||||
|
}
|
||||||
|
if(errno)
|
||||||
|
*err = 1;
|
||||||
|
if(closedir(dir) < 0)
|
||||||
|
*err = 1;
|
||||||
|
|
||||||
|
buf[off] = 0;
|
||||||
|
buf[off+1] = 0;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int dir_walk(char *);
|
||||||
|
|
||||||
|
|
||||||
|
/* Tries to recurse into the given directory item */
|
||||||
|
static int dir_scan_recurse(struct dir *d) {
|
||||||
|
int fail = 0;
|
||||||
|
char *dir;
|
||||||
|
|
||||||
|
if(chdir(d->name)) {
|
||||||
|
dir_setlasterr(dir_curpath);
|
||||||
|
d->flags |= FF_ERR;
|
||||||
|
dir_output.item(d);
|
||||||
|
dir_output.item(NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((dir = dir_read(&fail)) == NULL) {
|
||||||
|
dir_setlasterr(dir_curpath);
|
||||||
|
d->flags |= FF_ERR;
|
||||||
|
dir_output.item(d);
|
||||||
|
dir_output.item(NULL);
|
||||||
|
return chdir("..") ? 1 : 0; /* TODO: Error reporting */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* readdir() failed halfway, not fatal. */
|
||||||
|
if(fail)
|
||||||
|
d->flags |= FF_ERR;
|
||||||
|
|
||||||
|
dir_output.item(d);
|
||||||
|
fail = dir_walk(dir);
|
||||||
|
dir_output.item(NULL);
|
||||||
|
|
||||||
|
/* Not being able to chdir back is fatal */
|
||||||
|
if(!fail && chdir(".."))
|
||||||
|
return 1; /* TODO: Error reporting */
|
||||||
|
|
||||||
|
return fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Scans and adds a single item. Recurses into dir_walk() again if this is a
|
||||||
|
* directory. Assumes we're chdir'ed in the directory in which this item
|
||||||
|
* resides, i.e. d->name is a valid relative path to the item. */
|
||||||
|
static int dir_scan_item(struct dir *d) {
|
||||||
|
struct stat st;
|
||||||
|
int fail = 0;
|
||||||
|
|
||||||
|
#ifdef __CYGWIN__
|
||||||
|
/* /proc/registry names may contain slashes */
|
||||||
|
if(strchr(d->name, '/') || strchr(d->name, '\\')) {
|
||||||
|
d->flags |= FF_ERR;
|
||||||
|
dir_setlasterr(dir_curpath);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(exclude_match(dir_curpath))
|
||||||
|
d->flags |= FF_EXL;
|
||||||
|
|
||||||
|
if(!(d->flags & (FF_ERR|FF_EXL)) && lstat(d->name, &st)) {
|
||||||
|
d->flags |= FF_ERR;
|
||||||
|
dir_setlasterr(dir_curpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(d->flags & (FF_ERR|FF_EXL)))
|
||||||
|
stat_to_dir(d, &st);
|
||||||
|
|
||||||
|
/* Recurse into the dir or output the item */
|
||||||
|
if(d->flags & FF_DIR && !(d->flags & (FF_ERR|FF_EXL|FF_OTHFS)))
|
||||||
|
dir_scan_recurse(d);
|
||||||
|
else if(d->flags & FF_DIR) {
|
||||||
|
dir_output.item(d);
|
||||||
|
dir_output.item(NULL);
|
||||||
|
} else
|
||||||
|
dir_output.item(d);
|
||||||
|
|
||||||
|
return fail; /* TODO: UI */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Walks through the directory that we're currently chdir'ed to. *dir contains
|
||||||
|
* the filenames as returned by dir_read(), and will be freed automatically by
|
||||||
|
* this function. */
|
||||||
|
static int dir_walk(char *dir) {
|
||||||
|
struct dir *d;
|
||||||
|
int fail = 0;
|
||||||
|
char *cur;
|
||||||
|
|
||||||
|
fail = 0;
|
||||||
|
for(cur=dir; !fail&&cur&&*cur; cur+=strlen(cur)+1) {
|
||||||
|
dir_curpath_enter(cur);
|
||||||
|
d = dir_createstruct(cur);
|
||||||
|
fail = dir_scan_item(d);
|
||||||
|
dir_curpath_leave();
|
||||||
|
}
|
||||||
|
|
||||||
|
free(dir);
|
||||||
|
return fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Returns 0 to continue running ncdu, 1 to quit. */
|
||||||
|
int dir_scan_process() {
|
||||||
|
char *path;
|
||||||
|
char *dir;
|
||||||
|
int fail = 0;
|
||||||
|
struct stat fs;
|
||||||
|
struct dir *d;
|
||||||
|
|
||||||
|
if((path = path_real(dir_curpath)) == NULL) {
|
||||||
|
/* TODO */
|
||||||
|
}
|
||||||
|
dir_curpath_set(path);
|
||||||
|
free(path);
|
||||||
|
|
||||||
|
if(path_chdir(dir_curpath) < 0) {
|
||||||
|
/* TODO */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Can these even fail after a chdir? */
|
||||||
|
if(lstat(".", &fs) != 0 || !S_ISDIR(fs.st_mode)) {
|
||||||
|
/* TODO */
|
||||||
|
}
|
||||||
|
|
||||||
|
dir = dir_read(&fail);
|
||||||
|
if(!dir) {
|
||||||
|
/* TODO */
|
||||||
|
}
|
||||||
|
|
||||||
|
curdev = fs.st_dev;
|
||||||
|
d = dir_createstruct(dir_curpath);
|
||||||
|
if(fail)
|
||||||
|
d->flags |= FF_ERR;
|
||||||
|
stat_to_dir(d, &fs);
|
||||||
|
|
||||||
|
dir_output.item(d);
|
||||||
|
fail = dir_walk(dir);
|
||||||
|
dir_output.item(NULL);
|
||||||
|
|
||||||
|
return dir_output.final(fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void dir_scan_init(const char *path) {
|
||||||
|
dir_curpath_set(path);
|
||||||
|
dir_setlasterr(NULL);
|
||||||
|
pstate = ST_CALC;
|
||||||
|
}
|
12
src/global.h
12
src/global.h
@ -55,7 +55,7 @@ struct dir {
|
|||||||
struct dir *parent, *next, *prev, *sub, *hlnk;
|
struct dir *parent, *next, *prev, *sub, *hlnk;
|
||||||
off_t size, asize;
|
off_t size, asize;
|
||||||
ino_t ino;
|
ino_t ino;
|
||||||
unsigned long items;
|
long items;
|
||||||
dev_t dev;
|
dev_t dev;
|
||||||
unsigned char flags;
|
unsigned char flags;
|
||||||
char name[3]; /* must be large enough to hold ".." */
|
char name[3]; /* must be large enough to hold ".." */
|
||||||
@ -81,13 +81,13 @@ int input_handle(int);
|
|||||||
|
|
||||||
|
|
||||||
/* import all other global functions and variables */
|
/* import all other global functions and variables */
|
||||||
#include "exclude.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "calc.h"
|
|
||||||
#include "delete.h"
|
|
||||||
#include "browser.h"
|
#include "browser.h"
|
||||||
|
#include "delete.h"
|
||||||
|
#include "dir.h"
|
||||||
|
#include "dirlist.h"
|
||||||
|
#include "exclude.h"
|
||||||
#include "help.h"
|
#include "help.h"
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
#include "dirlist.h"
|
#include "util.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
11
src/main.c
11
src/main.c
@ -44,7 +44,7 @@ static long lastupdate = 999;
|
|||||||
|
|
||||||
static void screen_draw() {
|
static void screen_draw() {
|
||||||
switch(pstate) {
|
switch(pstate) {
|
||||||
case ST_CALC: calc_draw(); break;
|
case ST_CALC: /* TODO */ break;
|
||||||
case ST_BROWSE: browse_draw(); break;
|
case ST_BROWSE: browse_draw(); break;
|
||||||
case ST_HELP: help_draw(); break;
|
case ST_HELP: help_draw(); break;
|
||||||
case ST_DEL: delete_draw(); break;
|
case ST_DEL: delete_draw(); break;
|
||||||
@ -82,7 +82,7 @@ int input_handle(int wait) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
switch(pstate) {
|
switch(pstate) {
|
||||||
case ST_CALC: return calc_key(ch);
|
case ST_CALC: return 0; /* TODO */
|
||||||
case ST_BROWSE: return browse_key(ch);
|
case ST_BROWSE: return browse_key(ch);
|
||||||
case ST_HELP: return help_key(ch);
|
case ST_HELP: return help_key(ch);
|
||||||
case ST_DEL: return delete_key(ch);
|
case ST_DEL: return delete_key(ch);
|
||||||
@ -120,7 +120,7 @@ static char *argv_parse(int argc, char **argv) {
|
|||||||
len = strlen(argv[i]);
|
len = strlen(argv[i]);
|
||||||
for(j=1; j<len; j++)
|
for(j=1; j<len; j++)
|
||||||
switch(argv[i][j]) {
|
switch(argv[i][j]) {
|
||||||
case 'x': calc_smfs = 1; break;
|
case 'x': dir_scan_smfs = 1; break;
|
||||||
case 'r': read_only = 1; break;
|
case 'r': read_only = 1; break;
|
||||||
case 'q': update_delay = 2000; break;
|
case 'q': update_delay = 2000; break;
|
||||||
case '?':
|
case '?':
|
||||||
@ -157,7 +157,8 @@ int main(int argc, char **argv) {
|
|||||||
if((dir = argv_parse(argc, argv)) == NULL)
|
if((dir = argv_parse(argc, argv)) == NULL)
|
||||||
dir = ".";
|
dir = ".";
|
||||||
|
|
||||||
calc_init(dir, NULL);
|
dir_mem_init(NULL);
|
||||||
|
dir_scan_init(dir);
|
||||||
|
|
||||||
initscr();
|
initscr();
|
||||||
cbreak();
|
cbreak();
|
||||||
@ -168,7 +169,7 @@ int main(int argc, char **argv) {
|
|||||||
min_rows = min_cols = 0;
|
min_rows = min_cols = 0;
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
if(pstate == ST_CALC && calc_process())
|
if(pstate == ST_CALC && dir_scan_process())
|
||||||
break;
|
break;
|
||||||
else if(pstate == ST_DEL)
|
else if(pstate == ST_DEL)
|
||||||
delete_process();
|
delete_process();
|
||||||
|
29
src/util.c
29
src/util.c
@ -175,7 +175,7 @@ static void freedir_hlnk(struct dir *d) {
|
|||||||
* This works the same as with adding: only the parents in which THIS is the
|
* This works the same as with adding: only the parents in which THIS is the
|
||||||
* only occurence of the hard link will be modified, if the same file still
|
* only occurence of the hard link will be modified, if the same file still
|
||||||
* exists within the parent it shouldn't get removed from the count.
|
* exists within the parent it shouldn't get removed from the count.
|
||||||
* XXX: Same note as for calc.c / calc_hlnk_check():
|
* XXX: Same note as for dir_mem.c / hlink_check():
|
||||||
* this is probably not the most efficient algorithm */
|
* this is probably not the most efficient algorithm */
|
||||||
for(i=1,par=d->parent; i&∥ par=par->parent) {
|
for(i=1,par=d->parent; i&∥ par=par->parent) {
|
||||||
if(d->hlnk)
|
if(d->hlnk)
|
||||||
@ -212,7 +212,8 @@ static void freedir_rec(struct dir *dr) {
|
|||||||
|
|
||||||
|
|
||||||
void freedir(struct dir *dr) {
|
void freedir(struct dir *dr) {
|
||||||
struct dir *tmp;
|
if(!dr)
|
||||||
|
return;
|
||||||
|
|
||||||
/* free dr->sub recursively */
|
/* free dr->sub recursively */
|
||||||
if(dr->sub)
|
if(dr->sub)
|
||||||
@ -230,13 +231,7 @@ void freedir(struct dir *dr) {
|
|||||||
|
|
||||||
/* update sizes of parent directories if this isn't a hard link.
|
/* update sizes of parent directories if this isn't a hard link.
|
||||||
* If this is a hard link, freedir_hlnk() would have done so already */
|
* If this is a hard link, freedir_hlnk() would have done so already */
|
||||||
for(tmp=dr->parent; tmp; tmp=tmp->parent) {
|
addparentstats(dr->parent, dr->flags & FF_HLNKC ? 0 : -dr->size, dr->flags & FF_HLNKC ? 0 : -dr->asize, -(dr->items+1));
|
||||||
if(!(dr->flags & FF_HLNKC)) {
|
|
||||||
tmp->size -= dr->size;
|
|
||||||
tmp->asize -= dr->asize;
|
|
||||||
}
|
|
||||||
tmp->items -= dr->items+1;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(dr);
|
free(dr);
|
||||||
}
|
}
|
||||||
@ -280,3 +275,19 @@ char *getpath(struct dir *cur) {
|
|||||||
return dat;
|
return dat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct dir *getroot(struct dir *d) {
|
||||||
|
while(d && d->parent)
|
||||||
|
d = d->parent;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void addparentstats(struct dir *d, off_t size, off_t asize, long items) {
|
||||||
|
while(d) {
|
||||||
|
d->size += size;
|
||||||
|
d->asize += asize;
|
||||||
|
d->items += items;
|
||||||
|
d = d->parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -78,5 +78,11 @@ void freedir(struct dir *);
|
|||||||
returned pointer will be overwritten with a subsequent call */
|
returned pointer will be overwritten with a subsequent call */
|
||||||
char *getpath(struct dir *);
|
char *getpath(struct dir *);
|
||||||
|
|
||||||
|
/* returns the root element of the given dir struct */
|
||||||
|
struct dir *getroot(struct dir *);
|
||||||
|
|
||||||
|
/* Adds a value to the size, asize and items fields of *d and its parents */
|
||||||
|
void addparentstats(struct dir *, off_t, off_t, long);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Загрузка…
Ссылка в новой задаче
Block a user