

C言語でsyntaxhighlight.cを簡単な構文強調(シンタックスハイライト)をして表示したいと考えているのですが、下のコードを実行するとterminal1のようになってしまいます。一応色々調べてみて、gdbで原因を突き止めようとしましたが、できませんでした。

誰かこの原因を教えてください、お願いします。

  * syntaxhighlight.cは合計396行、警告やエラーはありません。
  * gccのバージョンは10.2.1
  * コンパイルコマンドは gcc -o "syntaxhighlight" "syntaxhighlight.c" -Wall -g -O0 です

syntaxhighlight.c

[code]

    #include <stdio.h>
    #include <string.h>
    #include <ctype.h>
    #include <stdlib.h>
    
    #define ERROR -1
    
    int c;
    char * keywords [] = {
      "int", "double", "short", "char"
    };
    
    enum {
      UNDEFINED, ALPHABET, NUMBER, SIGN, HASH, STRING, CALC_SIGN, SPACE
    };
    
    int include (const char * src, const char ** str)
    {
      while (str != NULL) {
        if (strcmp(src, *str) == 0)
          return 1; // 見つかった
        else
          str++;
      }
    
      return 0; // 見つからなかった
    }
    
    int alpha (const char * src)
    {
      if (include(src, (const char **)keywords) == 1) {
        printf("\x1b[1m\x1b[38;5;38m%s", src);
      } else {
        printf("\x1b[0m%s", src);
      }
    
      return strlen(src);
    }
    
    int number (const char * src)
    {
      printf("\x1b[94m%s", src);
    
      return strlen(src);
    }
    
    int string (const char * src)
    {
      printf("\x1b[33m%s", src);
    
      return strlen(src);
    }
    
    int hash (const char * src)
    {
      printf("\x1b[38;5;250m%s", src);
    
      return strlen(src);
    }
    
    int calc_sign (const char * src)
    {
      printf("\x1b[38;5;252m%s", src);
    
      return strlen(src);
    }
    
    int sign (const char * src)
    {
      printf("\x1b[38;5;248m%s", src);
    
      return strlen(src);
    }
    
    int space (const char * src)
    {
      while (*src) {
        if (*src == '\t') {
          printf("\x1b[0m%s", "  ");
          src++;
          continue;
        }
        printf("\x1b[0m%c", *src);
        src++
      }
    
      return strlen(src);
    }
    
    int type (int c)
    {
      if (isalpha(c)) {
        return ALPHABET;
      } else if ((c - '0') <= 9 && (c - '0') >= 0) {
        return NUMBER;
      } else if (strchr("&()^~|[]{},;:?", c) != 0) {
        return SIGN;
      } else if (c == "#") {
        return HASH;
      } else if (c == '"' || c == '\'') {
        return STRING;
      } else if (strchr("!%%-=+*/", c) != 0) {
        return CALC_SIGN; 
      } else {
        return SPACE;
      }
      return ERROR;
    }
    
    int main (void)
    {
      FILE * fp = fopen("syntaxhighlight.c", "r");
      char code[50] = "";
      int codenow = 0;
      int before = UNDEFINED;
    
      while ((c = fgetc(fp)) != EOF) {
        switch (type(c)) {
          case ALPHABET:
            if (before == ALPHABET) {
              code[codenow++] = c;
            } else {
              if (before != UNDEFINED) {
                switch (before) {
                  case NUMBER:
                  number(code);
                    break;
                  case STRING:
                    string(code);
                    break;
                  case HASH:
                    hash();
                    break;
                  case CALC_SIGN:
                    calc_sign();
                    break;
                  case SIGN:
                    sign();
                    break;
                  case SPACE:
                    space();
                    break;
                  default:
                    fprintf(stderr, "\n\x1b[91mERROR: %s: %d: before内の値が期待されるものでなかったため、プログラムを強制終了しました。\n\n", __func__, __LINE__);
                  exit(ERROR);
                }
                code[0] = '\0';
                codenow = 0;
                code[codenow] = c;
                before = ALPHABET;
              } else {
                before = ALPHABET;
                code[codenow++] = c;
              }
            }
            break;
          case CALC_SIGN:
            if (before == CALC_SIGN) {
              code[codenow++] = c;
            } else {
              if (before != UNDEFINED) {
                switch (before) {
                  case NUMBER:
                    number(code);
                    break;
                  case STRING:
                    string(code);
                    break;
                  case HASH:
                    hash();
                    break;
                  case ALPHABET:
                    alpha();
                    break;
                  case SIGN:
                    sign();
                    break;
                  case SPACE:
                    space();
                    break;
                  default:
                    fprintf(stderr, "\n\x1b[91mERROR: %s: %d: before内の値が期待されるものでなかったため、プログラムを強制終了しました。\n\n", __func__, __LINE__);
                    exit(ERROR);
                }
                code[0] = '\0';
                codenow = 0;
                code[codenow] = c; // 追加
                before = CALC_SIGN;
              } else {
                before = CALC_SIGN;
                code[codenow++] = c;
              }
            }
            break;
          case HASH:
            if (before == HASH) {
              code[codenow++] = c;
            } else {
              if (before != UNDEFINED) {
                switch (before) {
                  case NUMBER:
                    number(code);
                    break;
                  case STRING:
                    string(code);
                    break;
                  case ALPHABET:
                    alpha();
                    break;
                  case CALC_SIGN:
                    calc_sign();
                    break;
                  case SIGN:
                    sign();
                    break;
                  case SPACE:
                    space();
                    break;
                  default:
                    fprintf(stderr, "\n\x1b[91mERROR: %s: %d: before内の値が期待されるものでなかったため、プログラムを強制終了しました。\n\n", __func__, __LINE__);
                    exit(ERROR);
                }
                code[0] = '\0';
                codenow = 0;
                code[codenow] = c;
                before = ALPHABET;
              } else {
                before = ALPHABET;
                code[codenow++] = c;
              }
            }
            break;
          case NUMBER:
            if (before == NUMBER) {
              code[codenow++] = c;
            } else {
              if (before != UNDEFINED) {
                switch (before) {
                  case ALPHABET:
                    alpha(code);
                    break;
                  case STRING:
                    string(code);
                    break;
                  case HASH:
                    hash();
                    break;
                  case CALC_SIGN:
                    calc_sign();
                    break;
                  case SIGN:
                    sign();
                    break;
                  case SPACE:
                    space();
                    break;
                  default:
                    fprintf(stderr, "\n\x1b[91mERROR: %s: %d: before内の値が期待されるものでなかったため、プログラムを強制終了しました。\n\n", __func__, __LINE__);
                    exit(ERROR);
                }
                code[0] = '\0';
                codenow = 0;
                code[codenow] = c;
                before = NUMBER;
              } else {
                before = NUMBER;
                code[codenow++] = c;
              }
            }
            break;
          case SIGN:
            if (before == SIGN) {
              code[codenow++] = c;
            } else {
              if (before != UNDEFINED) {
                switch (before) {
                  case NUMBER:
                    number(code);
                    break;
                  case STRING:
                    string(code);
                    break;
                  case HASH:
                    hash();
                    break;
                  case CALC_SIGN:
                    calc_sign();
                    break;
                  case ALPHABET:
                    alpha();
                    break;
                  case SPACE:
                    space();
                    break;
                  default:
                    fprintf(stderr, "\n\x1b[91mERROR: %s: %d: before内の値が期待されるものでなかったため、プログラムを強制終了しました。\n\n", __func__, __LINE__);
                    exit(ERROR);
                }
                code[0] = '\0';
                codenow = 0;
                code[codenow] = c;
                before = SIGN;
              } else {
                before = SIGN;
                code[codenow++] = c;
              }
            }
            break;
          case SPACE:
            if (before == SPACE) {
              code[codenow++] = c;
            } else {
              if (before != UNDEFINED) {
                switch (before) {
                  case NUMBER:
                    number(code);
                    break;
                  case STRING:
                    string(code);
                    break;
                  case HASH:
                    hash();
                    break;
                  case CALC_SIGN:
                    calc_sign();
                    break;
                  case SIGN:
                    sign();
                    break;
                  case ALPHABET:
                    alpha();
                    break;
                  default:
                    fprintf(stderr, "\n\x1b[91mERROR: %s: %d: before内の値が期待されるものでなかったため、プログラムを強制終了しました。\n\n", __func__, __LINE__);
                    exit(ERROR);
                }
                code[0] = '\0';
                codenow = 0;
                code[codenow] = c;
                before = SPACE;
              } else {
                before = SPACE;
                code[codenow++] = c;
              }
            }
            break;
          case STRING:
            if (before == STRING) {
              code[codenow++] = c;
            } else {
              if (before != UNDEFINED) {
                switch (before) {
                  case NUMBER:
                    number(code);
                    break;
                  case ALPHABET:
                    alpha(code);
                    break;
                  case HASH:
                    hash();
                    break;
                  case CALC_SIGN:
                    calc_sign();
                    break;
                  case SIGN:
                    sign();
                    break;
                  case SPACE:
                    space();
                    break;
                  default:
                    fprintf(stderr, "\n\x1b[91mERROR: %s: %d: before内の値が期待されるものでなかったため、プログラムを強制終了しました。\n\n", __func__, __LINE__);
                    exit(ERROR);
                }
                code[0] = '\0';
                codenow = 0;
                code[codenow] = c;
                before = STRING;
              } else {
                before = STRING;
                code[codenow++] = c;
              }
            }
            break;
          default:
            fprintf(stderr, "\n\x1b[91mERROR: 問題が発生したため、プログラムを強制終了しました。\n");
            exit(ERROR);
        } // switch end
      } // while end
    
      fclose(fp);
    
      puts("end");
    
      return 0;
    } // main end
    
[/code]

terminal1

[code]

    pi@raspberrypi:~/C_Language $ ./systaxhighlight
    Segmentation fault
    pi@raspberrypi:~/C_Language $ gdb ./syntaxhighlight core
    GNU gdb (Raspbian 10.1-1.7) 10.1.90.20210103-git
    Copyright (C) 2021 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law
    Type "show copying" and "show warranty" for details.
    This GDB was configured as "arm-linux-gnueabihf".
    Type "show configuration" for configuration details.
    For bug reporting instuctions, please see:
    <https://www.gnu.org/software/gdb/bugs/>.
    Find the GDB manual and other documentation resources online at:
        <http://www.gnu.org/software/gdb/documentation/>.
    
    For help, type "help".
    Type "apropos word" to search for commands related to "word"...
    Reading symbols from ./syntaxhighlight...
    
    warning: exec file is newer than core file.
    [New LWP 1025]
    Core was generated by `./syntaxhighlight'
    Program terminated with signal SIGSEGV, Segmentation fault.
    #0 0x00010488 in ?? ()
    (gdb) bt
    #0 0x00010488 in ?? ()
    #1 0xb6ddf608 in ?? ()
    Backtrace stopped: previous frame identical to this frame (corrupt stack?)
    (gdb)
    
[/code]


CC BY-SA 4.0
2023-07-24T12:49:56.510
95746
2023-07-26T02:58:48.467
2023-07-25T11:56:46.347
56895
56895
question
c raspberry-pi gcc gdb
C言語のSegmentation Fault。gdbで原因調査しても終わっているところがわかりません。


実行ファイルにシンボルが含まれていないようなので、まずは `gcc -g -Wall -o syntaxhighlight
syntaxhighlight.c` でコンパイルが成功してから実行してみましょう。


CC BY-SA 4.0
2023-07-24T13:40:02.017
95747
2023-07-24T13:40:02.017
3475
95746
answer


コンパイルが正常終了する様にソースコードを修正して実行してみましたが、segmentation fault が発生する原因は `char *` 型配列
`keywords` に `NULL` 終端が無いことです。以下の修正で少なくとも segmentation fault
は発生しなくなりますが、その後の処理で `stack smashing` が発生します。

[code]

    const char * keywords [] = {
      "int", "double", "short", "char", NULL
    };
    
                  :
    
    int include (const char * src, const char ** str)
    {
      while (*str != NULL) {
        if (strcmp(src, *str) == 0)
          return 1; // 見つかった
        else
          str++;
      }
    
      return 0; // 見つからなかった
    }
    
    int alpha (const char * src)
    {
      if (include(src, keywords) == 1) {
    
    
[/code]


CC BY-SA 4.0
2023-07-24T15:15:45.833
95748
2023-07-24T15:15:45.833
47127
95746
answer


自己回答：

いくつかソースコードに問題点が見つかりました。

・関数の引数の不足  
switch内で使用しているhashやsign関数にわたす引数が少なかったので、hash(code)なのに修正しました。

・before変数に代入する値  
前の文字がどのタイプだったかを識別するbefore変数が正しく代入されていなかったので正しく代入されるように変更しました。

・比較する型が違う  
type関数内の c == "#" のところを c == '#' に修正しました。

・文字列を正しく空にできていない  
文字列を正しく空にするために、emptystr関数を追加しました。

・type関数内のif文の変更  
一番最後のSPACEが「どのifで判定された文字でもなかった場合すべて」となってしまうので else if (isspace(c))としました。

・codeのサイズ不足とcodenowのインデックスが範囲内に収まっているかの判定  
kunifさんの言う通り、codeのサイズ不足の解消とcodenowが範囲内に収まっているかの条件が必要だと思いましたので、コードを追加しました。

この問題点を変更・修正したことでSegmentation faultがなくなりました。

最終的なコードはこちらです。

[code]

    #include <stdio.h>
    #include <string.h>
    #include <ctype.h>
    #include <stdlib.h>
    #define ERROR -1
    
    int c;
    char * keywords [] = {
      "int", "double", "short", "char", NULL
    };
    
    typedef enum {
      UNDEFINED, ALPHABET, NUMBER, SIGN, HASH, STRING, CALC_SIGN, SPACE
    } before_value;
    
    before_value before = UNDEFINED;
    
    void emptystr (char * ary, int idx)
    {
      for (int i = 0; i < idx; i++) {
        ary[i] = '\0';
      }
    }
    
    int include (const char * src, const char ** str) 
    {
      while (str != NULL) {
        if (strcmp(src, *str) == 0)
          return 1; // 見つかった
        else
          str++;
      }
    
      return 0; // 見つからなかった
    }
    
    int alpha (const char * src)
    {
      if (include(src, (const char **)keywords) == 1) {
        printf("\x1b[1m\x1b[38;5;38m%s", src);
      } else {
        printf("\x1b[0m%s", src);
      }
    
      return strlen(src);
    }
    
    int number (const char * src)
    {
      printf("\x1b[94m%s", src);
    
      return strlen(src);
    }
    
    int string (const char * src)
    {
      printf("\x1b[33m%s", src);
    
      return strlen(src);
    }
    
    int hash (const char * src)
    {
      printf("\x1b[38;5;250m%s", src);
    
      return strlen(src);
    }
    
    int calc_sign (const char * src)
    {
      printf("\x1b[38;5;252m%s", src);
    
      return strlen(src);
    }
    
    int sign (const char * src)
    {
      printf("\x1b[38;5;248m%s", src);
    
      return strlen(src);
    }
    
    int space (const char * src)
    {
      while (*src) {
        if (*src == '\t') {
          printf("\x1b[0m%s", "  ");
          src++;
          continue;
        }
        printf("\x1b[0m%c", *src);
        src++;
      }
    
      return strlen(src);
    }
    
    int type (int c)
    {
      if (isalpha(c)) {
        return ALPHABET;
      } else if ((c - '0') <= 9 && (c - '0') >= 0) {
        return NUMBER;
      } else if (strchr("&()^~|[]{},;:?\\", c) != 0) {
        return SIGN;
      } else if (c == '#') {
        return HASH;
      } else if (c == '"' || c == '\'') {
        return STRING;
      } else if (strchr("!%%-=+*/", c) != 0) {
        return CALC_SIGN; 
      } else if (isspace(c)) {
        return SPACE;
      }
      return ERROR;
    }
    
    int main (void)
    {
      FILE * fp = fopen("syntaxhighlight.c", "r");
      char code[128] = "";
      int codenow = 0;
    
      while ((c = fgetc(fp)) != EOF) {
    
        if (codenow >= 128) {
          fprintf(stderr, "\x1b[91mERROR: codeに格納されている文字が限界に達したため終了しました。\n");
          return ERROR;
        }
        switch (type(c)) {
          case ALPHABET:
            if (before == ALPHABET) {
              code[codenow++] = c;
            } else {
              if (before != UNDEFINED) {
                switch (before) {
                  case NUMBER:
                  number(code);
                    break;
                  case STRING:
                    string(code);
                    break;
                  case HASH:
                    hash(code);
                    break;
                  case CALC_SIGN:
                    calc_sign(code);
                    break;
                  case SIGN:
                    sign(code);
                    break;
                  case SPACE:
                    space(code);
                    break;
                  default:
                    fprintf(stderr, "\n\x1b[91mERROR: %s: %d: before内の値が期待されるものでなかったため、プログラムを強制終了しました。\n\n", __func__, __LINE__);
                  return ERROR;
                }
                emptystr(code, 50);
                codenow = 0;
                code[codenow++] = c;
                before = ALPHABET;
              } else {
                before = ALPHABET;
                code[codenow++] = c;
              }
            }
            break;
          case CALC_SIGN:
            if (before == CALC_SIGN) {
              code[codenow++] = c;
            } else {
              if (before != UNDEFINED) {
                switch (before) {
                  case NUMBER:
                    number(code);
                    break;
                  case STRING:
                    string(code);
                    break;
                  case HASH:
                    hash(code);
                    break;
                  case ALPHABET:
                    space(code);
                    break;
                  case SIGN:
                    sign(code);
                    break;
                  case SPACE:
                    space(code);
                    break;
                  default:
                    fprintf(stderr, "\n\x1b[91mERROR: %s: %d: before内の値が期待されるものでなかったため、プログラムを強制終了しました。\n\n", __func__, __LINE__);
                    return ERROR;
                }
                emptystr(code, 50);
                codenow = 0;
                code[codenow++] = c; // 追加
                before = CALC_SIGN;
              } else {
                before = CALC_SIGN;
                code[codenow++] = c;
              }
            }
            break;
          case HASH:
            if (before == HASH) {
              code[codenow++] = c;
            } else {
              if (before != UNDEFINED) {
                switch (before) {
                  case NUMBER:
                    number(code);
                    break;
                  case STRING:
                    string(code);
                    break;
                  case ALPHABET:
                    space(code);
                    break;
                  case CALC_SIGN:
                    calc_sign(code);
                    break;
                  case SIGN:
                    sign(code);
                    break;
                  case SPACE:
                    space(code);
                    break;
                  default:
                    fprintf(stderr, "\n\x1b[91mERROR: %s: %d: before内の値が期待されるものでなかったため、プログラムを強制終了しました。\n\n", __func__, __LINE__);
                    return ERROR;
                }
                emptystr(code, 50);
                codenow = 0;
                code[codenow++] = c;
                before = HASH;
              } else {
                before = HASH;
                code[codenow++] = c;
              }
            }
            break;
          case NUMBER:
            if (before == NUMBER) {
              code[codenow++] = c;
            } else {
              if (before != UNDEFINED) {
                switch (before) {
                  case ALPHABET:
                    alpha(code);
                    break;
                  case STRING:
                    string(code);
                    break;
                  case HASH:
                    hash(code);
                    break;
                  case CALC_SIGN:
                    calc_sign(code);
                    break;
                  case SIGN:
                    sign(code);
                    break;
                  case SPACE:
                    space(code);
                    break;
                  default:
                    fprintf(stderr, "\n\x1b[91mERROR: %s: %d: before内の値が期待されるものでなかったため、プログラムを強制終了しました。\n\n", __func__, __LINE__);
                    return ERROR;
                }
                emptystr(code, 50);
                codenow = 0;
                code[codenow++] = c;
                before = NUMBER;
              } else {
                before = NUMBER;
                code[codenow++] = c;
              }
            }
            break;
          case SIGN:
            if (before == SIGN) {
              code[codenow++] = c;
            } else {
              if (before != UNDEFINED) {
                switch (before) {
                  case NUMBER:
                    number(code);
                    break;
                  case STRING:
                    string(code);
                    break;
                  case HASH:
                    hash(code);
                    break;
                  case CALC_SIGN:
                    calc_sign(code);
                    break;
                  case ALPHABET:
                    space(code);
                    break;
                  case SPACE:
                    space(code);
                    break;
                  default:
                    fprintf(stderr, "\n\x1b[91mERROR: %s: %d: before内の値が期待されるものでなかったため、プログラムを強制終了しました。\n\n", __func__, __LINE__);
                    return ERROR;
                }
                emptystr(code, 50);
                codenow = 0;
                code[codenow++] = c;
                before = SIGN;
              } else {
                before = SIGN;
                code[codenow++] = c;
              }
            }
            break;
          case SPACE:
            if (before == SPACE) {
              code[codenow++] = c;
            } else {
              if (before != UNDEFINED) {
                switch (before) {
                  case NUMBER:
                    number(code);
                    break;
                  case STRING:
                    string(code);
                    break;
                  case HASH:
                    hash(code);
                    break;
                  case CALC_SIGN:
                    calc_sign(code);
                    break;
                  case SIGN:
                    sign(code);
                    break;
                  case ALPHABET:
                    space(code);
                    break;
                  default:
                    fprintf(stderr, "\n\x1b[91mERROR: %s: %d: before内の値が期待されるものでなかったため、プログラムを強制終了しました。\n\n", __func__, __LINE__);
                    return ERROR;
                }
                emptystr(code, 50);
                codenow = 0;
                code[codenow++] = c;
                before = SPACE;
              } else {
                before = SPACE;
                code[codenow++] = c;
              }
            }
            break;
          case STRING:
            if (before == STRING) {
              code[codenow++] = c;
            } else {
              if (before != UNDEFINED) {
                switch (before) {
                  case NUMBER:
                    number(code);
                    break;
                  case ALPHABET:
                    alpha(code);
                    break;
                  case HASH:
                    hash(code);
                    break;
                  case CALC_SIGN:
                    calc_sign(code);
                    break;
                  case SIGN:
                    sign(code);
                    break;
                  case SPACE:
                    space(code);
                    break;
                  default:
                    fprintf(stderr, "\n\x1b[91mERROR: %s: %d: before内の値が期待されるものでなかったため、プログラムを強制終了しました。\n\n", __func__, __LINE__);
                    return ERROR;
                }
                emptystr(code, 50);
                codenow = 0;
                code[codenow++] = c;
                before = STRING;
              } else {
                before = STRING;
                code[codenow++] = c;
              }
            }
            break;
          default:
            fprintf(stderr, "\n\x1b[91mERROR: 問題が発生したため、プログラムを強制終了しました。before: %d\n", before);
            return ERROR;
        } // switch end
      } // while end
    
      fclose(fp);
    
      puts("end");
    
      return 0;
    } // main end
    
    
[/code]

これでSegmentation faultがなくなりましたが、挙動が思った通りになっていないのでまた別の質問として質問したいと思います。


CC BY-SA 4.0
2023-07-26T02:36:42.960
95757
2023-07-26T02:58:48.467
2023-07-26T02:58:48.467
56895
56895
95746
answer