. 파일에서 찾기

파일검색중에 조건에 맞는 파일을 찾았을 때 FindInFiles 함수는 콜백으로 미리 등록된 다음 함수를 호출한다. 이 함수는 인수로 전달된 Path 파일에서 arFind[0].Get(0) 문자열을 찾아 검색결과창의 hList에 출력한다.

 

int OnFindFile(TCHAR *Path,DWORD Attr,LPVOID pCustom)

{

     TCHAR what[256];

     HANDLE hFile;

     DWORD size, dwRead;

     TCHAR *buf, *pbuf;

     TCHAR *p;

     int line,col;

     TCHAR Text[501];

     TCHAR Mes[2048];

     LVITEM LI;

     int idx;

 

     if (Attr & FILE_ATTRIBUTE_DIRECTORY)

          return 0;

 

     lstrcpy(what,arFind[0].Get(0));

 

     hFile=CreateFile(Path,GENERIC_READ,0,NULL,

          OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

     if (hFile==INVALID_HANDLE_VALUE) {

          return -1;

     }

     size=GetFileSize(hFile,NULL);

     size=min(size,30*1048576);

     buf=(TCHAR *)malloc(size+1);

     ReadFile(hFile,buf,size,&dwRead,NULL);

     buf[size]=0;

 

     for (pbuf=buf;;) {

          p=FindString(pbuf,0,what,TRUE,(FindFlag & AE_FIND_MATCHCASE)!=0);

          if (p == NULL)

              break;

 

          if (FindFlag & AE_FIND_WHOLEWORD) {

              if (!((p==buf || IsDelimiter(*(p-1))) && IsDelimiter(*(p+lstrlen(what))))) {

                   pbuf=p+lstrlen(what);

                   continue;

              }

          }

 

          TotalFind++;

          GetLineNumAndText(buf,p,Text,line,col);

 

          LI.mask=LVIF_TEXT;

          LI.state=0;

          LI.stateMask=0;

          LI.iSubItem=0;

          LI.iItem=ListView_GetItemCount(hList);

          itoa(TotalFind,Mes,10);

          LI.pszText=Mes;

          idx=ListView_InsertItem(hList,&LI);

 

          if (FindFlag & AE_FIND_SHORTPATH) {

              wsprintf(Mes,"%s(%d,%d)",Path+lstrlen(arFind[2].Get(0))+1,line,col);

          } else {

              wsprintf(Mes,"%s(%d,%d)",Path,line,col);

          }

          ListView_SetItemText(hList,idx,1,Mes);

 

          ListView_SetItemText(hList,idx,2,Text);

          ListView_EnsureVisible(hList,idx,FALSE);

          UpdateWindow(hList);

 

          pbuf=p+lstrlen(what);

     }

 

     free(buf);

     CloseHandle(hFile);

     return 0;

}

 

Attr인수로는 파일의 속성이 전달되는데 폴더인 경우는 문자열을 찾을 수가 없으므로 그냥 리턴하면 된다. Path 파일의 크기만큼 버퍼를 할당하여 이 파일을 몽땅 읽어들이되 30MB 이상일 때는 30MB까지만 읽어들이도록 했다. 이 문제에 대해서는 앞에서도 설명한 적이 있는데 우연히 700MB나 되는 파일이 검색되면 큰일나기 때문이다. 참고로 큰 파일도 시스템에 무리를 주지 않고 검색하려면 파일 매핑을 사용하면 된다.

검색 대상 파일을 버퍼에 읽어들였으면 이 버퍼에서 찾고자 하는 문자열 what이 있는지 검색한다. 이때 ApiEdit.cpp에 정의되어 있는 FindString이라는 함수를 사용했는데 이 함수는 CApiEdit의 멤버가 아니므로 원형만 알고 있으면 누구나 호출할 수 있도록 되어 있다. 이 함수가 CApiEdit의 멤버가 될 수 없는 이유는 바로 여기서 이 함수의 기능을 필요로 하기 때문이다. FindString 함수는 대소문자 옵션은 처리하지만 단어 단위로 옵션은 처리하지 않으므로 검색된 후 단어 단위로 옵션 여부에 따라 양쪽에 구분자가 있는지를 점검해보아야 한다.

조건에 맞는 문자열을 찾았으면 TotalFind 1 증가시키고 검색결과창의 리스트 뷰에 검색결과를 출력한다. 어떤 파일의 어느 위치에서 어떤 문자열을 찾았다는 보고서를 작성하는 셈인데 이 작업을 위해 다음 두 개의 도우미 함수가 필요하다.

 

void GetLineNumAndText(TCHAR *szBuf, TCHAR *ptr, TCHAR *Text, int &line, int &col)

{

     TCHAR *p=szBuf;

     TCHAR *t=Text;

     line=1;

 

     while (p != ptr) {

          if (*p == ‘\n’) {

              line++;

          }

          p++;

     }

 

     while ((p != szBuf-1) && (*p != ‘\r’) && (*p != ‘\n’)) p--;

     p++;

 

     col=ptr-p+1;

 

     while ((*p != 0) && (*p != ‘\r’) && (*p != ‘\n’) && (t-Text < 499)) {

          if (*p == ‘\t’) {

              *t++=‘ ‘;

              *t++=‘ ‘;

              *t++=‘ ‘;

              *t=‘ ‘;

          } else {

              if (IsDBCSLeadByte(*p)) {

                   *t++=*p++;

              }

              *t=*p;

          }

          t++;

          p++;

     }

     *t=0;

}

 

BOOL IsDelimiter(TCHAR t)

{

     return (strchr(" \t\r\n\"\’.,<>:;/()[]{}",t) || t==0);

}

 

GetLineNumAndText 함수는 szBuf에서 ptr번지의 줄번호와 칸번호를 구해 참조인수로 전달된 line, col에 대입하고 그 줄의 텍스트를 구해 세 번째 인수로 전달된 Text 버퍼에 복사한다. 탭문자는 그대로 출력하면 이상한 모양의 제어코드로 보이기 때문에 4개의 공백으로 바꾸어 주었다. 이 함수가 구하는 정보는 검색결과창에 출력된다. IsDelimiter 함수는 단어단위로 옵션이 선택되어 있을 때 검색된 문자열의 양쪽이 구분자인지 조사하기 위해 사용한다. 파일검색시는 문법 분석기를 사용할 수 없으므로 가장 일반적인 문자들을 구분자로 선정하였다. 앞에서 미리 말했지만 파일검색 기능은 ApiEdit와는 전혀 상관이 없고 호스트가 알아서 기능을 구현해야 하므로 구분자 점검도 호스트의 몫이다.

여기까지 작성 후 컴파일해보면 파일 찾기가 가능할 것이다. 단 아직 파일 바꾸기가 완성되지 않았으므로 OnReplaceFile 함수 호출만 잠시 주석 처리해두면 된다. 검색된 결과는 검색결과창의 리스트 뷰에 보고서 형태로 표시된다.

발견된 파일과 위치 그리고 검색 문자열의 주변 텍스트를 같이 보여준다.