#if !defined(SAPHIRE_INNER_H)
#define SAPHIRE_INNER_H

////////////////////////////////////////////////////////////
/// from saphire_main.c
////////////////////////////////////////////////////////////
extern volatile BOOL gSigUser;

/// 内部コマンドの定数と対応する文字列
enum eCommandKind { 
    kSubshell, kMSleep, kTrue, kFalse, kTest,
    kIndex, kRIndex, kLength, kLc, kUc,
    kChomp, kSubstr, kEval, kFg, kBg, kCpg,
    kJobs, kRehash, kKanjiCode, kLineField, kVar, kGlobal,
    kCalc, kExport, kPrint,  kPuts, kLoad, 
    kWhile, kBreak, kExit, kIf, kSplit, kX, kJoin, kSelect, kLines, kRows,
    kAryNew, kAryAdd, kAryErase, 
    kHashNew, kHashAdd, kHashErase, kMatch, kScan, kErase, kSub,
    kRead, 
    kCd, 
    kAdd, kDel, kSelector, kMax, kMin,
    kExtName, kParentName, kNoExtName, 
    kCompile, kRaise,
    kPopd, kPushd,
    kPlusPlus,
    kMinusMinus,
    kPlus,
    kMinus,
    kMult,
    kDiv,
    kMod,
    kPow,
    kSort,
    kRange,
    kPrintf,
    kPomch,
    kDef,
    kPTee,
    kPCat,
    kReturn,
    kMInitScr,
    kMEndWin,
    kMClear,
    kMClearImmediately,
    kMRefresh,
    kMMove ,
    kMMoveImmediately,
    kMPrintw, 
    kSweep,
    kShow,
    kYeild,
    kEach,
    kForeach,
//    kParse,
    kAbs,
    kFAbs,
    kP,
    kTry,
    kInnerCommand,
    kCommand 
};

extern string_obj* gAncestalMemoryPipeIn;
    // トップレベルの内部コマンド用のパイプ
extern string_obj* gAncestalMemoryPipeOut;
    // トップレベルの内部コマンド用のパイプ

extern char gTmpDir[PATH_MAX];
    // 一時ファイルの置き場

extern char* gStatmentKindStrs[kCommand+1];
extern hash_obj* gFuncs;
    // ユーザー関数 sFunction*が入っている
    // キーは関数名

extern vector_obj* gStackFrame;
    // ローカル変数のスタック
    // 内容はhash_obj*
    // これのキーはローカル変数名
    // 内容はstring_obj*
extern enum eAppType gAppType;
    // アプリケーションの種類
extern BOOL gJobControl;
    // ジョブのコントロールをするかどうか
extern vector_obj* gJobs;
    // ジョブ sJob*の配列

extern char* gMagicNumber;
    // オブジェクトファイルのマジックナンバー

extern hash_obj* gReadBuffers;
    // キー ファイル名
    // 内容 sRFd*
    // read用のファイルのリードバッファー

extern vector_obj* gGlobalPipes;
    // スタッカブルグローバルパイプの読み込みんだデータ

extern vector_obj* gQGlobalPipes;
    // キューグローバルパイプの読み込みんだデータ

extern string_obj* gGlobalPipe;
    // グローバルパイプの読み込みんだデータ

extern string_obj* gGlobalPipeNum[10];
    // ナンバーグローバルパイプの読み込みんだデータ
extern BOOL gSaphireBackground;
    // Saphireがバックグラウンドで動いているかどうか

extern hash_obj* gRegexs;
    // 正規表現のキャッシュ
    // キーは正規表現の文字列
    // 内容はregex_t

extern hash_obj* gRegexsI;
    // 正規表現のキャッシュ(ignore case)
    // キーは正規表現の文字列
    // 内容はregex_t

extern hash_obj* gRegexsM;
    // 正規表現のキャッシュ(multi line)
    // キーは正規表現の文字列
    // 内容はregex_t

extern hash_obj* gRegexsIM;
    // 正規表現のキャッシュ(ignore case|multi line)
    // キーは正規表現の文字列
    // 内容はregex_t

extern hash_obj* gFReadBuffers;
    // 読み込みバッファ
    // キーはファイルディスクリプタ番号の文字列
    // 内容は読み込んだファイルの内容
    // statment_tree_internal_commands_read_nextin_oneline
    // で使われる
extern hash_obj* gFWriteBuffers;
    // 書き込みバッファ
    // キーはファイルディスクリプタ番号の文字列
    // 内容は書き込んだファイルの内容

extern hash_obj* gInnerCommands;
    // 外部登録の内部コマンド
    // saphireを組み込んだアプリケーションで
    // 定義された内部コマンドのデータ
    // キーはコマンド名
    // 内容はsInnerCommand

extern vector_obj* gDirStack;
    // popd, pushdで使われるディレクトリスタック
    // 内容は string_obj*
extern enum eLineField gLineField;
    // saphireが文字列処理の内部コマンドで使う改行コード

extern vector_obj* gPSubTmpFiles;
    // プロセス置換で使われる一時ファイルを行末で消すために
    // 保存しておく配列

BOOL is_line_field(char* p);
    // 引数がラインフィールドをポイントしているか

BOOL is_line_field2(enum eLineField lf, char* p);
    // 引数がラインフィールドをポイントしているか2

BOOL string_chomp(string_obj* str);
    // 改行コードを取り除く

void string_ponch(string_obj* str);
    // 改行コードを付ける(\nに統一)

void string_pomch(string_obj* str, enum eLineField lf);
    // 改行コードを付ける(改行コードそのまま)

BOOL xclose(int fd);
    // closeするときにそのファイルディスクリプタに
    // gFReadBuffersのバッファがあれば削除
    // gFWriteBuffersのバッファがあれば書き込んで削除

void update_ary_env(char* key);
    // 配列変数にまつわる変数(_size)を正しい値に更新

void clear_ary_env(char* key);
    // 配列変数にまつわる変数を消す

void saphire_delete_same_name_var(char* name);
    // 同名のグローバル系の変数を全て消す

int get_command_kind(char* arg0);
    // コマンド名からコマンドIDを探す

void null_fun();
    // gotoのラベルをコンパイラーに通すための関数

BOOL parse_is_redirect(char* p);
    // ポインターが差す文字列がリダイレクトかどうか

BOOL parse_is_redirect2(char* p);
    // ポインタが差す文字列がリダイレクトかどうか2

BOOL is_env_name_char(char c);
    // 変数で使える文字かどうか

BOOL is_glob_terminated_char(char* p);
    // グロブの区切り文字かどうか

BOOL is_terminated_char(char* p);
    // 

void get_quoted_linefield(char* str, string_obj* out);

typedef struct _sJob {
    string_obj* mName;          // ジョブの名前
    pid_t mPGroup;              // プロセスグループID
    vector_obj* mPIDs;          // 子プロセスのPIDのリスト
    struct termios* mTty;
} sJob;

sJob* sJob_new();
void sJob_delete(sJob* self);

enum eStatmentTerminated { kTNormal, kTOrOr, kTAndAnd };

typedef struct
{
    sStatments* joukensiki;
    sStatments* contents;
} sWhile;

sWhile* sWhile_new(MANAGED sStatments* joukensiki, MANAGED sStatments* contents);
sWhile* sWhile_new2(sWhile* while_);

void sWhile_delete(sWhile* self);
void sWhile_save(sWhile* self, int fd);
sWhile* sWhile_load(int fd);

typedef struct
{
    sStatments* loops;
    sStatments* loops2;
} sTry;

sTry* sTry_new(MANAGED sStatments* loops, MANAGED sStatments* loops2);
sTry* sTry_new2(sTry* try_);
void sTry_view(sTry* self);
void sTry_delete(sTry* self);
void sTry_save(sTry* self, int fd);
sTry* sTry_load(int fd);

typedef struct
{
    sStatments* contents;
} sSubshell;

sSubshell* sSubshell_new(MANAGED sStatments* contents);
sSubshell* sSubshell_new2(sSubshell* subshell);

void sSubshell_delete(sSubshell* self);
void sSubshell_save(sSubshell* self, int fd);
sSubshell* sSubshell_load(int fd);
void sSubshell_view(sSubshell* self);

typedef struct
{
    vector_obj* joukensiki_list;
    vector_obj* contents_list;
} sIf;

sIf* sIf_new(MANAGED vector_obj* joukensiki_list, MANAGED vector_obj* contents_list);
sIf* sIf_new2(sIf* if_);

void sIf_delete(sIf* self);
void sIf_save(sIf* self, int fd);
sIf* sIf_load(int fd);

enum eCommandKind2 {
    kCKLocalVar, kCKUserCommand, kCKExternInnerCommand, kCKInnerCommand,
    kCKExternCommand
};

enum eRedirect { kRedirectInput, kRedirectOverwrite, kRedirectAppend
          , kRedirectErrAndOutput };

typedef struct {
    enum eRedirect mType;
    int mFd;
    string_obj* mFName;
} sRedirect; // 実行時に使う方 オブジェクトファイルに保存される

sRedirect* sRedirect_new(enum eRedirect type, int fd, char* fname);
sRedirect* sRedirect_new2(sRedirect* redirect);
void sRedirect_save(sRedirect* self, int fd);
sRedirect* sRedirect_load(int fd);
void sRedirect_delete(sRedirect* self);

typedef struct {
    enum eRedirect mType;
    int mFd;
} sRedirectTmp; // パーサーで一時的に使う

sRedirectTmp* sRedirectTmp_new(enum eRedirect type, int fd);
void sRedirectTmp_delete(sRedirectTmp* self);

typedef struct {
    string_obj* mName;

    string_obj* mIndex;
    string_obj* mIndex2;
    string_obj* mDelimiter;
} sVar;

sVar* sVar_new(MANAGED string_obj* name,  MANAGED string_obj* index, MANAGED string_obj* index2, MANAGED string_obj* delimiter);
sVar* sVar_new2(sVar* var);
void sVar_delete(sVar* self);

sVar* sVar_load(int fd);
void sVar_save(sVar* self, int fd);

sBlock* sBlock_new(sStatments* statments, vector_obj* args);
sBlock* sBlock_new2(sBlock* block);
void sBlock_delete(sBlock* self);
void sBlock_save(sBlock* self, int fd);
sBlock* sBlock_load(int fd);

typedef struct
{
    int mKind;   // 0: string 1: var 2: redirect 3: statments 4:var2 5: statments2 6: block
    void* mBody;
} sArgTmp; // パーサーで一時的に使う

sArgTmp* sArgTmp_new(int kind, void* body);
void sArgTmp_delete(sArgTmp* arg);

typedef struct
{
    int mKind; // 0:string 1: var 2: statments 3:var2 4: statments2 5: block
    void* mBody;
} sArg; // mArgsの中身

sArg* sArg_new(int kind, void* body);
sArg* sArg_new2(sArg* arg);
void sArg_delete(sArg* arg);

void sArg_save(sArg* self, int fd);
sArg* sArg_load(int fd);

typedef struct
{
    enum eCommandKind mKind;
    vector_obj* mArgs;
        // vector of sArg*
        // コマンドライン引数
    vector_obj* mRedirects;
        // vector of sRedirect* 
        // リダイレクションのリスト

    void* mExtra;

    vector_obj* mArgTmps;
        // vector of sArgTmp*
        // パーサー用に一時的に使う変数
} sCommand;

#ifdef MDEBUG 
sCommand* sCommand_new(const char* file, int line, const char* function);
#define COMMAND_NEW() sCommand_new(__FILE__, __LINE__, __FUNCTION__)
void sCommand_view(sCommand* self);
#else
sCommand* sCommand_new();
#define COMMAND_NEW() sCommand_new()
#endif

void sCommand_save(sCommand* self, int fd);
sCommand* sCommand_load(int fd);
void sCommand_delete(sCommand* self);

typedef struct
{
    vector_obj* mCommands;   // コマンド

    string_obj* mFName;      // 文があるファイル名
    int mLine;               // 文がある行

    string_obj* mNotEvaled;  // run時にパースされる文
                             // 環境変数とマクロを含む文
    string_obj* mTitle;      // タイトル

    enum eStatmentTerminated mTerminated;   
            // 複文の最後が何で区切られているか

    BOOL mRCodeReverse;      // リターンコードを反転する

    BOOL mBackground;        // バックグラウンドで実行

    int mGlobalPipeIn;   // グローバルパイプの入力があるか 1: 普通 2: スタッカブル 3:キュー 4: ナンバー
    int mGlobalPipeOut;  // グローバルパイプの出力があるか 1: 普通 2: スタッカブル 3:キュー 4:ナンバー
    int mGlobalPipeInNum; // ナンバーグローバルパイプの番号
    int mGlobalPipeOutNum; // ナンバーグローバルパイプの番号
} sStatment;

#ifdef MDEBUG

sStatment* sStatment_new(const char* file, int line, const char* function);
#define STATMENT_NEW() sStatment_new(__FILE__, __LINE__, __FUNCTION__)
void sStatment_view(sStatment* self);

#else

sStatment* sStatment_new();
#define STATMENT_NEW() sStatment_new()

#endif

sStatment* sStatment_new2(sStatment* statment);

void sStatment_delete(sStatment* self);
void sStatment_save(sStatment* self, int fd);
sStatment* sStatment_load(int fd);

#ifdef MDEBUG
sStatments* sStatments_new(const char* file, int line, const char* function);

#define STATMENTS_NEW() sStatments_new(__FILE__, __LINE__, __FUNCTION__)
#else

sStatments* sStatments_new();

#define STATMENTS_NEW() sStatments_new()

#endif

void sStatments_view(sStatments* self);

void sStatments_save(sStatments* self, int fd);
sStatments* sStatments_load(int fd);
void sStatments_delete(sStatments* self);

typedef struct {
    string_obj* name;       // コマンド名
    string_obj* arg_name;
    sStatments* statments; // 命令表
} sFunction;

sFunction* sFunction_new(char* name, MANAGED sStatments* statments, char* arg_name);
sFunction* sFunction_new2(sFunction* func);
  
void sFunction_delete(sFunction* self);
void sFunction_save(sFunction* self, int fd);
sFunction* sFunction_load(int fd);
void sFunction_view(sFunction* self);

typedef struct
{
    int mPid;
    int mStatus;
    int mPGroup;
} sStrayCProg;

extern vector_obj* gStrayCProgs;

sStrayCProg* sStrayCprog_new(int pid, int status, int pgroup);
void sStrayCProg_delete(sStrayCProg* self);

typedef struct {
    pid_t mPid;
    string_obj* mTitle;
    BOOL mWriterProcess;
} sChildProgram;

sChildProgram* sChildProgram_new(pid_t pid, char* title, BOOL writer_process);
void sChildProgram_delete(sChildProgram* self);

typedef struct {
    string_obj* mName;
    fInnerCommand mFun;
} sInnerCommand;

sInnerCommand* sInnerCommand_new(char* name, fInnerCommand fun);
void sInnerCommand_delete(sInnerCommand* self);

void sigchld_block(int block);

////////////////////////////////////////////////////////////
/// from saphire_commands.c
////////////////////////////////////////////////////////////
int statment_tree_internal_commands(sCommand* command, int* rcode, vector_obj* argv, vector_obj* blocks, vector_obj* parent_blocks, sWFd* nextout, sRFd* nextin, int nexterr, int j, vector_obj* cprogs, char* title, BOOL input, BOOL* return_, char* sname, int line);
BOOL vcat_expand_env(string_obj* pipeout, char* env, vector_obj* numbers,char* sname, int line);

////////////////////////////////////////////////////////////
/// from saphire_parser.c
////////////////////////////////////////////////////////////
BOOL expand_env(char* p, string_obj* out, sRFd* rfd, char* sname, int* line);
BOOL parse(char* cmdline, char* sname, int* line, sStatments* statments);
BOOL read_expand_brace_expansion(char* p, string_obj* cmdline2, char* separator, char* sname, int* line);
BOOL parse_number(char* p, vector_obj* numbers, char* sname, int line);

typedef struct {
    int number;
    int number2;
    int number_num;
} sIndex;

sIndex* sIndex_new(int num, int num2, int num_num);
void sIndex_delete(sIndex* self);

BOOL read_until_next_cbrace_end(char** p, string_obj* not_evaled, int* read_end_of_statment, BOOL line_inc, char* sname, int* line);

////////////////////////////////////////////////////////////
// from saphire_vm.c
////////////////////////////////////////////////////////////
BOOL forground_job(int num);
void background_job(int num);
void tmpfile_all_sweep();
void tmpfile_sweep();

extern vector_obj* gStackTraceFName;
extern vector_obj* gStackTraceLineNum;
    // スタックフレームの記録

int run(sStatments* statments, char* title, sWFd* pipeout, sRFd* pipein, int pipeerr, BOOL finaly_exec, BOOL in_loop, BOOL* return_, vector_obj* blocks, BOOL try_);
    // saphire_compile2でコンパイルしたコンパイル結果を実行する。
    // verboseは実行する前に実行するコマンド名を表示するかどうか

#endif
