LINUX.ORG.RU

Не работает велосипед pwd на сях

 


0

2

Ребят, помогите, пожалуйста, написать велосипед pwd на сях.

Вот, что набросал, получаю «ошибку сегментации (сделан дамп памяти)».

Уверен, что тут что с выделенмем памяти под эти указателями...

Указатели никогда не трогал почти, под unux пишу впервые.

Выручите, посмотрите, пожалуйста:

#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>

using namespace std;

int main()
{
    DIR* dir;
    struct  dirent* entry;
    struct stat st;
    char *cur_ent;
    char find_elem[100][20];
    char *last;
    char *end;


    strcpy(end,"home");
    strcpy(last, "OS3cpp"); // OS3cpp - название проги

    while(last!=end) {
        strcat(cur_ent,"//");
        printf("%s", last);
        strcat(cur_ent, last);
        chdir("..");
        dir = opendir(".");
        int i = 0;
        while ((entry = readdir(dir))!=NULL) {
            if(strcmp(entry->d_name, ".") == 0 && strcmp(entry->d_name, "..") == 0){
                stat(entry->d_name,&st);
                    if(S_ISDIR(st.st_mode)) {
                        strcpy(find_elem[i++], entry->d_name);
                    }
            }
        }

        int t = 0;
        while(t == 0) {
            i++;
            chdir(find_elem[i]);
            dir = opendir(".");
            while((entry = readdir(dir))!=NULL) {
                if(entry->d_name == last) {
                    chdir("..");
                    strcat(last,find_elem[i]);
                    t=1;
                } else chdir("..");
                t=1;
            }
        }
    }
    return 0;
}

Перемещено leave из general



Последнее исправление: beastie (всего исправлений: 2)

Кошмар какой. Проще всего - делать getenv на $PWD. Ну и strcpy, strcat и прочее - это не C++, используй std::string.

DELIRIUM ☆☆☆☆☆
()
char *last;
char *end;
strcpy(end,"home");
strcpy(last, "OS3cpp");

Начнем с того, что определимся с языком. Если это все же должны быть плюсы, то используй массивы чаров:

char last[] = "OS3cpp";

Если хочешь с указателями, то объявляй так:

const char* end = "home";
В таком случае память будет выделена сразу.

Ты же, если я все правильно помню, *last и *end создаешь нулевой длины и пытаешься в них записать сколько-то байт.

leave ★★★★★
()
Последнее исправление: leave (всего исправлений: 2)
Ответ на: комментарий от SWAROVSKI

Но чего-то я побаиваюсь, что пошлет он меня

Зато кто-то из вас двоих после этого научится понимать как правильно составлять и понимать ТЗ

zolden ★★★★★
()
Ответ на: комментарий от zolden

ТЗ: Реализовать программу определения полного имени файла (подъем по дереву каталогов)

Все-таки он требует подъем по дереву каталогов. Т.е. как я пытался сделать изначально.

SWAROVSKI
() автор топика
Ответ на: комментарий от SWAROVSKI

Я не знаю, что ожидает твой учитель, но «кошеный» вариант был бы вот такой:

#include <limits.h>
#include <stdio.h>
#include <unistd.h>

int
main()
{
        char path[PATH_MAX];

        getcwd(path, sizeof(path));
        puts(path);

        return 0;
}

beastie ★★★★★
()
Ответ на: комментарий от SWAROVSKI

Все-таки он требует подъем по дереву каталогов. Т.е. как я пытался сделать изначально.

Значит, начал ты правильно. Но рекурсия тут больше уместна:

#include <sys/stat.h>
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define ROOT_INODE 2

char *
pwd(char *path, size_t len)
{
        DIR *dirp = opendir("..");
        struct dirent *dp;
        struct stat st;

        stat(".", &st);
        if (st.st_ino == ROOT_INODE)
                return path;

        dirp = opendir("..");
        while ((dp = readdir(dirp)) != NULL) {
                if (st.st_ino == dp->d_ino) {
                        size_t dlen = strlen(dp->d_name);
                        size_t plen = strlen(path);

                        if (len < dlen + 2)
                                return path;

                        memmove(path + dlen + 1, path, plen + 1);
                        memcpy(path + 1, dp->d_name, dlen);
                        path[0] = '/';
                        chdir("..");

                        return pwd(path, len - dlen - 1);
                }
        }
        closedir(dirp);

        return path;
}

int
main()
{
        char path[PATH_MAX] = {};

        puts(pwd(path, sizeof(path)));

        return 0;
}
beastie ★★★★★
()
Ответ на: комментарий от SWAROVSKI

Одному мне кажется, что подъем по дереву и pwd могут давать разные результаты?

t184256 ★★★★★
()
    char *last;
    char *end;


    strcpy(end,"home");
    strcpy(last, "OS3cpp"); // OS3cpp - название проги

Дальше смотреть не стал. Сдается мне, что книжку по си вы даже не открывали.

andreyu ★★★★★
()
Ответ на: комментарий от SWAROVSKI

char *end = malloc( sizeof(*end ) );

Нет, нужно было открыть учебник по си.

andreyu ★★★★★
()

И кто-то еще будет называть мои велосипеды быдлокодом???

Eddy_Em ☆☆☆☆☆
()
GETCWD(3)                                                                        Linux Programmer's Manual                                                                       GETCWD(3)



NAME
       getcwd, getwd, get_current_dir_name - get current working directory

SYNOPSIS
       #include <unistd.h>

       char *getcwd(char *buf, size_t size);

       char *getwd(char *buf);

       char *get_current_dir_name(void);
Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от beastie

предлогали

Позор!

без

Выдрать исходники get_current_dir_name из glibc.

Ну или с инодами париться, сравнивая инод текущей директории с инодами из ..

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от beastie

Тьфу ты! Вот что значит — "тред не читай, сразу отвечай". Ты этот вариант и предлагал.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от beastie

Премного благодарен Вам.

Сейчас буду вникать в код.

Пока интересует вопрос: эта программа вообще не требует данных на вход, т.е. аргументов

$1, $2...
?

SWAROVSKI
() автор топика
Ответ на: комментарий от beastie

Спасибо. Теперь можно, пожалуйста, Вас попросить, объяснить, что же тут происходит внутри Вашего цикла

while
?

С последними тремя строками я разобрался, а вот, что происходит выше — почти не понимаю.

SWAROVSKI
() автор топика
Ответ на: комментарий от SWAROVSKI

Кстати, у меня тут маленький баг закрался: opendir нужен, конечно, только один раз.

Т.е. поменять объявление переменной на DIR *dirp;

По принципу действия:

  • смотрим inode директория, где мы находимся
  • если inode == 2 — возвращаем результат (hint: у root inode всегда равен 2)
  • открываем выше-лежащий директорий и ищем там (while) имя директория с вышенайденным inode — это наше имя
  • если нашли — добавляем в path и идём на уровень выше
  • повторяем весь процесс рекурсивно, пока не дойдём до root

PS: там вполне ещё могут быть баги, т.к. делалось на коленке. В частности в самом root вернётся, скорей всего неправильный результат. Оставлю это как домашнее задание. ;)

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 5)
Ответ на: комментарий от beastie

Спасибо, но никак не пойму, что происходит внутри цикла.

if (st.st_ino == dp->d_ino) { // Тут вы проверяете на равенство что? Порядковый номер файла и что? Не очень понимаю смысл этого условия...

    size_t dlen = strlen(dp->d_name); // Здесь записываете в переменную dlen ко-во символов в dp->d_name, где поле d_name есть начало массива символов, задающего имя элемента каталога. Тут разве не всегда 1 будет? Гуглил и нашел такую вещь "char d_name [1];" в структуре dirent.

    size_t plen = strlen(path); // Здесь записываете не понимаю что... :( но догадываюсь, что это как-то связано с рекурсией...

                        if (len < dlen + 2)
                                return path; // аналогично не понимаю, но что-то с рекурсией

                        memmove(path + dlen + 1, path, plen + 1);
                        memcpy(path + 1, dp->d_name, dlen);
                        path[0] = '/';
                        chdir("..");

                        return pwd(path, len - dlen - 1);
                }

Если Вас не затруднит, можете прокомментировать подробно каждую строчку в этом куске кода...

Заранее благодарен.

SWAROVSKI
() автор топика
Ответ на: комментарий от beastie

Проверил: получается косяк с примонтированными директориями:

scan tmp (ino: 14096, need: 9497)
(ls -i / выдает для /tmp значение 9497, а вот readdir для /tmp выдает 14096).

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от SWAROVSKI

Annotated and patched version without boundary checking (it's homework to you) :)

#include <sys/stat.h>
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define ROOT_INODE 2

/* insert name with prefix '/' at beginning of path */
/* not really effectiv, but enough for our purposes */
/* caveats: no boundaries checks, assumimg buffer is big enough */
/* homework: add boundary checking */
char *
insert(char *path, char *name)
{
	size_t plen = strlen(path);			/* len of path */
	size_t nlen = strlen(name);			/* len of name */

	memmove(path + nlen + 1, path, plen + 1);	/* move old content */
	memcpy(path + 1, name, nlen);			/* insert name */
	path[0] = '/';					/* ensure prefix */

	return path;
}

/* walk up a dir tree up to the root */
/* caveats: no boundaries checks, assumimg buffer is big enough */
/* homework: add boundary checking */
/* homework: what happens if we don't find a right inode? fix it. */
char *
pwd(char *path)
{
        struct stat st;
        struct dirent *dp;
        DIR *dirp;

        stat(".", &st);				/* get stats of current dir */
						/* if we are in root, we are done */
        if (st.st_ino == ROOT_INODE) {		/* root's inode is always 2 */
		path[0] = '/';			/* ensure there is a prefix */
                return path;
	}

	chdir("..");				/* go up to the parent dir */

        dirp = opendir(".");			/* open it */
        while ((dp = readdir(dirp)) != NULL) {	/* walk through content */
                if (st.st_ino == dp->d_ino) {	/* looking for inode */
			insert(path, dp->d_name);	/* found name, add */
			break;			/* break the loop */
                }
        }
	closedir(dirp);				/* clean up */

	return pwd(path);			/* repeat */
}

int
main()
{
        char path[PATH_MAX] = {};		/* zeroed buffer big enough */

        puts(pwd(path));			/* do it and show it */

        return 0;
}
beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 4)
Ответ на: комментарий от beastie

Вот что у меня получилось:

#include <sys/stat.h>
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

#define BUFSZ       (128)

char *pwd(char **path, size_t *len){
	size_t L = 0;
	struct dirent *dp;
	struct stat st;
	stat(".", &st);
	if(!path) return NULL;
	if(!*path){
		*path = malloc(BUFSZ + 1);
		**path = 0;
		L = BUFSZ;
		if(len) *len = BUFSZ;
	}else{
		if(len) L = *len;
		else return NULL;
	}
	DIR *dirp = opendir("..");
	while((dp = readdir(dirp))){
			if(strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
				continue;
			if(strcmp(dp->d_name, "/") == 0){
				printf("got ROOT\n");
				if(**path != '/'){
					**path = '/';
					*(*path+1) = 0;
				}
				return *path;
			}
			printf("scan %s (ino: %zd, need: %zd), %zd\n", dp->d_name, dp->d_ino, st.st_ino, st.st_rdev);
			if(st.st_ino == dp->d_ino){
					size_t dlen = strlen(dp->d_name) + 2;
					size_t plen = strlen(*path);
					if(plen + dlen < L){
						*path = realloc(*path, plen + dlen);
						L = plen + dlen;
					}
					if(len)
						memmove(*path + dlen - 1, *path, plen+1);
					memcpy(*path + 1, dp->d_name, dlen-2);
					**path = '/';
					chdir("..");
					printf("process %s\n", *path);
					return pwd(path, &L);
			}
	}
	closedir(dirp);
	return *path;
}

int main(int argc, char **argv){
	char *path = NULL;
	if(argc == 2)
		chdir(argv[1]);
	puts(pwd(&path, NULL));
	return 0;
}
Для примонтированных ФС выдает путь только до их корня.

Так как при помощи stat() невозможно определить имя файла/директории, то из readdir() уровня выше мы никак не можем сказать, куда оно примонтировано → надо еще с mount вертеться.

В общем, фиговое домашнее задание. Очень уж жирным кодом оборачивается.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

Но задача такая же идиотская и честно признаться совсем не для начинающих. (Да и продвинутым оно не впилось.)

В общем, классический случай никакущего в педагогическом плане я-у-мамы-какир-лянуксоида-быдлокодера-препода мнящего себя пупом земли. (ТС — можешь ему так и передать от моего имени. А может он и сюда заходит, кто его знает?)

Полезности, навыков и знаний от таких задач — ноль целых, ноль десятых.

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 1)
Ответ на: комментарий от Eddy_Em

задача ТСу - вывод в консольку. Вот на обратном ходе рекурсии и выведи компоненты пути, ненадо мучить строчные функции. Код станет втрое короче и яснее.

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

Вот на обратном ходе рекурсии и выведи компоненты пути, ненадо мучить строчные функции

это для beastie :-) промахнулся

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

Тоже вариант, но будет уже не так красиво. ;)

beastie ★★★★★
()
Ответ на: комментарий от beastie

Кстати, я частично решил проблему смонтированных директорий. Теперь оно не работает только в том случае, если ты прямо из корня смонтированной директории вызываешь (понятно, что в этом случае тестовый инод == 2 и от корневого никак директорию отличить нельзя — тут уже сисвызовы ФС нужны).

#include <sys/stat.h>
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <mntent.h>

#define ROOTINO     (2)
#define BUFSZ       (128)

typedef struct _mntdef{
	char *mntpoint;
	struct _mntdef *next;
} mntdef;

mntdef *mntlist = NULL;

void addmntdef(char *name){
	mntdef *m, *last = NULL;
	last = mntlist;
	if(last){
		do{
			if(strcmp(last->mntpoint, name) == 0) return;
			if(last->next) last = last->next;
			else break;
		}while(1);
	}
	m = calloc(1, sizeof(mntdef));
	m->mntpoint = strdup(name);
	if(!last) mntlist = m;
	else last->next = m;
}


void push_dir(char **old, char* new, size_t *len, size_t *L){
	size_t dlen = strlen(new) + 2;
	size_t plen = strlen(*old);
	if(plen + dlen < *L){
		*old = realloc(*old, plen + dlen);
		*L = plen + dlen;
	}
	if(len)
		memmove(*old + dlen - 1, *old, plen+1);
	memcpy(*old + 1, new, dlen-2);
	if(plen == 0) *(*old + dlen - 1) = 0;
	**old = '/';
}

struct stat original_st;
char *chkmounted(char *path){
	mntdef *m = mntlist;
	char p[1024];
	struct stat st;
	do{
		snprintf(p, 1023, "%s%s", m->mntpoint, path);
		stat(p, &st);
		if(st.st_ino == original_st.st_ino){
			if(strcmp("/", m->mntpoint))
				return m->mntpoint + 1;
			else
				return NULL;
		}
		if(m->next) m = m->next;
		else break;
	}while(1);
	return NULL;
}

char *pwd(char **path, size_t *len){
	size_t L = 0;
	struct dirent *dp;
	struct stat st;
	stat(".", &st);
	if(!path) return NULL;
	if(!*path){
		*path = malloc(BUFSZ + 1);
		**path = 0;
		L = BUFSZ;
		if(len) *len = BUFSZ;
	}else{
		if(len) L = *len;
		else return NULL;
	}
	if(st.st_ino == ROOTINO){
		char *m = NULL;
		if((m = chkmounted(*path))) push_dir(path, m, len, &L);
		if(**path != '/'){
			**path = '/';
			*(*path+1) = 0;
		}
		return *path;
	}
	DIR *dirp = opendir("..");
	while((dp = readdir(dirp))){
			if(strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
				continue;
			if(st.st_ino == dp->d_ino){
					push_dir(path, dp->d_name, len, &L);
					chdir("..");
					return pwd(path, &L);
			}
	}
	chdir("..");
	return pwd(path, &L);
	closedir(dirp);
	return *path;
}


int main(int argc, char **argv){
	char *path = NULL;
	struct stat st;
	struct mntent *m;
	FILE *f;
	f = setmntent(_PATH_MOUNTED, "r");
	while((m = getmntent(f))){
		stat(m->mnt_dir, &st);
		addmntdef(m->mnt_dir);
	}
	endmntent(f);
	if(argc == 2){
		if(chdir(argv[1])){
			perror("chdir()");
			return -1;
		}
	}
	stat(".", &original_st);
	puts(pwd(&path, NULL));
	return 0;
}

Eddy_Em ☆☆☆☆☆
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.