Words and Lines Counting Utility

Environment: VC6, MFC

This utility is very useful for counting the number of lines and words in programs or text files. The utility is very simple but it is also very powerful. You can find any group of files and see through the list and check out the number of words and lines in a group of files. This was the kind of utility that I was wanting for a long time and I finally wrote it.

First, it has a dialog-based interface and it has numerous descriptive fields that you may find very useful as you start using it. The utility operates in a single thread and does PeekMessage to do GUI tasks, as shown in the following snippet of code:

MSG Msg;
if(::PeekMessage(&Msg, m_hWnd, WM_NULL, WM_USER-1, PM_NOREMOVE)){
   ::PeekMessage(&Msg, m_hWnd, WM_NULL, WM_USER-1, PM_REMOVE);
   TranslateMessage(&Msg);
   DispatchMessage(&Msg);
}

This dispatches any pending messages to the main window, thus not blocking. This is because the MFC classes cannot be exported to a different thread and it gives lots of problems, as I've observed in many of my utilities.

First, the directory where the files have to be counted is to be mentioned in the edit box provided. The Browse button will open the shell dialog box for directory browsing; this is implemented in the following piece of code:

BOOL ShellGetPathCount(HANDLE hDlg, char lpszPath[])
{
  BOOL bRet;
  char szPath[MAX_PATH];
  LPITEMIDLIST lpil;
  HGLOBAL hgMem;
  BROWSEINFO bi;

  bi.hwndOwner=(HWND) hDlg;
  bi.pidlRoot = NULL;
  bi.pszDisplayName = szPath;
  bi.lpszTitle = "Select Destination Folder";
  bi.ulFlags = BIF_RETURNONLYFSDIRS;
  bi.lpfn = NULL;
  bi.lParam = 0L;
  bi.iImage = 0;
  lpil = SHBrowseForFolder(&bi);
  if(lpil == NULL)
    return FALSE;
  bRet = SHGetPathFromIDList(lpil, lpszPath);
  hgMem = GlobalHandle(lpil);
  GlobalFree(hgMem);
  return bRet;
}

The above code gets the path from the ID list and gives the caller the path. Then it frees the memory associated with the list. It fills a structure and passes it to the shell API that displays the dialog. If OK is clicked, it gets the directory from the list and returns the directory to the caller. It can be any directory that is browsible using Explorer.

Then, after the "Start Count" button is clicked, it starts the following routine that does the work of counting the lines and words in the filespecs provided in the next edit box. The filespecs can contain multiple filespecs such as *.C;*.CPP;*.txt without any spaces in between them. This counts the files in that directory with those filespecs. You can also put in individual files such as RFC.TXT, and so forth, which are ; delimeted.

void CWordLineDlg::OnCount()
{
  // TODO: Add your control notification handler code here
  char Directory[MAX_PATH], FileSpecs[300], TmpStr[MAX_PATH],
                            *TmpVal=FileSpecs;
  int NumFileSpecs=0, NumWords=0, NumLines=0;
  Stop = false;

  m_Specs.GetWindowText(FileSpecs, 290);
  m_Dir.GetWindowText(Directory, MAX_PATH);
  if(!strlen(Directory)){
    AfxMessageBox("Directory not found");
    return;
  }
  if(Directory[3])
    strcat(Directory, "\\");
  while(strlen(TmpVal)){
    if(*(TmpVal++) == ';'){
      NumFileSpecs++;
    }
  }
  NumFileSpecs++;
  TmpVal=FileSpecs;
  if(_chdir(Directory)){
    AfxMessageBox("Directory not found");
    return;
  }
  if(!strlen(FileSpecs)){
    AfxMessageBox("Filespec not found");
    return;
  }
  m_List.DeleteAllItems();
  m_Words.SetWindowText("");
  m_Files.SetWindowText("");
  m_Lines.SetWindowText("");
  m_TFiles.SetWindowText("");
  m_MarkedWords.SetWindowText("");
  m_MarkedLines.SetWindowText("");
  m_Start.EnableWindow(FALSE);
  m_Stop.EnableWindow(TRUE);
  for(int i=0; i<NumFileSpecs; i++){
    strcpy(TmpStr, Directory);
    char TmpStr1[5]={0, 0};
    while(strlen(TmpVal)){
      if(*(TmpVal++) != ';'){
        TmpStr1[0]=*(TmpVal-1);
        strcat(TmpStr, TmpStr1);
      }
      else
        break;
    }
    struct _finddata_t ff;
    long hFind = _findfirst(TmpStr, &ff);
    while(strcmp(ff.name, ".")==0 || strcmp(ff.name, "..")==0)
      _findnext(hFind, &ff);
    do{
      HANDLE hFile = CreateFile(ff.name, GENERIC_READ, NULL, NULL,
                                OPEN_EXISTING,
                                FILE_ATTRIBUTE_NORMAL, NULL);
      if(hFile==INVALID_HANDLE_VALUE){
        continue;
      }
      NumWords = CountWords(hFile);
      SetFilePointer(hFile, 0, 0, FILE_BEGIN);
      NumLines = CountLines(hFile);
      CloseHandle(hFile);
      m_List.InsertItem(0, ff.name);
      char TmpStr2[20];
      wsprintf(TmpStr2, "%u", NumWords);
      m_List.SetItemText(0, 1, TmpStr2);
      wsprintf(TmpStr2, "%u", NumLines);
      m_List.SetItemText(0, 2, TmpStr2);
      MSG Msg;
      if(::PeekMessage(&Msg, m_hWnd, WM_NULL, WM_USER-1,
                             PM_NOREMOVE)){
        ::PeekMessage(&Msg, m_hWnd, WM_NULL, WM_USER-1, PM_REMOVE);
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
      }
      if(Stop)
        break;
    }while(!_findnext(hFind, &ff));
    _findclose(hFind);
  }
  m_Stop.EnableWindow(FALSE);
  m_Start.EnableWindow(TRUE);
  long Tmp;
  OnItemchangedFilelist(NULL, &Tmp);
}

The files are searches from the first filespec fully in the directory and followed by the next one in the list. Then, after the files have been found, selected files are listed with the number of words and lines in the box provided beneath the list view. This way, you can easily see the number of words and lines found in specific files alone and you can watch specific text or program files.

The routine that groups the files marked is provided below. It calculates this information every time the files in the list are being touched.

void CWordLineDlg::OnItemchangedFilelist(NMHDR* pNMHDR,
                                         LRESULT* pResult)
{
  NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
  // TODO: Add your control notification handler code here
  int NumWords=0, NumLines=0, MarkedLines=0, MarkedWords=0,
      NumMarkedFiles=0;
  char TmpStr[100];
  for(int i=0; i<m_List.GetItemCount(); i++){
    m_List.GetItemText(i, 1, TmpStr, 20);
    NumWords += atoi(TmpStr);
    m_List.GetItemText(i, 2, TmpStr, 20);
    NumLines += atoi(TmpStr);
    if(m_List.GetItemState(i, LVIS_SELECTED)==LVIS_SELECTED){
      m_List.GetItemText(i, 1, TmpStr, 20);
      MarkedWords += atoi(TmpStr);
      m_List.GetItemText(i, 2, TmpStr, 20);
      MarkedLines += atoi(TmpStr);
      wsprintf(TmpStr, "%u", MarkedWords);
      m_MarkedWords.SetWindowText(TmpStr);
      wsprintf(TmpStr, "%u", MarkedLines);
      m_MarkedLines.SetWindowText(TmpStr);
      NumMarkedFiles++;
      wsprintf(TmpStr, "%u", NumMarkedFiles);
      m_Files.SetWindowText(TmpStr);
    }
  }
  wsprintf(TmpStr, "%u", NumWords);
  m_Words.SetWindowText(TmpStr);
  wsprintf(TmpStr, "%u", NumLines);
  m_Lines.SetWindowText(TmpStr);
  wsprintf(TmpStr, "%u", m_List.GetItemCount());
  m_TFiles.SetWindowText(TmpStr);

  *pResult = 0;
}

When an item changes its selection state, the notification handler calls the preceding routine. When it gets activated, it calculates the total marked files and displays the information accordingly.

Downloads

Download source - WordLine.zip 12 Kb


About the Author

Vinoj Kumar

I have been programming for the past 16 years. I started programming in 1990. I came to Windows in 1993. I have authored a book called, "Classic Utilities Using Assembly Language" , 1995. In my free time I listen a lot to Kenny G sax all the albums and Valentine Classics Songs. I like to watch a lot of TinTin adventure Comics. I am currently working in K7 Computing antivirus company (www.k7computing.com) as Senior Technical Lead. My contact is: Phone: +91 944 411 7353

Comments

  • Excellent!

    Posted by Legacy on 03/07/2003 12:00am

    Originally posted by: Raj

    It is a very good program and excellet work there.

    Reply
  • Words and Lines Counting Utility

    Posted by Legacy on 11/27/2002 12:00am

    Originally posted by: Mamtha Ragupathy

    That was a Wonderful Article. Very Helpful!!! Thanks Vinoj Kumar.
    

    Reply
  • Give peace a chance ...

    Posted by Legacy on 08/30/2002 12:00am

    Originally posted by: Christiaan


    Criticasters should spend more time to contribute themselves, assuming that their contribution contains at least one headline that is worth to be revealed ( which is the case with this article ) ...

    Reply
  • I don't get it?

    Posted by Legacy on 08/30/2002 12:00am

    Originally posted by: Hector

    First, its slow. Why have two separate functions to CountWords() and then file reset to CountLines()? Why not do it at the same time? Also Read by 1 byte at a time? Ouch! You can speed up this program tremendously!

    Second, I believe the word count is off by one.

    Third, the only thing I see useful if your dialog "file processor". There is more worthiness in this.

    Separate your "file processor" functions into their own files and then place a stub for "ProcessFiles(filename)"

    then your dialog can be used for many things.

    Reply
  • Reinventing

    Posted by Legacy on 08/30/2002 12:00am

    Originally posted by: Andrey Koubychev

    Day by day I see CodeGuru and CodeProject authors reinventing old UNIX programs :)

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • Protecting business operations means shifting the priorities around availability from disaster recovery to business continuity. Enterprises are shifting their focus from recovery from a disaster to preventing the disaster in the first place. With this change in mindset, disaster recovery is no longer the first line of defense; the organizations with a smarter business continuity practice are less impacted when disasters strike. This SmartSelect will provide insight to help guide your enterprise toward better …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds