/*-------------------------------------------------------------------*/
/*  ＣＨＫＴＡＢ （Ｃ言語のＴＡＢサイズを自動解析をし変換をする）    */
/*                                                      Ver. 0.60    */
/*                                                                   */
/*                                             for MS-DOS            */
/*                                                                   */
/* Debug Date 940729-940819,950719-950818                            */
/*                                                                   */
/* Free Software Collection 11 Release Date   '95-08-19  By T.O6809  */
/*                                                                   */
/*-------------------------------------------------------------------*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <io.h>

#define  VER       "0.60"
#define  BUFF_MAX  1024

#define  ON        1
#define  OFF       0

#define  ESC       0x1b

typedef unsigned char uchar;

FILE *fp;
uchar bf_mst[BUFF_MAX];
uchar bf_wk[BUFF_MAX];

int   space;                                               /* １ラインのＳＰＡＣＥの総数         */
int   tab;                                                 /* １ラインのＴＡＢの総数             */
int   tab_all;                                             /* ＴＡＢの総数                       */
int   chk_tab;                                             /* ＴＡＢチェック                     */
int   answer1;                                             /* ＴＡＢチェック１                   */
int   answer2;                                             /* ＴＡＢチェック２                   */

int   t8_same_line_c, t4_same_line_c;                      /* 前後行のインデント同一カウンタ     */

int   t8_cmnt_s_cnt[BUFF_MAX], t4_cmnt_s_cnt[BUFF_MAX];    /* コメント開始位置のカラム数テーブル */
int   t8_cmnt_e_cnt[BUFF_MAX], t4_cmnt_e_cnt[BUFF_MAX];    /* コメント終了位置のカラム数テーブル */
int   t8_cmnt_s, t4_cmnt_s;                                /* コメント開始位置                   */
int   t8_cmnt_e, t4_cmnt_e;                                /* コメント終了位置                   */

int   t8_max_s[2], t4_max_s[2];                            /* コメント開始位置の最多カラム       */
int   t8_max_e[2], t4_max_e[2];                            /* コメント終了位置の最多カラム       */

int   t8_m_cnt_s[BUFF_MAX], t4_m_cnt_s[BUFF_MAX];          /* 複数行にまたがるコメント開始位置   */
int   t8_m_cnt_e[BUFF_MAX], t4_m_cnt_e[BUFF_MAX];          /* 複数行にまたがるコメント終了位置   */
int   t8_m_bf_s, t4_m_bf_s, t8_m_bf_e, t4_m_bf_e;          /* 複数行にまたがるコメント           */
int   t8_m_cmnt, t4_m_cmnt;                                /* 複数行にまたがるコメント中フラグ   */

int   o_bflag = OFF;                                       /* バッチ用リターン値                 */
int   o_tflag = OFF;                                       /* ＴＡＢ−＞ＳＰＣ変換               */
int   o_vflag = OFF;                                       /* 解析した結果表示                   */
int   o_rflag = OFF;                                       /* リダイレクト用                     */
int   o_cnt   = 0;                                         /* オプション数                       */


void  chk_tab_main(char *, int);                              /*                                 */
void  chk_cmnt(int, int *, int *, int *, int *, int *, int *);/* コメントの開始終了のェック      */
void  disp_tab(void);                                         /* ＴＡＢサイズを解析した結果表示  */
void  chk_tab_max(void);                                      /* 同一カラム最多行テーブル作成    */
void  calc_tab(void);                                         /* ＴＡＢサイズの割り出し          */
void  tab_answer(void);                                       /* ＴＡＢサイズ数を返す            */
int   chk_opt(int, char *[]);                                 /* オプションのチェック            */
void  err_msg(void);                                          /* エラーメッセージ処理            */
void  tab_spc(uchar [], uchar [], int);                       /* TAB to SPACE                    */

/*-------------------------------------------------------------------*/
/*                                                                   */
/*-------------------------------------------------------------------*/
int main(int argc, char *argv[])
{
    int  i;

    o_cnt = chk_opt(argc, argv);

    for (i = 1+o_cnt; i < argc; i++) {
        tab_all = 0;
        chk_tab_main(argv[i], argc);
    }

    return(1);
}

/*-------------------------------------------------------------------*/
/*                                                                   */
/*-------------------------------------------------------------------*/
void  chk_tab_main(char *fname, int ac)
{
    char errmsg[128], t8s[16], t8e[16], t4s[16], t4e[16];
    int  i, j, flag, indent_t8, indent_t4, indent_t8_old, indent_t4_old;

    if ((fp = fopen(fname, "r")) == NULL) {
        sprintf(errmsg, "(Error %d) File Name[%s] ", _doserrno, fname);
        perror(errmsg);
        exit(99);
    }

    if (o_tflag == 0 && ac >= 3) {
        if (o_rflag == OFF) {
            printf("%c[32m%s%c[m\n", ESC, fname, ESC);
        } else {
            printf("%s\n", fname);
        }
    }

    indent_t8_old = indent_t4_old = t8_same_line_c = t4_same_line_c = 0;
    for (i = 0;; i++, tab = 0) {
        t8_cmnt_s = t4_cmnt_s = t8_cmnt_e = t4_cmnt_e = BUFF_MAX-1;
        if (fgets(bf_mst, BUFF_MAX, fp) == NULL) {
            break;
        }
                                        /* ＴＡＢの有無チェック */
        flag = OFF;
        for (j = 0; j < strlen(bf_mst); j++) {
            if (flag == OFF && bf_mst[j] == ' ') {
                space++;
            } else if (flag == OFF && bf_mst[j] == '\t') {
                tab++;
            } else {
                if (bf_mst[j] == '\t') {
                    tab_all = tab + tab_all + 1;
                }
                flag = ON;
            }
        }
                                        /* コメント内のＴＡＢチェック */
        indent_t8 = 0;
        indent_t4 = 0;

        chk_cmnt(8, &t8_m_bf_s, &t8_m_bf_e, &t8_m_cmnt, &t8_cmnt_s, &t8_cmnt_e, &indent_t8);
        chk_cmnt(4, &t4_m_bf_s, &t4_m_bf_e, &t4_m_cmnt, &t4_cmnt_s, &t4_cmnt_e, &indent_t4);

                                        /* 前後行のインデント数が同一のものをカウント */
        if (indent_t8_old != indent_t8) {
            t8_same_line_c++;
        }
        if (indent_t4_old != indent_t4) {
            t4_same_line_c++;
        }
        indent_t8_old = indent_t8;
        indent_t4_old = indent_t4;
                                        /* コメント開始終了位置のカラム数テーブル作成 */
        t8_cmnt_s_cnt[t8_cmnt_s]++;
        t4_cmnt_s_cnt[t4_cmnt_s]++;
        t8_cmnt_e_cnt[t8_cmnt_e]++;
        t4_cmnt_e_cnt[t4_cmnt_e]++;
                                        /* 複数行にまたがるコメントカラム位置テーブル */
        if (t8_m_cmnt != 0) {
            t8_m_cnt_s[t8_m_bf_s]++;
            t8_m_cnt_e[t8_m_bf_e]++;
        }
        if (t4_m_cmnt != 0) {
            t4_m_cnt_s[t4_m_bf_s]++;
            t4_m_cnt_e[t4_m_bf_e]++;
        }

        if (o_vflag == 2 || o_vflag >= 10) {
            if (i == 0) {
                printf("\nコメント開始,終了位置\n");
                printf("%-10s     8                           4\n", "[TAB STOP]");
                printf("     行     開始     終了 Indent数      開始     終了 Indent数\n");
            }
            itoa(t8_cmnt_s, t8s, 10);
            itoa(t8_cmnt_e, t8e, 10);
            itoa(t4_cmnt_s, t4s, 10);
            itoa(t4_cmnt_e, t4e, 10);
            if (t8_cmnt_s == BUFF_MAX-1) {
                strcpy(t8s, "-");
            }
            if (t8_cmnt_e == BUFF_MAX-1) {
                strcpy(t8e, "-");
            }
            if (t4_cmnt_s == BUFF_MAX-1) {
                strcpy(t4s, "-");
            }
            if (t4_cmnt_e == BUFF_MAX-1) {
                strcpy(t4e, "-");
            }
            printf("[%5d] %8s %8s %8d, %8s %8s %8d\n", i+1, t8s, t8e, indent_t8, t4s, t4e, indent_t4);
        }
    }

    disp_tab();

    chk_tab_max();
    calc_tab();
    tab_answer();

    fclose(fp);
}

/*-------------------------------------------------------------------*/
/*  コメントの開始終了のェック                                       */
/*-------------------------------------------------------------------*/
void  chk_cmnt(int tab_stop, int *buff_s, int *buff_e, int *cmnt, int *cmnt_s, int *cmnt_e, int *indent)
{
    int   i, j, flag = OFF;

    strcpy(bf_wk, bf_mst);
    tab_spc(bf_mst, bf_wk, tab_stop);

    *buff_s = 0;
    *buff_e = strlen(bf_wk);

    for (i = 0; i < *buff_e; i++) {
        if (*cmnt == 1) {
            for (j = 0; bf_wk[i+j] != ' '; j++) {
                *buff_s = j;
            }
        }
        if (bf_wk[i] == '/' && bf_wk[i+1] == '*' ) {
            *cmnt_s = i;
            (*cmnt)++;
        }
        if (bf_wk[i] == '*' && bf_wk[i+1] == '/' ) {
            *cmnt_e = i;
            (*cmnt)--;
        }
        if (flag == OFF && bf_wk[i] == ' ') {
            (*indent)++;
        } else {
            flag = ON;
        }
    }
}

/*-------------------------------------------------------------------*/
/*  ＴＡＢサイズを解析した結果表示                                   */
/*-------------------------------------------------------------------*/
void  disp_tab(void)
{
    int i;
    char wk[16];

    if (o_vflag == 3 || o_vflag >= 10) {
        printf("\nコメント開始,終了位置のカラムに対する個数\n");
        printf("%-25s8         4          8         4\n", "[TAB STOP]");
        printf("%57s\n", "カラム         コメント開始         コメント終了");
        for (i = 0; i < BUFF_MAX-1; i++) {
            if (t8_cmnt_s_cnt[i] != 0 || t8_cmnt_e_cnt[i] != 0 ||
                t4_cmnt_s_cnt[i] != 0 || t4_cmnt_e_cnt[i] != 0) {
                if (((i+1) % 8) == 0) {
                    sprintf(wk, "TAB4,8");
                } else if (((i+1) % 4) == 0) {
                    sprintf(wk , "TAB4");
                } else {
                    strcpy(wk, "");
                }
                printf("%-10s[%4d]%10d%10d,%10d%10d\n",
                       wk, i+1, t8_cmnt_s_cnt[i], t4_cmnt_s_cnt[i],
                       t8_cmnt_e_cnt[i], t4_cmnt_e_cnt[i]);
            }
        }
        printf("\n");
    }
                                        /* ここで表示するものは今回の解析処理には使用していません */
    if (o_vflag == 4 || o_vflag >= 10) {
        printf("\n複数行にまたがるコメント開始,終了位置\n");
        printf("%-26s8   4         8   4\n", "[TAB STOP]");
        printf("%9cカラム数          個数          個数\n", ' ');
        for (i = 0; i < BUFF_MAX-1; i++) {
            if (((i+1) % 8) == 0) {
                sprintf(wk, "TAB4,8");
            } else if (((i+1) % 4) == 0) {
                sprintf(wk , "TAB4");
            } else {
                strcpy(wk, "");
            }
            if (t8_m_cnt_s[i] != 0 || t4_m_cnt_s[i] != 0 ||
                t8_m_cnt_e[i] != 0 || t4_m_cnt_e[i] != 0    ) {
                printf("%-10s[%5d]       %3d,%3d       %3d,%3d\n",
                       wk, i+1, t8_m_cnt_s[i], t4_m_cnt_s[i], t8_m_cnt_e[i], t4_m_cnt_e[i]);
            }
        }
        printf("\n");
    }

}

/*-------------------------------------------------------------------*/
/*  同一カラム最多行テーブル作成                                     */
/*-------------------------------------------------------------------*/
void  chk_tab_max(void)
{
    int   i;

    for (i = 1; i < BUFF_MAX-1; i++) {
        if (t8_cmnt_s_cnt[i] != 0) {
            if (t8_max_s[0] < t8_cmnt_s_cnt[i]) {
                t8_max_s[1] = t8_max_s[0];
                t8_max_s[0] = t8_cmnt_s_cnt[i];
            }
        }
        if (t4_cmnt_s_cnt[i] != 0) {
            if (t4_max_s[0] < t4_cmnt_s_cnt[i]) {
                t4_max_s[1] = t4_max_s[0];
                t4_max_s[0] = t4_cmnt_s_cnt[i];
            }
        }
        if (t8_cmnt_e_cnt[i] != 0) {
            if (t8_max_e[0] < t8_cmnt_e_cnt[i]) {
                t8_max_e[1] = t8_max_e[0];
                t8_max_e[0] = t8_cmnt_e_cnt[i];
            }
        }
        if (t4_cmnt_e_cnt[i] != 0) {
            if (t4_max_e[0] < t4_cmnt_e_cnt[i]) {
                t4_max_e[1] = t4_max_e[0];
                t4_max_e[0] = t4_cmnt_e_cnt[i];
            }
        }
    }
}

/*-------------------------------------------------------------------*/
/*  ＴＡＢサイズの割り出し                                           */
/*-------------------------------------------------------------------*/
void  calc_tab(void)
{
                                        /* 前後行インデント同一チェック */
    if (t8_same_line_c == t4_same_line_c) {
        answer1 = 0;
    } else if (t8_same_line_c > t4_same_line_c) {
        answer1 = 4;
    } else {
        answer1 = 8;
    }

                                        /* コメント開始終了位置チェック */
    if (t8_max_s[0] == t4_max_s[0]) {
        if (t8_max_s[1] == t4_max_s[1]) {
            if (t8_max_e[0] == t4_max_e[0]) {
                if (t8_max_e[1] == t4_max_e[1]) {
                    answer2 = 0;
                } else if (t8_max_e[1] > t4_max_e[1]) {
                    answer2 = 8;
                } else if (t8_max_e[1] < t4_max_e[1]) {
                    answer2 = 4;
                }
            } else if (t8_max_e[0] > t4_max_e[0]) {
                answer2 = 8;
            } else if (t8_max_e[0] < t4_max_e[0]) {
                answer2 = 4;
            }
        } else if (t8_max_s[1] > t4_max_s[1]) {
            answer2 = 8;
        } else if (t8_max_s[1] < t4_max_s[1]) {
            answer2 = 4;
        }
    } else if (t8_max_s[0] > t4_max_s[0]) {
        answer2 = 8;
    } else if (t8_max_s[0] < t4_max_s[0]) {
        answer2 = 4;
    }

    if (answer1 == 0) {
        chk_tab = answer2;
    } else {
        chk_tab = answer1;
    }

    if (o_vflag != 0) {
        printf("前後行インデント同一 チェック結果:%d\n", answer1);
        printf("コメント開始,終了位置チェック結果:%d\n", answer2);
    }
}

/*-------------------------------------------------------------------*/
/*  ＴＡＢサイズ数を返す                                             */
/*-------------------------------------------------------------------*/
void  tab_answer(void)
{
    if (o_tflag == 0) {
        if (tab_all == 0) {
            printf("ＴＡＢは未使用\n", answer1);
        } else if (chk_tab == 0) {
            printf("ＴＡＢサイズは分かりませんでした\n");
        } else {
            if (o_vflag == 0) {
                printf("ＴＡＢサイズは:%d\n", chk_tab);
            } else {
                printf("ＴＡＢサイズは%19c:%d\n", ' ', chk_tab);
            }
        }
    }

    if (o_bflag == ON) {
        if (tab_all == 0) {
            exit(0);
        } else if (chk_tab == 0) {
            exit(-1);
        } else {
            exit(chk_tab);
        }
    }

    if (o_tflag != 0) {
        rewind(fp);
        while (1) {
            if (fgets(bf_mst, BUFF_MAX-1, fp) == NULL) {
                break;
            }
            if (tab_all != 0) {
                if (o_tflag != 0) {
                    if (o_tflag == 100) {
                        tab_spc(bf_mst, bf_wk, chk_tab);
                    } else {
                        tab_spc(bf_mst, bf_wk, o_tflag);
                    }
                } else {
                    if (chk_tab == 4) {
                        tab_spc(bf_mst, bf_wk, 4);
                    } else if (chk_tab == 8) {
                        tab_spc(bf_mst, bf_wk, 8);
                    } else {
                        tab_spc(bf_mst, bf_wk, 4);
                    }
                }
            }
            printf("%s", bf_wk);
        }
    }
}

/*-------------------------------------------------------------------*/
/*  オプションのチェック                                             */
/*-------------------------------------------------------------------*/
int chk_opt(int ac, char *av[])
{
    int  o_cnt = 0, i;

    if (ac == 1) {
        err_msg();
        exit(99);
    }
    for (i = 1; i < ac; i++) {
        if (av[i][0] == '-' || av[i][0] == '/') {
            if (av[i][1] == '?') {
                printf("\nＣ言語のＴＡＢサイズを解析し変換を行う\n\n");
                err_msg();
                printf("\t-?          ヘルプ\n");
                printf("\t-b          ＴＡＢサイズをＥＲＲＯＲＬＥＶＥＬで返す\n");
                printf("\t-t[1-99]    ＴＡＢをＮ個のスペースに変換\n");
                printf("\t-ta         ＴＡＢサイズを解析しスペースに変換\n");
                printf("\t-v1         ＴＡＢサイズを解析した結果を表示\n");
                printf("\t-v2         コメント開始,終了位置を表示\n");
                printf("\t-v3         コメント開始,終了位置のカラムに対する個数を表示\n");
                printf("\t-v4         複数行にまたがるコメント開始,終了位置を表示\n");
                printf("\t-va         全てを表示(-v1, -v2, -v3, -v4)\n");
                printf("\t-r          ファイル名のカラー(緑色)表示をやめる\n");
                printf("\n");
                printf("\t<name>      ファイル名(-t,-bを指定時ワイルドカードの使用できません)\n");
                exit(99);
            } else if (av[i][1] == 'r' || av[i][1] == 'R') {
                o_rflag = ON;
            } else if (av[i][1] == 'v' || av[i][1] == 'V') {
                o_vflag = 1;
                if ((av[i][2] == 'a' || av[i][2] == 'A') && av[i][3] == '\0') {
                    o_vflag = 10;
                } else if (av[i][2] != (uchar)NULL) {
                    if (av[i][3] == (uchar)NULL) {
                        o_vflag = av[i][2] - '0';
                    } else {
                        err_msg();
                        exit(99);
                    }
                }
                if (o_vflag == 0 || (o_vflag >= 5 && o_vflag <= 9) || o_vflag > 10) {
                    err_msg();
                    exit(99);
                }
            } else if (av[i][1] == 't' || av[i][1] == 'T') {
                if ((av[i][2] == 'a' || av[i][2] == 'A') && av[i][3] == '\0') {
                    o_tflag = 100;
                } else if (av[i][2] != (uchar)NULL) {
                    o_tflag = atoi(&av[i][2]);
                    if (o_tflag == 0 || o_tflag >= 100) {
                        err_msg();
                        exit(99);
                    }
                }
                if (o_tflag == 0) {
                    o_tflag = 100;
                }
            } else if (av[i][1] == 'b' || av[i][1] == 'B') {
                o_bflag = ON;
            } else {
                err_msg();
                exit(99);
            }
            o_cnt++;
        }
    }

    if (o_tflag != OFF) {
        if (o_rflag == OFF && o_cnt >= 2) {
            err_msg();
            exit(99);
        } else if (ac >= o_rflag+4) {
            err_msg();
            exit(99);
        }
    } else if (o_bflag != OFF) {
        if (o_rflag == OFF && o_cnt >= 2) {
            err_msg();
            exit(99);
        } else if (ac >= o_rflag+4) {
            err_msg();
            exit(99);
        }
    } else if (o_vflag != OFF) {
        if (o_rflag == OFF && o_cnt >= 2) {
            err_msg();
            exit(99);
        }
    }
    return (o_cnt);

}

/*-------------------------------------------------------------------*/
/*  エラーメッセージ処理                                             */
/*-------------------------------------------------------------------*/
void  err_msg(void)
{
    printf("chktab (Ver. %s)  for MS-DOS%39cBy T.O6809\n" , VER, ' ');
    puts("Usage: chktab -? | [-b][-r] | [-t[1-99|a]][-r] | [-v[1-4|a][-r]] <name>");
}

/*-------------------------------------------------------------------*/
/*  TAB to SPACE                                                     */
/*-------------------------------------------------------------------*/
void  tab_spc(uchar in[], uchar out[], int tab_cnt)
{
    static int  i, j, k,
                t_s,                   /* Space -> Tab              */
                cnt;                   /* Tab Counter               */
    static char temp[1024];

    t_s = 0;
    for (i = 0, j = 0; in[i] != (char)NULL; i++, j++) {
        if (in[i] == '\t') {
            cnt = (t_s+i) % tab_cnt;
            for (k = 1; k <= tab_cnt-cnt; k++, j++, t_s++) {
                temp[j] = ' ';
            }
            j--;
            t_s--;
        } else {
            temp[j] = in[i];
        }
    }
    temp[j] = (char)NULL;
    strcpy(out, temp);
}
