티스토리 뷰
이번 파트는 동기화 리스트.txt 파일을 읽고, 거기에 저장된 경로를 추출하는 것이다.
때문에 유효한 경로인지 확인하는 것과 상대 경로 구분하고 베이스 설정 등을 소개할 것이다.
이번 코드는 서버 코드만 나오면 클라이언트 관련 코드는 없다
실행 인자
먼저 실행 시 입력하는 인자에 대해서이다. 우선 서버 간 동기화는 고려하지 않기에 실행 인자는 동기화 리스트 1개이다.
다음은 인자 관련 코드이다.
#define MAX_LENGTH 4096
int main(int argc, char *argv[])
{
char sync_file_path[MAX_LENGTH];
memset(sync_file_path, 0, sizeof(sync_file_path));
// 인자 미입력시 입력
if (0 == file_path_check(argv[1]))
{
do
{
printf("파일 리스트를 입력하시오:");
scanf("%s", sync_file_path);
} while (0 == file_path_check(sync_file_path));
}
else
{
snprintf(sync_file_path, MAX_LENGTH, "%s", argv[1]);
}
]
main 함수에서 실행인자를 받고, 그 인자를 저장할 변수를 선언한다.
이때 MAX_LENGTH가 4096인 이유는 리눅스에서 최대로 인식가능한 경로의 길이가 4096이기 때문이다.
이렇게 받은 인자가 유효한지 확인하고, 유효하지 않다면 사용자가 직접 입력하도록 if문을 분기하였다.
while로 계속 유효한 경로인지 확인하고, 유효한 경로면 sync_file_path에 저장한다.
file_path_check는 경로의 유효성 확인 변수로 코드는 다음과 같다.
/**
* @brief file_path_check
* 유효한 파일 경로인지 검증하는 함수
*
* @param char *path
*
* @return int
*/
int file_path_check(char *path)
{
if (NULL == path)
{
return 0;
}
struct stat file_data;
memset(&file_data, 0, sizeof(file_data));
int result = 0;
result = stat(path, &file_data);
if (0 == result)
{
return 1;
}
else
{
printf("path: %s에 파일이 존재하지 않습니다.\n", path);
return 0;
}
}
stat 함수로 해당 경로에 파일이 존재하는지를 확인하고, 1을 반환하면 유효한 경로임을 입증하는 함수이다.
이 함수는 이후에도 경로 검증함수로 많이 사용된다.
베이스 경로
경로는 절대 경로가 있고, 상대 경로가 존재한다.
절대 경로의 경우, 파일이나 디렉터리의 명확한 주소를 제시하지만 pc마다 경로의 명칭이 다르기에 여러 사용자가 사용하기에는 어렵다.
때문에 주로, 상대경로를 사용한다.
그러나 상대 경로는 주로 현재위치를 기준으로 하기에, 해당 위치에서 벗어나면 인식을 하디 못한다.
따라서 실행 위치와 관계없이 상대경로를 사용하기 위해서는 기준이 되는 베이스 경로가 필요하다.
char base_path[MAX_LENGTH];
int main(int argc, char *argv[])
{
char sync_file_path[MAX_LENGTH];
memset(sync_file_path, 0, sizeof(sync_file_path));
// 인자 미입력시 입력
if (0 == file_path_check(argv[1]))
{
do
{
printf("파일 리스트를 입력하시오:");
scanf("%s", sync_file_path);
} while (0 == file_path_check(sync_file_path));
}
else
{
snprintf(sync_file_path, MAX_LENGTH, "%s", argv[1]);
}
memset(base_path, 0, sizeof(base_path));
snprintf(base_path, MAX_LENGTH, "%s", sync_file_path);
flie_name_remover(base_path);
}
이 코드는 베이스 경로를 인자로 받은 동기화 리스트를 기준으로 한다.
동기화 리스트에는 경로가 저장되고, 이 경로가 상대 경로인 경우 기준이 동기화 리스트라고 가정하기 때문이다.
때문에 동기화 리스트의 경로를 저장하고, flie_name_remover 함수로 해당 경로에서 동기화 리스트 파일만 제외한다.
/**
* @brief flie_name_remover
* 경로에서 파일의 이름을 지워주는 함수
*
* @param char *path
*
* @return int
*/
int flie_name_remover(char *path)
{
if (NULL == path)
{
printf("flie_name_remover의 매개변수가 올바르지 않습니다.\n");
return -1;
}
// 문자열에서 마지막 '/' 문자를 찾음
char *lastSlashPos = strrchr(path, '/');
if (NULL != lastSlashPos)
{
*(lastSlashPos) = '\0'; // '/' 다음 문자를 null 문자로 설정
}
return 1;
}
문자열에서 마지막 '/' 문자를 찾고, 해당 문자열 이후를 null문자로 변경한다.
이렇게 만든 베이스 경로는 이후 상대 경로 앞에 붙여서 사용한다.
예) /home/../1.txt => /1.txt
동기화 리스트 읽기
동기화 리스트는 라인 단위로 경로가 나누어져 있다.
예시는 아래와 같다.
/home/nesusa/shared_dir/1.txt
./2.html
nesusa/shared_dir/3
이제 동기화 파일을 읽어 경로를 추출할 것이다.
/**
* @brief process_sync_file
* 동기화 파일을 읽고, 해쉬 테이블에 저장하는 함수
*
* @param file_list_t **file_list
*
* @param char *sync_file_path
*
* @return void
*/
void process_sync_file(file_list_t **file_list, char *sync_file_path)
{
if (NULL == sync_file_path)
{
printf("process_sync_file의 매개변수가 올바르지 않습니다.\n");
return;
}
FILE *file = fopen(sync_file_path, "r");
static char buffer[MAX_LENGTH];
memset(buffer, 0, sizeof(buffer));
char *token = NULL;
while (fgets(buffer, sizeof(buffer), file) != NULL)
{
if (2 == relative_path_check(buffer))
{
token = absolute_path_change(buffer);
}
else
{
token = strtok(buffer, " \n");
}
if (1 == file_path_check(token))
{
add_path(file_list, token);
}
}
fclose(file);
}
먼저 동기화 리스트의 경로를 저장할 해쉬 테이블 file_list을 선언하고, 동기화 리스트의 경로와 함께 process_sync_file 함수의 인자로 넘겨준다.
그후 동기화 리스트를 fopen으로 열어서, fgets로 라인 단위로 읽는다.
이렇게 읽은 문자열을 buffer변수에 저장하고, relative_path_check로 상대경로인지를 확인한다.
절대 경로하면 strtok로 문자열에 포함된 \n을 제거하고, 이후 file_path_check로 유효한 경로인지를 확인한다.
유효한 경로면 해쉬 테이블에 추가한다.
상대경로인 경우 앞써 말했듯이, absolute_path_change함수에서 베이스 경로와 결합된 경로로 수정하여 반환한다.
이후에는 절대경로와 같다.
이후 동기화 리스트를 다 읽었다면 fclose로 파일을 닫고 종료된다.
다음은 relative_path_check와 absolute_path_change 코드이다.
/**
* @brief relative_path_check
* 상대 경로인지를 체크하는 정규식 함수
*
* @param char *path
*
*
* @return int result
*/
int relative_path_check(char *path)
{
if (NULL == path)
{
printf("relative_path_check의 매개변수가 올바르지 않습니다.\n");
return -1;
}
const char *pattern = "(\\./|\\.\\./|\\.\\.)";
int check_result = 0;
regex_t regex;
memset(®ex, 0, sizeof(regex));
int reti = 0;
reti = regcomp(®ex, pattern, REG_EXTENDED | REG_ICASE);
// 정규식 매치 여부 확인
reti = regexec(®ex, path, 0, NULL, 0);
if (REG_NOMATCH == reti)
{
// 절대 경로
check_result = 1;
}
else
{
// 상대 경로
check_result = 2;
}
regfree(®ex);
return check_result;
}
정규식으로 ../ , ./ 등의 상대경로가 있을 시에는 2를, 절대 경로는 1을 반환한다.
이전에 몰랐는데, valgrind로 확인한 결과 regfree로 정규식을 해제하지 않으면 메모리 릭이 발생한다.
/**
* @brief absolute_path_change
* 상대 경로를 절대경로로 변환하는 함수
*
* @param char *path
*
* @return void
*/
char *absolute_path_change(char *path)
{
if (NULL == path)
{
printf("absolute_path_change의 매개변수가 올바르지 않습니다.\n");
return NULL;
}
char buffer[MAX_LENGTH * 2];
memset(buffer, 0, sizeof(buffer));
if (base_path[0] == '\0')
{
realpath(path, buffer);
}
else
{
snprintf(buffer, MAX_LENGTH * 2, "%s/%s", base_path, path);
}
char *token = NULL;
token = strtok(buffer, "\n");
return token;
}
absolute_path_change는 전역 변수로 베이스 경로가 존재하면 베이스 경로와 상대경로를 합치지만
베이스 경로가 없는 경우는 그냥 realpath함수로 현재 실행위치를 기준으로 절대경로로 변환한다.
이렇게 나눈 이유는 이유 유닛 테스트 함수 작성시에 편의를 위해서와 클라이언트에서 상대경로를 사용할 경우를 위해 범용성을 높이기 위해서이다.
'프로젝트 > 파일 동기화' 카테고리의 다른 글
프로젝트: 파일 동기화 - 스레드 (0) | 2024.02.17 |
---|---|
프로젝트: 파일 동기화 - 동기화 리스트와 파일 변경 감지 (0) | 2024.02.15 |
프로젝트: 파일 동기화 - TCP 소켓 통신 (1) | 2024.02.14 |
프로젝트: 파일 동기화 - 해쉬 테이블 (1) | 2024.02.14 |
프로젝트: 파일 동기화 - 요구사항 (0) | 2024.02.07 |