Split path handling into path.c and replaced rpath() with a better implementation
This is the first step into replacing all code that relies on PATH_MAX, more changes will follow.
Этот коммит содержится в:
родитель
1739ee74d6
Коммит
7698bfd980
@ -1,5 +1,5 @@
|
||||
bin_PROGRAMS = ncdu
|
||||
|
||||
ncdu_SOURCES = browser.c calc.c delete.c exclude.c help.c main.c util.c
|
||||
ncdu_SOURCES = browser.c calc.c delete.c exclude.c help.c main.c path.c util.c
|
||||
|
||||
noinst_HEADERS = browser.h calc.h delete.h exclude.h help.h ncdu.h util.h
|
||||
noinst_HEADERS = browser.h calc.h delete.h exclude.h help.h ncdu.h path.h util.h
|
||||
|
124
src/calc.c
124
src/calc.c
@ -28,6 +28,7 @@
|
||||
#include "exclude.h"
|
||||
#include "util.h"
|
||||
#include "browser.h"
|
||||
#include "path.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
@ -45,21 +46,6 @@
|
||||
# define S_BLKSIZE 512
|
||||
#endif
|
||||
|
||||
#ifndef LINK_MAX
|
||||
# ifdef _POSIX_LINK_MAX
|
||||
# define LINK_MAX _POSIX_LINK_MAX
|
||||
# else
|
||||
# define LINK_MAX 32
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef S_ISLNK
|
||||
# ifndef S_IFLNK
|
||||
# define S_IFLNK 0120000
|
||||
# endif
|
||||
# define S_ISLNK(x) (x & S_IFLNK)
|
||||
#endif
|
||||
|
||||
|
||||
/* external vars */
|
||||
int calc_delay = 100;
|
||||
@ -77,107 +63,6 @@ long lastupdate; /* time of the last screen update */
|
||||
int anpos; /* position of the animation string */
|
||||
|
||||
|
||||
/* My own implementation of realpath()
|
||||
- assumes that *every* possible path fits in PATH_MAX bytes
|
||||
- does not set errno on error
|
||||
- has not yet been fully tested
|
||||
*/
|
||||
char *rpath(const char *from, char *to) {
|
||||
char tmp[PATH_MAX], cwd[PATH_MAX], cur[PATH_MAX], app[PATH_MAX];
|
||||
int i, j, l, k, last, ll = 0;
|
||||
struct stat st;
|
||||
|
||||
getcwd(cwd, PATH_MAX);
|
||||
strcpy(cur, from);
|
||||
app[0] = 0;
|
||||
|
||||
loop:
|
||||
/* not an absolute path, add current directory */
|
||||
if(cur[0] != '/') {
|
||||
if(!(cwd[0] == '/' && cwd[1] == 0))
|
||||
strcpy(tmp, cwd);
|
||||
else
|
||||
tmp[0] = 0;
|
||||
if(strlen(cur) + 2 > PATH_MAX - strlen(tmp))
|
||||
return(NULL);
|
||||
strcat(tmp, "/");
|
||||
strcat(tmp, cur);
|
||||
} else
|
||||
strcpy(tmp, cur);
|
||||
|
||||
/* now fix things like '.' and '..' */
|
||||
i = j = last = 0;
|
||||
l = strlen(tmp);
|
||||
while(1) {
|
||||
if(tmp[i] == 0)
|
||||
break;
|
||||
/* . */
|
||||
if(l >= i+2 && tmp[i] == '/' && tmp[i+1] == '.' && (tmp[i+2] == 0 || tmp[i+2] == '/')) {
|
||||
i+= 2;
|
||||
continue;
|
||||
}
|
||||
/* .. */
|
||||
if(l >= i+3 && tmp[i] == '/' && tmp[i+1] == '.' && tmp[i+2] == '.' && (tmp[i+3] == 0 || tmp[i+3] == '/')) {
|
||||
for(k=j; --k>0;)
|
||||
if(to[k] == '/' && k != j-1)
|
||||
break;
|
||||
j -= j-k;
|
||||
if(j < 1) j = 1;
|
||||
i += 3;
|
||||
continue;
|
||||
}
|
||||
/* remove double slashes */
|
||||
if(tmp[i] == '/' && i>0 && tmp[i-1] == '/') {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
to[j++] = tmp[i++];
|
||||
}
|
||||
/* remove leading slashes */
|
||||
while(--j > 0) {
|
||||
if(to[j] != '/')
|
||||
break;
|
||||
}
|
||||
to[j+1] = 0;
|
||||
/* make sure we do have something left in case our path is / */
|
||||
if(to[0] == 0) {
|
||||
to[0] = '/';
|
||||
to[1] = 0;
|
||||
}
|
||||
/* append 'app' */
|
||||
if(app[0] != 0)
|
||||
strcat(to, app);
|
||||
|
||||
j = strlen(to);
|
||||
/* check for symlinks */
|
||||
for(i=1; i<=j; i++) {
|
||||
if(to[i] == '/' || to[i] == 0) {
|
||||
strncpy(tmp, to, i);
|
||||
tmp[i] = 0;
|
||||
if(lstat(tmp, &st) < 0)
|
||||
return(NULL);
|
||||
if(S_ISLNK(st.st_mode)) {
|
||||
if(++ll > LINK_MAX || (k = readlink(tmp, cur, PATH_MAX)) < 0)
|
||||
return(NULL);
|
||||
cur[k] = 0;
|
||||
if(to[i] != 0)
|
||||
strcpy(app, &to[i]);
|
||||
strcpy(cwd, tmp);
|
||||
for(k=strlen(cwd); --k>0;)
|
||||
if(cwd[k] == '/')
|
||||
break;
|
||||
cwd[k] = 0;
|
||||
goto loop;
|
||||
}
|
||||
if(!S_ISDIR(st.st_mode))
|
||||
return(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return(to);
|
||||
}
|
||||
|
||||
|
||||
int calc_item(struct dir *par, char *path, char *name) {
|
||||
char tmp[PATH_MAX];
|
||||
struct dir *t, *d;
|
||||
@ -400,12 +285,12 @@ int calc_key(int ch) {
|
||||
|
||||
|
||||
void calc_process() {
|
||||
char tmp[PATH_MAX];
|
||||
char *tmp;
|
||||
struct stat fs;
|
||||
struct dir *t;
|
||||
|
||||
/* check root directory */
|
||||
if(rpath(curpath, tmp) == NULL || lstat(tmp, &fs) != 0 || !S_ISDIR(fs.st_mode)) {
|
||||
if((tmp = path_real(curpath)) == NULL || lstat(tmp, &fs) != 0 || !S_ISDIR(fs.st_mode)) {
|
||||
failed = 1;
|
||||
strcpy(errmsg, "Directory not found");
|
||||
goto fail;
|
||||
@ -416,8 +301,7 @@ void calc_process() {
|
||||
t->size = fs.st_blocks * S_BLKSIZE;
|
||||
t->asize = fs.st_size;
|
||||
t->flags |= FF_DIR;
|
||||
t->name = (char *) malloc(strlen(tmp)+1);
|
||||
strcpy(t->name, orig ? orig->name : tmp);
|
||||
t->name = tmp;
|
||||
root = t;
|
||||
curdev = fs.st_dev;
|
||||
|
||||
|
243
src/path.c
Обычный файл
243
src/path.c
Обычный файл
@ -0,0 +1,243 @@
|
||||
/* ncdu - NCurses Disk Usage
|
||||
|
||||
Copyright (c) 2007-2009 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 "path.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#ifndef LINK_MAX
|
||||
# ifdef _POSIX_LINK_MAX
|
||||
# define LINK_MAX _POSIX_LINK_MAX
|
||||
# else
|
||||
# define LINK_MAX 32
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
#define RPATH_CNKSZ 256
|
||||
|
||||
|
||||
/* splits a path into components and does a bit of cannonicalization.
|
||||
a pointer to a reversed array of components is stored in res and the
|
||||
number of components is returned.
|
||||
cur is modified, and res has to be free()d after use */
|
||||
int path_split(char *cur, char ***res) {
|
||||
char **old;
|
||||
int i, j, n;
|
||||
|
||||
cur += strspn(cur, "/");
|
||||
n = strlen(cur);
|
||||
|
||||
/* replace slashes with zeros */
|
||||
for(i=j=0; i<n; i++)
|
||||
if(cur[i] == '/') {
|
||||
cur[i] = 0;
|
||||
if(cur[i-1] != 0)
|
||||
j++;
|
||||
}
|
||||
|
||||
/* create array of the components */
|
||||
old = malloc((j+1)*sizeof(char *));
|
||||
*res = malloc((j+1)*sizeof(char *));
|
||||
for(i=j=0; i<n; i++)
|
||||
if(i == 0 || (cur[i-1] == 0 && cur[i] != 0))
|
||||
old[j++] = cur+i;
|
||||
|
||||
/* re-order and remove parts */
|
||||
for(i=n=0; --j>=0; ) {
|
||||
if(!strcmp(old[j], "..")) {
|
||||
n++;
|
||||
continue;
|
||||
}
|
||||
if(!strcmp(old[j], "."))
|
||||
continue;
|
||||
if(n) {
|
||||
n--;
|
||||
continue;
|
||||
}
|
||||
(*res)[i++] = old[j];
|
||||
}
|
||||
free(old);
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/* copies path and prepends cwd if needed, to ensure an absolute path
|
||||
return value has to be free()'d manually */
|
||||
char *path_absolute(const char *path) {
|
||||
int i, n;
|
||||
char *ret;
|
||||
|
||||
/* not an absolute path? prepend cwd */
|
||||
if(path[0] != '/') {
|
||||
n = RPATH_CNKSZ;
|
||||
ret = malloc(n);
|
||||
errno = 0;
|
||||
while(!getcwd(ret, n) && errno == ERANGE) {
|
||||
n += RPATH_CNKSZ;
|
||||
ret = realloc(ret, n);
|
||||
errno = 0;
|
||||
}
|
||||
if(errno) {
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i = strlen(path) + strlen(ret) + 2;
|
||||
if(i > n)
|
||||
ret = realloc(ret, i);
|
||||
strcat(ret, "/");
|
||||
strcat(ret, path);
|
||||
/* otherwise, just make a copy */
|
||||
} else {
|
||||
ret = malloc(strlen(path)+1);
|
||||
strcpy(ret, path);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* NOTE: cwd and the memory cur points to are unreliable after calling this function */
|
||||
char *path_real_rec(char *cur, int *links) {
|
||||
int i, j, n, tmpl, lnkl = 0;
|
||||
char **arr, *tmp, *lnk, *ret = NULL;
|
||||
|
||||
tmpl = strlen(cur);
|
||||
tmp = malloc(tmpl);
|
||||
|
||||
/* split path */
|
||||
i = path_split(cur, &arr);
|
||||
|
||||
/* re-create full path */
|
||||
strcpy(tmp, "/");
|
||||
if(i > 0) {
|
||||
j = 1;
|
||||
lnkl = RPATH_CNKSZ;
|
||||
lnk = malloc(lnkl);
|
||||
if(chdir("/") < 0)
|
||||
goto path_real_done;
|
||||
}
|
||||
|
||||
while(--i>=0) {
|
||||
if(arr[i][0] == 0)
|
||||
continue;
|
||||
/* check for symlink */
|
||||
while((n = readlink(arr[i], lnk, lnkl)) == lnkl || (n < 0 && errno == ERANGE)) {
|
||||
lnkl += RPATH_CNKSZ;
|
||||
lnk = realloc(lnk, lnkl);
|
||||
}
|
||||
if(n < 0 && errno != EINVAL)
|
||||
goto path_real_done;
|
||||
if(n > 0) {
|
||||
if(++*links > LINK_MAX) {
|
||||
errno = ELOOP;
|
||||
goto path_real_done;
|
||||
}
|
||||
lnk[n] = 0;
|
||||
/* create new path and call path_real_rec() again */
|
||||
if(lnk[0] != '/') {
|
||||
n += strlen(tmp) + 1;
|
||||
if(tmpl < n) {
|
||||
tmpl = n;
|
||||
tmp = realloc(tmp, tmpl);
|
||||
}
|
||||
strcat(tmp, lnk);
|
||||
} else
|
||||
strcpy(tmp, lnk);
|
||||
ret = path_real_rec(tmp, links);
|
||||
goto path_real_done;
|
||||
}
|
||||
/* not a symlink, append component and go to the next part */
|
||||
strcat(tmp, arr[i]);
|
||||
if(i) {
|
||||
if(chdir(arr[i]) < 0)
|
||||
goto path_real_done;
|
||||
strcat(tmp, "/");
|
||||
}
|
||||
}
|
||||
ret = tmp;
|
||||
|
||||
path_real_done:
|
||||
if(ret != tmp)
|
||||
free(tmp);
|
||||
if(lnkl > 0)
|
||||
free(lnk);
|
||||
free(arr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
char *path_real(const char *orig) {
|
||||
int links = 0;
|
||||
char *tmp, *ret;
|
||||
DIR *d;
|
||||
|
||||
if(orig == NULL)
|
||||
return NULL;
|
||||
if((d = opendir(".")) == NULL)
|
||||
return NULL;
|
||||
|
||||
tmp = path_absolute(orig);
|
||||
ret = path_real_rec(tmp, &links);
|
||||
free(tmp);
|
||||
fchdir(dirfd(d));
|
||||
closedir(d);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int path_chdir(const char *path) {
|
||||
char **arr, *cur;
|
||||
int i, r = -1;
|
||||
DIR *d;
|
||||
|
||||
if((d = opendir(".")) == NULL)
|
||||
return -1;
|
||||
|
||||
if((cur = path_absolute(path)) == NULL)
|
||||
return -1;
|
||||
|
||||
i = path_split(cur, &arr);
|
||||
if(chdir("/") < 0)
|
||||
goto path_chdir_done;
|
||||
while(--i >= 0)
|
||||
if(chdir(arr[i]) < 0)
|
||||
goto path_chdir_done;
|
||||
r = 0;
|
||||
|
||||
path_chdir_done:
|
||||
if(r < 0)
|
||||
fchdir(dirfd(d));
|
||||
free(cur);
|
||||
free(arr);
|
||||
return r;
|
||||
}
|
||||
|
47
src/path.h
Обычный файл
47
src/path.h
Обычный файл
@ -0,0 +1,47 @@
|
||||
/* ncdu - NCurses Disk Usage
|
||||
|
||||
Copyright (c) 2007-2009 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.
|
||||
|
||||
*/
|
||||
/*
|
||||
path.c reimplements realpath() and chdir(), both functions accept
|
||||
arbitrary long path names not limited by PATH_MAX.
|
||||
|
||||
Caveats/bugs:
|
||||
- path_real uses chdir(), so it's not thread safe
|
||||
- Process requires +x access for all directory components
|
||||
- Potentionally slow
|
||||
- Doesn't check return value of malloc() and realloc()
|
||||
- path_real doesn't check for the existance of the last component
|
||||
*/
|
||||
|
||||
#ifndef _path_h
|
||||
#define _path_h
|
||||
|
||||
/* path_real reimplements realpath(). The returned string is allocated
|
||||
by malloc() and should be manually free()d by the programmer. */
|
||||
extern char *path_real(const char *);
|
||||
|
||||
/* works exactly the same as chdir() */
|
||||
extern int path_chdir(const char *);
|
||||
|
||||
#endif
|
Загрузка…
x
Ссылка в новой задаче
Block a user