
/* 
 プログラムの局所性を調べる
 オリジナルプログラムに手を入れる前処理を行う
 
 オリジナルプログラムの冒頭に
 	#define XXX {fprintf(stderr, "@@@ %d\n",__LINE__);};
 といったマクロ定義を行い、各行に
 
 main()
 {
   XXX   printf("hello\n");
   XXX   printf("hello\n");
   XXX   printf("hello\n");
   XXX   printf("hello\n"); 
 }
 
 というように各行前に行位置記録を入れたものに変換する。
 変換後のプログラムをコンパイルし、実行時に bash にて
 $ ./a.out 2> a.log
 のように実行すればログが得られる。
 変換後のプログラムをコンパイルする際に最適化しないこと
 （実行順序がおかしくなる）
 
 コメント /-* *-/ については事前に uncomment.c によって除去
 
 */

#include  <stdio.h>
#include  <stdlib.h>
#include  <unistd.h>
 
void usage() {
fprintf(stderr, "usage: locsim [-vn]\n");
fprintf(stderr, "   v : verbose mode.\n");
  return;
}

int vmode=1;

char *str_def="    #define XXX {fprintf(stderr, \"@@@ %d\\n\",__LINE__);};";
char *str_lab="XXX";
char *str_pad="   "; /* パディング用空白 */

/* 
 先頭文字が宣言文、またはブロック { } 文字かどうかをチェック
 戻り値が 0 ならロギング処理付加、1 なら無視 
*/
int checkcom(char *s)
{
  if(strlen(s)==0) { return 1;
  } else if( strncmp(s, "int ", 4) == 0 ) { return 1; 
  } else if( strncmp(s, "char ", 5) == 0 ) { return 1; 
  } else if( strncmp(s, "extern ", 7) == 0 ) { return 1; 
  } else if( strncmp(s, "unsigned ", 9) == 0 ) { return 1; 
  } else if( strncmp(s, "double ", 7) == 0 ) { return 1; 
  } else if( strncmp(s, "float ", 6) == 0 ) { return 1; 
  } else if( strncmp(s, "struct ", 7) == 0 ) { return 1; 
  } else if( strncmp(s, "typedef ", 8) == 0 ) { return 1; 
  } else if( strncmp(s, "case ", 5) == 0 ) { return 1; 
  } else if( strncmp(s, "else ", 5) == 0 ) { return 1; 
  } else if( strncmp(s, "{", 1) == 0 ) { return 1; 
  } else if( strncmp(s, "}", 1) == 0 ) { return 1; 
  };
  return 0;
}

/* 
 最初の空白・タブ以外の文字の位置を返す
 先頭なら 0
 全部空白・タブなら -1
*/
int checkline(char *s)
{
  int i, l;

  l=strlen(s);
  for(i=0; i<l; i++) {
    if( (s[i]!=' ' )&&( s[i]!='\t' )) break;
  };
  if(i==l) i= -1;

  return i;
}

/*
 入り口
 */
int main(int argc, char *argv[])
{
  int ch;
  extern char *optarg;
  extern int optind;
  char buf[256];
  int cmode, smode;
  int i, p;

  /* option recognise */
  while ((ch = getopt(argc, argv, "vn")) != EOF) {
    switch((char)ch) {
    case 'v': /* verbose mode */
      vmode=1;
      break;
    case 'n': /* quiet mode */
      vmode=0;
      break;
    case '?': /* Help message */
    default:
      usage();
    fprintf(stderr,"locsim : unrecognized parameter exist.\n");
      exit(1);
    };
  };
  /* Skip the position of argc,argv[] */
  argc -= optind;
  argv += optind;

  if(argc!=0) {
    usage();
  fprintf(stderr,"locsim : extra parameter is exist.\n");
    exit(1);
  };
  
  printf("\n%s\n\n", str_def);

  cmode=0; /* out of comment */
  smode=0; /* out of string */

  fgets(buf, sizeof(buf), stdin);
  while( !feof(stdin) ) {
    
    p=checkline(buf); /* 空白以外の文字の場所をチェック */
    switch(p) {
    case -1: /* 空行（または空白だけの行だった）はパス */
      break;
    case 0: /* 行頭から文字あり global section と見てパス */
      printf("%s %s", str_pad, buf);
      break;
    default: /* インデントあり。行頭に付加 */
      if( checkcom(&buf[p]) == 1 ) { /* 宣言文、中カッコだった */
	printf("%s %s", str_pad, buf); /* パス */
      } else {
	printf("%s %s", str_lab, buf); /* ロギング処理付加 */
      };
    };
    fgets(buf, sizeof(buf), stdin);
  };

  return 0;
}
