プリント基板設計・シミュレーション

TOP > アポロレポート > コラム > 第128回 プログラミングについて『作っておくと便利なちょっとしたプログラム その12』
コラム
2026/05/26

第128回 プログラミングについて『作っておくと便利なちょっとしたプログラム その12』

アポロレポート

           プログラミングについて 第128回目

       『作っておくと便利なちょっとしたプログラム その12』


 前回は予定よりも説明が長くなってしまい途中で中断しましたので、
今回は前回の説明を踏まえて作っていきましょう。

検索ディレクトリリストはファイル名のリストと同じ構造体、

 struct file_name_strct{
  char *fname;
  struct file_name_strct *next;
 };

を使うことにして、

 struct file_name_strct *dir_name_top=NULL;
 struct file_name_strct *dir_name_end=NULL;

とメイン関数の前に定義しておき、関数 display_total_line を次のようにします。

 display_total_lines()
 {
  if(file_name_top==NULL){
   printf("\n===== 標準入力 =====\n\n");

   if(display_file_total_lines()== -1)return -1;

   printf("\n==================\n");
   printf("\n合計 %10u 行\n",grand_total_lines);
  }
  else{
   struct file_name_strct *fnc,*fnn;

                 /* 検索ディレクトリのポインタです。
                   前回の説明では1つのディレクトリを検索し終 わったら削除する説明をし
                   ましたが、 1つの検索ファイルの検索が終了した時点でまとめて削除す
                   ることにします。
                   dnc は現在の検索ディレクトリを示す目的に使用します */
   struct file_name_strct *dnc;

                 /* パス名とファイル名を連結して、検索を開始するときに使用する変数です */
   char *search_fname;

   search_fname=new_strcat(NULL,"");
       fname=new_strcat(NULL,"");

                 /* 指定された検索ファイル名の最初から開始し、
                   指定されたものがなくなるまで繰返します */
   fnc=file_name_top;
   while(fnc){
                 /* 検索するファイル名のディレクトリ名とファイ
                   ル名を分離し、ディレクトリ名は検索ディレク
                   トリリストに追加、ファイル名を検索するファ
                   イル名にします */
    add_file_dir(fnc);

                 /* 検索ディレクトリの先頭から開始し、リストが
                   終了するまで繰返します */
    for(dnc=dir_name_top;dnc;dnc=dnc->next){
                 /* 検索ディレクトリ名と検索ファイル名を連結し
                   て、それを表示します */
     search_fname[0]='\0';
     search_fname=new_strcat(search_fname,dnc->fname);
     search_fname=new_strcat(search_fname,fnc->fname);

     printf("\n===== %s =====\n",search_fname);

                 /* 小計用の変数を初期化します */
     total_files=0;
     total_lines=0;
                 /* 検索を開始 */
     if(start_search(search_fname)){
      do{

       if(!is_file()       )continue;
       if( is_hide() && !with_hide)continue;
                 /* ファイル数の小計と総計を1つ増やす */
          total_files++;
       grand_total_files++;
                 /* 行数を表示するファイルをオープンする */
       fname[0]='\0';
       fname=new_strcat(fname,dnc->fname);
       fname=new_strcat(fname,file_info.name);

       fp=fopen(fname,"r");
       if(fp==NULL){
        printf(
         "ファイル\n"
         "%s\n"
         "のオープン時にエラーが発生しました。\n"
         "%s",fname,strerror(errno));
       }
       else{
                 /* 行数を表示 */
        if(display_file_total_lines()== -1)return -1;

        fclose(fp);
       }
      }while(next_search());
     }
                 /* 小計を表示 */
     if(total_files){
      printf("\n小計 %10d ファイル\n",total_files);
      printf( "   %10d 行\n"   ,total_lines);
     }
                 /* サブディレクトリを検索 */
     if(search_sub_directory){
                 /* 検索するサブディレクトリは現在の検索ディレ
                   クトリに *.* を連結したもの */
      search_fname[0]='\0';
      search_fname=new_strcat(search_fname,dnc->fname);
      search_fname=new_strcat(search_fname,"*.*");

      if(!start_search(search_fname))continue;

      do{
       if(!is_dir ()       )continue;
       if( is_hide() && !with_hide)continue;
                 /* 現在のパスを示す . と1つ上のパスを示す
                   .. も検索されますが、この2つは検索ディレ
                  クトリには加えません */
       if(strcmp(file_info.name,"." )==0)continue;
       if(strcmp(file_info.name,"..")==0)continue;

                 /* 現在の検索ディレクトリの次以降に発見された
                   ディレクトリを挿入 */
       insert_dir(dnc,dnc->fname,file_info.name);
      }while(next_search());
     }
    }
                 /* 1つのファイルの検索が終了した時点で検索デ
                  ィレクトリリスト全部を削除します */
    purge_dir_name();
                 /* 先頭の検索ファイルを削除し、次の検索ファイ
                  ルにポインタを移動します */
    fnn=fnc->next;
    DELETE(fnc->fname);
    DELETE(fnc);
    fnc=fnn;
   }
                 /* 不要になったメモリを解放 */
   DELETE(search_fname);
   DELETE(fname);

   fname=NULL;
                 /* 総数を表示 */
   printf("\n========================\n");
   printf("\n合計 %10u ファイル\n",grand_total_files);
   printf( "   %10u 行\n"   ,grand_total_lines);
  }

  return 1;
 }

難しい内容ではありませんがゆっくり読んで理解して下さい。

残っている関数 add_file_dir と insert_dir の2つです。まずは insert_dir。

この関数は指定された検索ディレクトリを指定された検索ディレクトリデータ以降に挿入するものです。

         /* dnp はそれ以降に挿入する検索ディレクトリデータのポインタ、
          dir_name1 と dir_name2 は連結して1つのディレクトリ名にしま
          す */

 insert_dir(dnp,dir_name1,dir_name2)
 struct file_name_strct *dnp;
 char *dir_name1;
 char *dir_name2;
 {
  struct file_name_strct *dnc;
         /* 挿入検索ディレクトリデータを作成します */
  dnc=NEW(struct file_name_strct,1);
  dnc->fname=new_strcat(NULL,dir_name1);

  if(dir_name1[strlen(dir_name1)-1]!='\\'
  && dir_name2[0] && dir_name2[0]!='\\'){
   dnc->fname=new_strcat(dnc->fname,"\\");
  }

  dnc->fname=new_strcat(dnc->fname,dir_name2);

  if(dnc->fname[strlen(dnc->fname)-1]!='\\'){
   dnc->fname=new_strcat(dnc->fname,"\\");
  }

  dnc->next=NULL;

         /* dnp がヌルのときには検索ディレクトリリストには何もないので
          リストの先頭として挿入します。それ以外は dnp 以降に挿入しま
          す */
  if(dnp==NULL){
   if(dir_name_end==NULL)dir_name_top   =dnc;
   else         dir_name_end->next=dnc;

   dir_name_end=dnc;
  }
  else{
        /* 挿入すべき適当な場所を決めます */
   while(dnp->next){
    if(stricmp(dnc->fname,dnp->next->fname)<0)break;
    dnp=dnp->next;
   }
        /* ポインタをつなぎ直します */
   dnc->next=dnp->next;
   dnp->next=dnc;
  }

  return 1;
 }

次に add_file_dir です。この関数は引数で渡された struct file_name_strct 構造体のディレクトリ名とファイル名を分離し、ディレクトリ名は検索ディレクトリリストに挿入し、ファイル名は引数で渡された struct file_name_strct 構造体のファイル名にします。

 add_file_dir(fnc)
 struct file_name_strct *fnc;
 {
  char drive[_MAX_PATH], dir[_MAX_PATH];
  char file[_MAX_PATH],type[_MAX_PATH];

                /* Visual C の関数で、パス名を各成分に
                  分離します */
  _splitpath(fnc->fname,drive,dir,file,type);

                /* ドライブ名とディレクトリ名を連結したものを
                  (最後の文字は必ず \ にする)挿入する検索デ
                  ィレクトリ名にします */
  strcat(drive,dir );
  if(!drive[0])strcpy(drive,".");
  if(drive[strlen(drive)-1]!='\\')strcat(drive,"\\");

                /* もしファイル名がないときには *.* をファイル
                  名にします */
  strcat(file,type);
  if(!file[0])strcpy(drive,"*.*");

  fnc->fname[0]='\0';
  fnc->fname=new_strcat(fnc->fname,file);

                /* 検索ディレクトリの先頭以降に検索ディレクトリ
                  を挿入します。この add_file_dir 関数が呼び出
                  されたときには必ず、検索ディレクトリリストは
                  空になっています */
  insert_dir(dir_name_top,drive,"");

  return 1;
 }

これにヘルプの機能を追加してプログラムは完成です。最後にプログラムの全文です。
今回も私のリストそのまま載せてありますので簡単なコメントも残っています。タブは4タブのスペースに変換してあります。関数の順番はバラバラです。

#define VERSION_NUMBER "v1.0"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <io.h>

#define READ_BUF_SIZE    8192

#define NEW(type,element)  (type *)malloc(sizeof(type)*(element))
#define DELETE(addr)    free(addr)

#define PROC_MODE_DISPLAY  1
#define PROC_MODE_HELP   2
int proc_mode=PROC_MODE_DISPLAY;

int with_hide      =0;
int search_sub_directory=0;

char *fname=NULL;
FILE *fp=stdin;

struct file_name_strct{
  char *fname;
  struct file_name_strct *next;
};
struct file_name_strct *file_name_top=NULL;
struct file_name_strct *file_name_end=NULL;

struct file_name_strct *dir_name_top=NULL;
struct file_name_strct *dir_name_end=NULL;

     int    total_files=0;
unsigned int    total_lines=0;
     int grand_total_files=0;
unsigned int grand_total_lines=0;

struct _finddata_t file_info;
long        find_handle;

char *new_strcat(char *string1,char *string2);

/*==================================== main ===================================
* Main routine.
*/
void main(argc,argv)
int  argc;
char *argv[];
{
/*
*  " Analize argment "
*/
  if(analize_argment(argc,argv)== -1)goto end;
/*
*  " Process "
*/
  if(proc_mode==PROC_MODE_HELP){
    display_help();
  }
  else{
    if(display_total_lines()== -1)goto end;
  }
/*
*  " End of program "
*/
end:
  exit(0);
}
/*================================= new_strcat ================================
* strcat.
*/
char *new_strcat(char *string1,char *string2)
{
  if(string1==NULL){
    string1=NEW(char,strlen(string2)+1);
    strcpy(string1,string2);
  }
  else{
    int l;

    l=strlen(string1)+strlen(string2)+1;

    if(l>_msize(string1))string1=realloc(string1,l);

    strcat(string1,string2);
  }

  return string1;
}
/*================================ start_search ===============================
* Start search.
*/
start_search(search_fname)
char *search_fname;
{
  find_handle=_findfirst(search_fname,&file_info);

  if(find_handle== -1)return 0;
  else        return 1;
}
/*================================ next_search ================================
* Search next.
*/
next_search()
{
  if(_findnext(find_handle,&file_info)){
    _findclose(find_handle);
    return 0;
  }

  return 1;
}
/*================================== is_file ==================================
* Return found file is file or not.
*/
is_file()
{
  if(file_info.attrib & _A_ARCH)return 1;

  return 0;
}
/*=================================== is_dir ==================================
* Return found file is directory or not.
*/
is_dir()
{
  if(file_info.attrib & _A_SUBDIR)return 1;

  return 0;
}
/*================================== is_hide ==================================
* Return found file is hide or not.
*/
is_hide()
{
  if(file_info.attrib & _A_HIDDEN)return 1;

  return 0;
}
/*============================== analize_argment ==============================
* Analize argment.
*/
analize_argment(argc,argv)
int  argc;
char *argv[];
{
  int i;

  for(i=1;i<argc;i++){
    if(stricmp(argv[i],"-?")==0
    || stricmp(argv[i],"/?")==0){
      proc_mode=PROC_MODE_HELP;
      return 1;
    }
    else if(stricmp(argv[i],"-s")==0
       || stricmp(argv[i],"/s")==0)search_sub_directory=1;
    else if(stricmp(argv[i],"-h")==0
       || stricmp(argv[i],"/h")==0)with_hide=1;
    else{
      struct file_name_strct *fnc;

      fnc=NEW(struct file_name_strct,1);
      fnc->fname=new_strcat(NULL,argv[i]);
      fnc->next =NULL;

      if(file_name_end==NULL)file_name_top   =fnc;
      else          file_name_end->next=fnc;

      file_name_end=fnc;
    }
  }

  return 1;
}
/*=============================== purge_dir_name ==============================
* Purge directory name.
*/
purge_dir_name()
{
  struct file_name_strct *dnc,*dnn;

  dnc=dir_name_top;
  while(dnc){
    dnn=dnc->next;

    DELETE(dnc->fname);
    DELETE(dnc);

    dnc=dnn;
  }

  dir_name_top=NULL;
  dir_name_end=NULL;

  return 1;
}
/*================================ add_file_dir ===============================
* Add directory of file_name_strct file name.
* And reject directory name from it.
*/
add_file_dir(fnc)
struct file_name_strct *fnc;
{
  char drive[_MAX_PATH], dir[_MAX_PATH];
  char file[_MAX_PATH],type[_MAX_PATH];

  _splitpath(fnc->fname,drive,dir,file,type);

  strcat(drive,dir );
  if(!drive[0])strcpy(drive,".");
  if(drive[strlen(drive)-1]!='\\')strcat(drive,"\\");

  strcat(file,type);
  if(!file[0])strcpy(drive,"*.*");

  fnc->fname[0]='\0';
  fnc->fname=new_strcat(fnc->fname,file);

  insert_dir(dir_name_top,drive,"");

  return 1;
}
/*========================== display_file_total_lines =========================
* Display total line of input.
*/
display_file_total_lines()
{
  unsigned int lines;

  char data[READ_BUF_SIZE];
  int ldata,i;

  lines=0;

  while(ldata=fread(data,1,READ_BUF_SIZE,fp)){
    for(i=0;i<ldata;i++){
      if(data[i]=='\n')lines++;
    }
  }

  if(fname)printf(" %7d 行 %s\n",lines,fname);
  else   printf(" %7d 行\n",lines);

     total_lines+=lines;
  grand_total_lines+=lines;

  return 1;
}
/*================================= insert_dir ================================
* Insert directory.
*/
insert_dir(dnp,dir_name1,dir_name2)
struct file_name_strct *dnp;
char *dir_name1;
char *dir_name2;
{
  struct file_name_strct *dnc;

  dnc=NEW(struct file_name_strct,1);
  dnc->fname=new_strcat(NULL,dir_name1);

  if(dir_name1[strlen(dir_name1)-1]!='\\'
  && dir_name2[0] && dir_name2[0]!='\\'){
    dnc->fname=new_strcat(dnc->fname,"\\");
  }

  dnc->fname=new_strcat(dnc->fname,dir_name2);

  if(dnc->fname[strlen(dnc->fname)-1]!='\\'){
    dnc->fname=new_strcat(dnc->fname,"\\");
  }

  dnc->next=NULL;

  if(dnp==NULL){
    if(dir_name_end==NULL)dir_name_top   =dnc;
    else         dir_name_end->next=dnc;

    dir_name_end=dnc;
  }
  else{
    while(dnp->next){
      if(stricmp(dnc->fname,dnp->next->fname)<0)break;
      dnp=dnp->next;
    }

    dnc->next=dnp->next;
    dnp->next=dnc;
  }

  return 1;
}
/*============================ display_total_lines ============================
* Display total lines.
*/
display_total_lines()
{
  if(file_name_top==NULL){
    printf("\n===== 標準入力 =====\n\n");

    if(display_file_total_lines()== -1)return -1;

    printf("\n==================\n");
    printf("\n合計 %10u 行\n",grand_total_lines);
  }
  else{
    struct file_name_strct *fnc,*fnn;
    struct file_name_strct *dnc;
    char *search_fname;

    search_fname=new_strcat(NULL,"");
        fname=new_strcat(NULL,"");

    fnc=file_name_top;
    while(fnc){
      add_file_dir(fnc);

      for(dnc=dir_name_top;dnc;dnc=dnc->next){
        search_fname[0]='\0';
        search_fname=new_strcat(search_fname,dnc->fname);
        search_fname=new_strcat(search_fname,fnc->fname);

        printf("\n===== %s =====\n",search_fname);

        total_files=0;
        total_lines=0;

        if(start_search(search_fname)){
          do{
            if(!is_file()       )continue;
            if( is_hide() && !with_hide)continue;

               total_files++;
            grand_total_files++;

            fname[0]='\0';
            fname=new_strcat(fname,dnc->fname);
            fname=new_strcat(fname,file_info.name);

            fp=fopen(fname,"r");
            if(fp==NULL){
              printf(
                "ファイル\n"
                "%s\n"
                "のオープン時にエラーが発生しました。\n"
                "%s",fname,strerror(errno));
            }
            else{
              if(display_file_total_lines()== -1)return -1;

              fclose(fp);
            }
          }while(next_search());
        }

        if(total_files){
          printf("\n小計 %10d ファイル\n",total_files);
          printf( "   %10u 行\n"   ,total_lines);
        }

        if(search_sub_directory){
          search_fname[0]='\0';
          search_fname=new_strcat(search_fname,dnc->fname);
          search_fname=new_strcat(search_fname,"*.*");

          if(!start_search(search_fname))continue;

          do{
            if(!is_dir ()       )continue;
            if( is_hide() && !with_hide)continue;

            if(strcmp(file_info.name,"." )==0)continue;
            if(strcmp(file_info.name,"..")==0)continue;

            insert_dir(dnc,dnc->fname,file_info.name);
          }while(next_search());
        }
      }

      purge_dir_name();

      fnn=fnc->next;
      DELETE(fnc->fname);
      DELETE(fnc);
      fnc=fnn;
    }

    DELETE(search_fname);
    DELETE(fname);

    fname=NULL;

    printf("\n========================\n");
    printf("\n合計 %10d ファイル\n",grand_total_files);
    printf( "   %10u 行\n"   ,grand_total_lines);
  }

  return 1;
}
/*================================ display_help ===============================
* Display help.
*/
display_help()
{
#define p printf

p("linetot - 行数表示プログラム %s\n",VERSION_NUMBER);
p("\n");
p("使用方法:\n");
p("\n");
p(" linetot [-?] [-s] [-h] [ファイル名 [...]]\n");
p("\n");
p(" -? オンラインヘルプを表示します。\n");
p("\n");
p(" -s サブディレクトリも検索します。\n");
p("\n");
p(" -h 非表示属性のファイルやディレクトリも検索します。\n");
p("\n");
p(" ファイル名\n");
p("  表示するファイル名。\n");
p("  ファイル名は複数個指定でき、ワイルドカードも使用できます。\n");
p("  ファイル名を指定しなかったときには標準入力からの読み込みになります。\n");

  return 1;
}

それではまた次回。

そのお悩み、
アポロ技研に話してみませんか?

アポロ技研でワンランク上の物創りへ!
そのお悩み、アポロ技研に話してみませんか?