// mycp.c
// 파일 복사 프로그램: 파일 또는 여러 파일을 지정된 디렉토리로 복사
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <dirent.h>
// 단일 파일 복사 함수
void copy_file(const char *src, const char *dest)
{
// 소스 파일 열기 (읽기 전용)
int src_fd = open(src, O_RDONLY);
if (src_fd < 0) {
perror("Error opening source file"); // 소스 파일 열기 실패 시 에러 메시지 출력
return;
}
// 소스 파일 정보 가져오기
struct stat src_stat;
if (fstat(src_fd, &src_stat) < 0) {
perror("Error getting file information"); // 파일 정보 가져오기 실패 시 에러 출력
close(src_fd);
return;
}
// 대상 파일 열기 (쓰기 전용, 없으면 생성, 내용 삭제)
int dest_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, src_stat.st_mode);
if (dest_fd < 0) {
perror("Error opening destination file"); // 대상 파일 열기 실패 시 에러 출력
close(src_fd);
return;
}
// 버퍼를 사용해 파일 내용을 읽고 쓰기
char buffer[1024];
ssize_t bytes_read;
while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) {
if (write(dest_fd, buffer, bytes_read) != bytes_read) {
perror("Error writing to destination file"); // 쓰기 실패 시 에러 출력
break;
}
}
// 읽기 실패 시 에러 출력
if (bytes_read < 0) {
perror("Error reading source file");
}
// 파일 디스크립터 닫기
close(src_fd);
close(dest_fd);
}
// 소스 파일을 지정된 디렉토리로 복사
void copy_to_directory(const char *src, const char *dest_dir) {
struct stat statbuf;
// 대상이 디렉토리인지 확인
if (stat(dest_dir, &statbuf) < 0 || !S_ISDIR(statbuf.st_mode)) {
fprintf(stderr, "%s is not a directory\n", dest_dir); // 디렉토리가 아니면 에러 출력
return;
}
// 대상 경로 생성: 디렉토리 경로 + 소스 파일 이름
char dest_path[1024];
snprintf(dest_path, sizeof(dest_path), "%s/%s", dest_dir, strrchr(src, '/') ? strrchr(src, '/') + 1 : src);
// 복사 실행
copy_file(src, dest_path);
}
int main(int argc, char *argv[])
{
// 인자가 부족하면 사용법 안내 메시지 출력
if (argc < 3) {
fprintf(stderr, "Usage: %s source_file(s) destination\n", argv[0]);
return EXIT_FAILURE;
}
struct stat dest_stat; // 대상 경로의 상태를 저장하는 변수
// 대상이 디렉토리일 경우
if (stat(argv[argc - 1], &dest_stat) == 0 && S_ISDIR(dest_stat.st_mode))
{
// 소스 파일들을 대상으로 지정된 디렉토리로 복사
for (int i = 1; i < argc - 1; i++) {
copy_to_directory(argv[i], argv[argc - 1]);
}
}
// 대상이 디렉토리가 아니고, 파일 복사 하나만 수행하는 경우
else if (argc == 3)
{
copy_file(argv[1], argv[2]);
}
// 다수의 파일을 복사하는 경우 대상이 디렉토리여야 함
else
{
fprintf(stderr, "Destination must be a directory when copying multiple files\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS; // 프로그램 정상 종료
}
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h> // 디렉터리 작업 관련 함수 (opendir, readdir, closedir)
#include <string.h>
#include <sys/stat.h> // 파일 상태 정보 확인 (stat, lstat)
#include <unistd.h> // 시스템 호출 및 파일 작업 관련 함수
#include <pwd.h> // 사용자 정보 관련 함수 (getpwuid)
#include <grp.h> // 그룹 정보 관련 함수 (getgrgid)
#include <time.h> // 시간 정보 처리 (strftime)
#include <sys/ioctl.h> // 터미널 크기 정보 확인 (ioctl)
#define MAX_ENTRIES 1024 // 한 디렉터리에서 처리 가능한 최대 파일 수
// 파일 정보를 저장하는 구조체
typedef struct
{
char filename[NAME_MAX]; // 파일 이름 (NAME_MAX: 시스템 정의 최대 파일 이름 길이)
struct stat filestat; // 파일 상태 정보 (lstat 함수로 가져옴)
} FileEntry;
// 파일 이름 오름차순 비교 함수
int name_compare(const void *a, const void *b)
{
FileEntry *entry1 = (FileEntry *)a;
FileEntry *entry2 = (FileEntry *)b;
return strcmp(entry1->filename, entry2->filename); // 사전순 비교
}
// 파일 이름 내림차순 비교 함수 (-r 옵션)
int name_reverse_compare(const void *a, const void *b) {
return name_compare(b, a); // 오름차순 비교 결과를 반전
}
// 파일 이름을 한 줄로 출력하는 함수
void print_inline(FileEntry entries[], int count, int terminal_width) {
int current_width = 0; // 현재 출력된 줄의 폭
for (int i = 0; i < count; i++) {
int name_length = strlen(entries[i].filename) + 1; // 파일 이름 길이 + 공백
if (current_width + name_length > terminal_width) {
printf("\n"); // 터미널 폭을 초과하면 줄 바꿈
current_width = 0;
}
printf("%s ", entries[i].filename); // 파일 이름 출력
current_width += name_length;
}
printf("\n"); // 마지막 줄 끝에 줄 바꿈
}
// 상세 정보 출력 (-l 옵션)
void print_detailed(FileEntry entry, int show_inode, int show_blocks, int show_type) {
if (show_inode) {
printf("%lu ", entry.filestat.st_ino); // inode 번호 출력
}
if (show_blocks) {
printf("%ld ", entry.filestat.st_blocks); // 블록 크기 출력
}
// 파일 유형 출력
printf("%c", S_ISDIR(entry.filestat.st_mode) ? 'd' :
S_ISLNK(entry.filestat.st_mode) ? 'l' : '-');
// 파일 권한 출력
for (int i = 8; i >= 0; i--) {
printf("%c", (entry.filestat.st_mode & (1 << i)) ?
((i % 3 == 0) ? 'r' : (i % 3 == 1) ? 'w' : 'x') : '-');
}
// 하드 링크 수, 소유자, 그룹 출력
printf(" %ld", entry.filestat.st_nlink);
struct passwd *user = getpwuid(entry.filestat.st_uid);
struct group *group = getgrgid(entry.filestat.st_gid);
printf(" %s %s", user->pw_name, group->gr_name);
// 파일 크기와 수정 시간 출력
printf(" %ld", entry.filestat.st_size);
char time_buf[20];
strftime(time_buf, sizeof(time_buf), "%b %d %H:%M", localtime(&entry.filestat.st_mtime));
printf(" %s", time_buf);
// 파일 이름 출력
printf(" %s", entry.filename);
// 파일 유형 기호 추가
if (show_type) {
if (S_ISDIR(entry.filestat.st_mode)) printf("/");
else if (entry.filestat.st_mode & S_IXUSR) printf("*");
else if (S_ISLNK(entry.filestat.st_mode)) printf("@");
}
printf("\n");
}
// 디렉터리 내용을 출력하는 함수
void list_contents(const char *directory, int show_all, int reverse, int detailed, int recursive,
int show_inode, int show_blocks, int show_type, int terminal_width) {
struct dirent *dir_entry; // 디렉터리 엔트리 구조체
DIR *dir = opendir(directory); // 디렉터리 열기
if (!dir) { // 디렉터리 열기 실패 시
perror("opendir");
return;
}
FileEntry entries[MAX_ENTRIES]; // 파일 정보 배열
int count = 0; // 파일 수
while ((dir_entry = readdir(dir)) != NULL) {
if (!show_all && dir_entry->d_name[0] == '.') {
continue; // 숨겨진 파일 제외 (-a 옵션 비활성화 시)
}
FileEntry entry;
snprintf(entry.filename, sizeof(entry.filename), "%s", dir_entry->d_name);
char full_path[PATH_MAX];
snprintf(full_path, sizeof(full_path), "%s/%s", directory, dir_entry->d_name);
if (lstat(full_path, &entry.filestat) == -1) { // 파일 상태 정보 가져오기
perror("lstat");
continue;
}
entries[count++] = entry; // 파일 정보 배열에 저장
}
closedir(dir); // 디렉터리 닫기
// 파일 정렬 (오름차순 또는 내림차순)
qsort(entries, count, sizeof(FileEntry), reverse ? name_reverse_compare : name_compare);
// 상세 출력 또는 간단 출력
if (detailed) {
for (int i = 0; i < count; i++) {
print_detailed(entries[i], show_inode, show_blocks, show_type);
}
} else {
print_inline(entries, count, terminal_width);
}
// 재귀적으로 하위 디렉터리 탐색 (-R 옵션)
if (recursive) {
for (int i = 0; i < count; i++) {
if (S_ISDIR(entries[i].filestat.st_mode) &&
strcmp(entries[i].filename, ".") != 0 &&
strcmp(entries[i].filename, "..") != 0) {
printf("\n%s:\n", entries[i].filename); // 하위 디렉터리 이름 출력
char next_directory[PATH_MAX];
snprintf(next_directory, sizeof(next_directory), "%s/%s", directory, entries[i].filename);
list_contents(next_directory, show_all, reverse, detailed, recursive,
show_inode, show_blocks, show_type, terminal_width);
}
}
}
}
// main 함수
int main(int argc, char *argv[]) {
int show_all = 0, reverse = 0, detailed = 0, recursive = 0;
int show_inode = 0, show_blocks = 0, show_type = 0;
// 터미널 폭 계산
struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
int terminal_width = w.ws_col;
const char *directory = "."; // 기본 디렉터리는 현재 디렉터리
// 명령줄 옵션 처리
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') { // 옵션 처리
for (int j = 1; argv[i][j] != '\0'; j++) {
switch (argv[i][j]) {
case 'a': show_all = 1; break;
case 'r': reverse = 1; break;
case 'l': detailed = 1; break;
case 'R': recursive = 1; break;
case 'i': show_inode = 1; break;
case 's': show_blocks = 1; break;
case 'F': show_type = 1; break;
default:
fprintf(stderr, "Unknown option: -%c\n", argv[i][j]);
exit(EXIT_FAILURE);
}
}
} else {
directory = argv[i]; // 디렉터리 이름 설정
}
}
// 디렉터리 내용 출력
list_contents(directory, show_all, reverse, detailed, recursive,
show_inode, show_blocks, show_type, terminal_width);
return 0;
}