2015-05-01 3 views
2

У меня есть домашняя работа, чтобы написать программу на C, которая действует как команда Linux ls -al. Я знаю, что в Интернете есть много примеров программ, которые делают то, что мне нужно, но у меня есть определенная проблема, к которой я не могу найти решение. Я также хочу упомянуть, что я новичок в программировании на С. Вот мой код:Linux «ls -al» как программа в C

#include <stdio.h> 
#include <stdlib.h> 
#include <dirent.h> 
#include <errno.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <unistd.h> 
#include <time.h> 
#include <pwd.h> 

int list_dir(const char *dirname)  { 

struct dirent* current_directory; 
struct stat my_stat; 
struct tm lt; 
struct passwd *pwd; // For User-ID 

DIR* directory = opendir(dirname); 


    if(directory == NULL)  { 

    printf("list_dir : %s : %s \n", dirname, strerror(errno)); 

    return 0; 
} 

    printf("Directory : %s\n", dirname); 
    printf("\n"); 

    while((current_directory = readdir(directory)))  { 

    stat(current_directory->d_name, &my_stat); 

     if ((stat(current_directory->d_name, &my_stat)) == 0) { 

     pwd = getpwuid(my_stat.st_uid); // Get User-ID 

    } 

     // Last Modified 
     time_t t = my_stat.st_mtime; 
     localtime_r(&t, &lt); 
     char timebuf[80]; 
     strftime(timebuf, sizeof(timebuf), "%c", &lt); 

     if (pwd != 0) { 

     printf("%s \t %ld \t %s \t %s", pwd->pw_name, (long)my_stat.st_size, timebuf, current_directory->d_name); 
     printf("\n"); 

     } else { 

      printf("%d \t %ld \t %s \t %s", my_stat.st_uid, (long)my_stat.st_size, timebuf, current_directory->d_name); 
      printf("\n"); 
     } 
} 
    closedir(directory);   

    return 0; 
} 

int main(int argc, char* argv[]) { 

    if (argc == 1) { 

    return list_dir ("."); 

    } else { 

    int ret = 0; 

    for (int i = 1; i < argc; i += 1) { 

     if (list_dir (argv[i]) != 0) { 

     ret = 1; 
     } 
    } 

    return ret; 
    } 
} 

Программа должна отображать те же самые вещи (без разрешений) как «ls -al». До сих пор так хорошо, если я скомпилировал его с помощью «gcc -std = gnu99 -o list_dir list_dir.c» и выполнил программу с помощью «./list_dir». Я получаю тот же результат, что и «ls -al», и выглядит так: :

username 1599 Fri May 1 20:43:57 2015 list_dir.c 

Однако если запустить программу с чем-то вроде: «./list_dir/главная/имя пользователя/Загрузки /» Я получаю это:

32727 0 Sun May 8 07:09:04 4461391  selection_sort.c 

Как вы можете видеть, программа может Получите правильную информацию о пользователе, размере файла и году. Также эта информация появляется благодаря else-случаю инструкции if (pwd! = 0). Если у меня нет другого случая, программа печатает только файлы, для которых он может получить правильную информацию. Также, если я удаляю этот оператор if, а также оператор if:

if ((stat(current_directory->d_name, &my_stat)) == 0) 

У меня возникает ошибка сегментации.

Так что мои вопросы: 1.Что я делаю неправильно. Я знаю, что я делаю что-то неправильно, потому что, как подсказка для домашней работы, у меня есть пример запуска программы, а также подсказка, что я могу использовать «stat, lstat, readlink, getpwnam, getpwuid, strftime».

2.Есть ли способ получить имя пользователя с помощью stat() и только с идентификатором пользователя, или это возможно только с помощью getpwuid?

+2

как насчет перехода через него с помощью 'gdb'? – lispHK01

+0

'gdb' - хорошая идея, но может первоначально указывать на нарушение прав доступа, существенно ниже по течению от фактической причины проблемы. 'strace', вероятно, будет еще более эффективным, так как он покажет ENOENT по вызову stat(). –

+0

каждый элемент каталога должен быть проверен, чтобы определить, является ли это вспомогательной директорией или концом списка каталогов или обычного файла или специального файла или ссылки, и т. Д. – user3629249

ответ

4

Здесь

if ((stat(current_directory->d_name, &my_stat)) == 0) { 
     pwd = getpwuid(my_stat.st_uid); // Get User-ID 
    } 

Что произойдет, если stat() потерпит неудачу? pwd имеет неинициализированное значение или неверное значение, установленное на предыдущей итерации.

Причина, по которой stat не удалась, current_directory->d_name содержит только имя файла, тогда как stat ожидает полного пути. Поэтому вам нужно добавить имя каталога в файл и передать его stat(). Вы передаете только имя файла прямо сейчас.

Что-то вроде:

char buf[1024]; 
    errno = 0; 
    snprintf(buf, sizeof buf, "%s/%s", dirname, current_directory->d_name); 
    if ((stat(buf, &my_stat)) == 0) { 
    ... 
    } 
    else { 
     printf("%s: %s\n", buf, strerror(errno)); 
     // exit here or continue to the next entry in the directory 
     //depending how you wish to handle failure 
    } 

и обрабатывать ошибки в случае stat() терпит неудачу.

У вас также есть еще один stat() внутри цикла, который ничего не делает.

+0

Спасибо большое! С помощью char [] и snprintf исправлена ​​проблема. Читая о stat(), я, вероятно, пропустил эту часть, что он исчерпывает полный путь к файлу. – user2989325

+0

Добро пожаловать. Я просто видел, что вы спрашивали о получении uid: Нет, 'stat()' не может предоставить информацию о имени пользователя. Таким образом, вы пытаетесь использовать 'getpwuid' в порядке. Однако вы можете хранить UID (и соответствующие имена пользователей), чтобы у вас не было вызова 'getpwuid', если UID уже указан в вашем списке. Это, вероятно, не имеет значения в вашем примере игрушек, но что-то примечание. –

Смежные вопросы