ls 명령어를 구현했다.아직 옵션을 주거나 경로를 지정하는 기능은 구현하지 않았고, 현재 디렉토리 기준으로만 가능하다.숨김 파일과 . .. 디렉토리도 출력하지 않는다. (-a 옵션 추가 예정)
zsh에서 ls 명령어를 사용하면 리스트 중 (가장 긴 파일명 + 1)의 공간으로 파일들이 출력되는 것을 알 수 있다.
이것을 만들기 위해 파일 목록을 연결리스트에 저장하고 가장 긴 파일명을 저장했다.
터미널 창의 사이즈에 따라 한 줄에 출력되는 파일의 개수가 달라졌는데 그것은 어떻게 해야할 지 아직 모르겠다.
그래서 한 라인에 8개의 파일만 출력되도록 구현했다.
파일(디렉토리) 목록은 opendir(), readdir()을 통해 읽었다.
// my_ls.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <dirent.h>
typedef struct _node {
char *fileName;
struct _node *next;
} Node;
Node *head = NULL;
int maxLength = -1;
void addNode(Node *newNode) {
if (head == NULL) {
head = newNode;
}
else {
Node *cur = head;
while (cur->next != NULL) {
cur = cur->next;
}
cur->next = newNode;
}
}
void deleteNode() {
while (head != NULL) {
Node *del = head;
head = head->next;
free(del);
}
}
Node *createNode(char *fname) {
Node *ret = (Node *)malloc(sizeof(Node));
ret->next = NULL;
ret->fileName = fname;
return ret;
}
void makeList() {
DIR *dp;
struct dirent *entry;
struct stat statbuf;
if ((dp = opendir(".")) == NULL) {
fprintf(stderr, "opendir error\n");
exit(1);
}
while ((entry = readdir(dp)) != NULL) {
if (stat(entry->d_name, &statbuf) < 0) {
fprintf(stderr, "stat error\n");
exit(1);
}
Node *newNode = createNode(entry->d_name);
addNode(newNode);
if ((int)strlen(newNode->fileName) > maxLength)
maxLength = (int)strlen(newNode->fileName);
}
maxLength++;
closedir(dp);
}
void ls() {
makeList();
Node *cur = head;
int cnt = 0;
while (cur != NULL) {
if (cur->fileName[0] == '.') {
cur = cur->next;
continue;
}
int tab = maxLength - (int)strlen(cur->fileName);
printf("%s", cur->fileName);
for (int i = 0; i < tab; i++) {
printf(" ");
}
cur = cur->next;
cnt++;
if (cnt % 8 == 0)
printf("\n");
}
deleteNode();
printf("\n\n");
}
// SM_shell.c
#include "my_header.h"
...
void lsExec() {
pid_t pid;
if ((pid = fork()) < 0) {
fprintf(stderr, "fork error\n");
exit(1);
}
else if (pid == 0) {
execl(execName, "ls", (char *)0);
exit(0);
}
else {
pid = wait(NULL);
}
}
void init() {
getcwd(execPath, PATH_MAX);
sprintf(homePath, "%s", getenv("HOME"));
}
void prompt() {
char input[STR_MAX];
int command;
while (1) {
printf("SM_shell > ");
fgets(input, STR_MAX, stdin);
input[strlen(input) - 1] = '\0';
if (!strcmp(input, "clear")) {
system("clear");
continue;
}
if (!strcmp(input, commandList[0])) { // exit
fprintf(stdout, "* SM_shell exit... *\n");
exit(0);
} else if (!strcmp(input, commandList[1])) { // help
command = CMD_HELP;
} else if (!strcmp(input, commandList[2])) {
printf("%s\n\n", execPath);
command = CMD_PWD;
continue;
} else if (!strcmp(input, commandList[3])) {
command = CMD_LS;
}
else {
command = NOT_CMD;
}
if (command & CMD_HELP || command == NOT_CMD) {
helpExec();
} else if (command & CMD_LS) {
lsExec();
}
}
}
int main(int argc, char **argv) {
init();
strcpy(execName, argv[0]);
if (!strcmp(argv[0], "help")) {
help();
} else if(!strcmp(argv[0], "ls")) {
ls();
}
else {
prompt();
}
exit(0);
}
위 코드는 아래 이미지를 클릭해 깃허브에서도 확인할 수 있다.
'Linux > 쉘, 쉘 명령어 구현하기' 카테고리의 다른 글
[쉘 구현하기] execv() 사용을 위한 문자열 배열 만들기 (0) | 2023.08.11 |
---|---|
[쉘 구현하기] ls 명령어 터미널 크기에 최적화 (0) | 2023.08.09 |
[쉘 구현하기] pwd 명령어 구현 (0) | 2023.08.08 |
[쉘 구현하기] help 명령어 구현 (0) | 2023.08.08 |
[쉘 구현하기] 쉘 초기 형태 (0) | 2023.08.08 |