/*	
 *	clipxls.c
 *
 *	ユーティリティ：エクセル
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2016 Naoyuki Sawa
 *
 *	* Tue Jun 28 21:02:01 JST 2016 Naoyuki Sawa
 *	- 1st リリース。
 *	  エクセルデータを直接操作する時に利用する関数を、当モジュールに実装する事にしました。
 *	  2016/06/28現在の所、strtoref()のみです。
 *	  実際にこれを使用するツールやアプリケーションは、まだ作成していません。
 *	  これからツールやアプリケーションを作成しながら、動作検証する予定です。
 *	* Wed Jun 29 21:00:15 JST 2016 Naoyuki Sawa
 *	- strtoref()の、パス名,ブック名,シート名のパース処理を、シフトJIS対応にしました。
 *	  そのために、strtoref_parse1()内で文字を取得する処理を、mbsnextc(),mbsinc()を使うように変更しました。
 *	- strtoref()が判定ている特殊文字は、「'」「!」「[」「]」が有りますが、シフトJISの2バイト目に一致するのは「[」「]」だけなので、「'」「!」は考慮不要です。
 *	  「[」「]」は、パス名,ブック名,シート名のパース(strtoref_parse1())と、R1C1形式のパース(strtoref_parse2())で判定していますが、
 *	  シフトJIS文字が現れる可能性が有るのは前者(strtoref_parse1())だけなので、後者(strtoref_parse2())ではシフトJIS対応は不要です。
 *	  従って、今回変更した部分は、strtoref_parse1()だけです。
 *	- これまでは、パス名,ブック名,シート名にシフトJIS文字が含まれていると、間違った結果になる事が有りましたが、今回の変更によって、正しい結果になりました。
 *	  動作確認結果は、以下の通りです。
 *	  □テストプログラム
 *	  │void test() {
 *	  │  const char ref[]="格納回転イロハ!A1";	//="\x8A\x69\x94\x5B\x89\xF1\x93\x5D\x83\x43\x83\x8D\x83\x6E\x21\x41\x31"
 *	  │  char *book, *sheet;
 *	  │  int flag, col, row;
 *	  │  flag = strtoref(ref, NULL, &book, &sheet, &row, &col);
 *	  │  printf("%-40s %06b %'8d %'8d %-24s %-24s\n", ref, flag, row, col, book ? book : "(NULL)", sheet ? sheet : "(NULL)");
 *	  │  free(book);
 *	  │  free(sheet);
 *	  │}
 *	  ×結果(修正前)
 *	  │格納回転イロハ!A1  101001  1  1  格\x94回\x93  イロハ	「納」の2バイト目が「[」，「転」の2バイト目が「]」に偶然一致して、"格\x94[回\x93]イロハ!A1"と見なされてしまっている。
 *	  ○結果(修正後)
 *	  │格納回転イロハ!A1  101001  1  1  (NULL)        格納回転イロハ
 *	* Sat Jul 02 21:34:21 JST 2016 Naoyuki Sawa
 *	- reftostr()を追加しました。
 */
#include "clip.h"
/****************************************************************************
 *	グローバル関数
 ****************************************************************************/
static int strtoref_parse1(const char* ptr, char** _book, char** _sheet);		//前方参照
static int strtoref_parse2(const char* _ptr, char** _endptr, int* _row, int* _col);	//前方参照
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□当関数が受け付ける、エクセルの参照表現は、以下の通りです。
//│セル参照	─	①	セル名
//│シート参照	┬	②	シート名!セル名
//│		└	③	'シート名'!セル名			シート名に空白や記号が含まれる場合は、この形式になるようです。
//│ブック参照	┬	④	[ブック名]シート名!セル名
//│		│	⑤	'[ブック名]シート名'!セル名		ブック名,又は,シート名に空白や記号が含まれる場合は、この形式になるようです。
//│		└	⑥	'パス名[ブック名]シート名'!セル名	パス名が含まれる場合は、空白や記号の有無に関係無く、この形式になるようです。
int strtoref(const char* _ptr, char** _endptr/*NULL可*/, char** _book/*NULL可*/, char** _sheet/*NULL可*/, int* _row/*NULL可*/, int* _col/*NULL可*/) {
	const char* ptr = _ptr;
	char *book = NULL, *sheet = NULL;
	GString* s = g_string_new(NULL);
	int flag, row, col, c;
	//先頭の空白を読み飛ばす。
	while(isspace((c = *ptr++))) { /** no job **/ }
	//空白以外の最初の文字が「'」ならば…						③⑤⑥
	if(c == '\'') {
		//「'」～「'」の間の文字列を取得する。
		for(;;) {
			c = *ptr++;
			if(!c) { goto L_ERR; }		//閉じる「'」が無い。
			if(c == '\'') {			//「'」を見つけたら…
				if(*ptr == '\'') {
					ptr++;		//「''」は「'」と見なして継続する。
				} else {
					break;		//単独の「'」ならばループを抜ける。
				}
			}
			g_string_append_c(s, c);
		}
		//閉じる「'」の次の文字を取得する。
		c = *ptr++;
		if(c != '!') { goto L_ERR; }		//閉じる「'」の次の文字は「!」でなくてはならない。
		//ここで、ptrは「!」の次の文字(=セル名)を指している。
	//空白以外の最初の文字が「'」以外ならば…					①②④
	} else {
		const char* save_ptr = ptr - 1/*c*/;
		//「!」が有ると仮定して、～「!」までの文字列を取得する。
		for(;;) {
			if(!c || (c == '!')) { break; }
			g_string_append_c(s, c);
			c = *ptr++;
		}
		//「!」が無ければ、取得した文字列をクリアし、ポインタを元に戻す。	①
		if(!c) {
			g_string_truncate(s, 0);
			ptr = save_ptr;
		}
		//ここで、ptrは「!」の次の文字(=セル名),又は,空白以外の最初の文字(=セル名)を指している。
	}
	//「!」の前の文字列を解析する。
	if(!strtoref_parse1(s->str, &book, &sheet)) { goto L_ERR; }
	//「!」の後の文字列を解析する。
	if(!(flag = strtoref_parse2(ptr, (char**)&ptr, &row, &col))) { goto L_ERR; }
	//結果を格納する。
	if(_endptr) {					//変換に使われた最終文字の次の文字へのポインタを格納する変数が指定されていたら…
		*_endptr = (char*)ptr;			//変換に使われた最終文字の次の文字へのポインタを格納する。
	}
	if(_book) {					//ブック名を格納する変数が指定されていたら…
		*_book = book;				//ブック名を格納する。
	} else {					//ブック名を格納する変数が指定されていなければ…
		free(book);				//ブック名の文字列が確保されていたら解放する。
	}
	if(_sheet) {					//シート名を格納する変数が指定されていたら…
		*_sheet = sheet;			//シート名を格納する。
	} else {					//シート名を格納する変数が指定されていなければ…
		free(sheet);				//シート名の文字列が確保されていたら解放する。
	}
	if(_row) {					//行を格納する変数が指定されていたら…
		*_row = row;				//行を格納する。
	}
	if(_col) {					//列を格納する変数が指定されていたら…
		*_col = col;				//列を格納する。
	}
	g_string_free(s, 1/*free_segment*/);		//作業用の文字列を解放する。
	return flag;					//フラグ(!=0)を返す。
L_ERR:
	if(_endptr) {					//変換に使われた最終文字の次の文字へのポインタを格納する変数が指定されていたら…
		*_endptr = (char*)_ptr;			//引数_ptrの元の値を格納する。(strtol()等と同じ動作)
	}
	if(_book) {					//ブック名を格納する変数が指定されていたら…
		*_book = NULL;				//NULLポインタを格納する。(安全のため。必須ではない。)
	}
	free(book);					//ブック名の文字列が確保されていたら解放する。
	if(_sheet) {					//シート名を格納する変数が指定されていたら…
		*_sheet = NULL;				//NULLポインタを格納する。(安全のため。必須ではない。)
	}
	free(sheet);					//シート名の文字列が確保されていたら解放する。
	if(_row) {					//行を格納する変数が指定されていたら…
		*_row = 0;				//0を格納する。(安全のため。必須ではない。)
	}
	if(_col) {					//列を格納する変数が指定されていたら…
		*_col = 0;				//0を格納する。(安全のため。必須ではない。)
	}
	g_string_free(s, 1/*free_segment*/);		//作業用の文字列を解放する。
	return 0;					//エラー(0)を返す。
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int strtoref_parse1(const char* ptr, char** _book, char** _sheet) {
	int success = 0, c;
	GString* s = g_string_new(NULL);
	//│シート名
	//│[ブック名]シート名
	//│パス名[ブック名]シート名
	//「[」が有ると仮定して、～「[」までの文字列を取得する。
	for(;;) {
//{{2016/06/29変更:strtoref()の、パス名,ブック名,シート名のパース処理を、シフトJIS対応にしました。
//		c = *ptr++;
//↓2016/06/29変更:strtoref()の、パス名,ブック名,シート名のパース処理を、シフトJIS対応にしました。
		c = mbsnextc(ptr);
		ptr = mbsinc(ptr);
//}}2016/06/29変更:strtoref()の、パス名,ブック名,シート名のパース処理を、シフトJIS対応にしました。
		if(!c) {				//「[」が無かった場合は…
			if(s->len) {			//一文字以上取得していたら、それをシート名として格納する。
				*_sheet = strdup(s->str);
				if(!*_sheet) { DIE(); }	//メモリ不足
			}
			goto L_RET;			//ここまで
		}
		if(c == '[') { break; }			//「[」が有ったらループを抜けて、「[」の前のパス名(又は空文字)の続きにシート名を取得する。
//{{2016/06/29追加:strtoref()の、パス名,ブック名,シート名のパース処理を、シフトJIS対応にしました。
		if(c > UCHAR_MAX) { g_string_append_c(s, c >> 8); }
//}}2016/06/29追加:strtoref()の、パス名,ブック名,シート名のパース処理を、シフトJIS対応にしました。
		g_string_append_c(s, c);		//パス名(又はシート名)に文字を追加する。
	}
	//「[」の前のパス名(又は空文字)の続きに、「]」までの文字列を追加する。
	if(!*ptr) { goto L_ERR; }			//「[]」はエラー。
	for(;;) {
//{{2016/06/29変更:strtoref()の、パス名,ブック名,シート名のパース処理を、シフトJIS対応にしました。
//		c = *ptr++;
//↓2016/06/29変更:strtoref()の、パス名,ブック名,シート名のパース処理を、シフトJIS対応にしました。
		c = mbsnextc(ptr);
		ptr = mbsinc(ptr);
//}}2016/06/29変更:strtoref()の、パス名,ブック名,シート名のパース処理を、シフトJIS対応にしました。
		if(!c) { goto L_ERR; }			//「]」が無ければエラー。
		if(c == ']') { break; }			//「]」が有ったらループを抜けて、ブック名を確定する。
//{{2016/06/29追加:strtoref()の、パス名,ブック名,シート名のパース処理を、シフトJIS対応にしました。
		if(c > UCHAR_MAX) { g_string_append_c(s, c >> 8); }
//}}2016/06/29追加:strtoref()の、パス名,ブック名,シート名のパース処理を、シフトJIS対応にしました。
		g_string_append_c(s, c);		//パス名にブック名の文字を追加する。
	}
	if(s->len) {					//一文字以上取得していたら、それをブック名として格納する。
		*_book = strdup(s->str);
		if(!*_book) { DIE(); }			//メモリ不足
	}
	//「]」の後の文字列が、シート名である。
	if(!*ptr) { goto L_ERR; }			//「]」の後に文字が無ければエラー。
	*_sheet = strdup(ptr);
	if(!*_sheet) { DIE(); }				//メモリ不足
L_RET:
	success = 1;
L_ERR:
	g_string_free(s, 1/*free_segment*/);		//作業用の文字列を解放する。
	return success;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int strtoref_parse2(const char* _ptr, char** _endptr, int* _row, int* _col) {
	const char* ptr;
	char* endptr;
	int flag, row, col;
	//まず、R1C1形式と仮定してパースしてみる。
	//│RC			行相対		列相対
	//│R数値C		行絶対		列相対
	//│RC数値		行相対		列絶対
	//│R数値C数値		行絶対		列絶対
	//│R[数値]C		行相対		列相対
	//│RC[数値]		行相対		列相対
	//│R[数値]C[数値]	行相対		列相対
	flag = XlsRef_Style_R1C1;
	ptr = _ptr;
	if(toupper(*ptr++) != 'R') { goto L_A1; }
	if(*ptr == '[') {
		ptr++;	//'['
		row = strtol(ptr, &endptr, 0);		//マイナス可
		if((endptr == ptr) ||			//数値が無ければエラー。
		  (*endptr != ']')) { goto L_A1; }	//閉じる「]」が無ければエラー。
		ptr = endptr;
		ptr++;	//']'
		flag |= XlsRef_RowType_Rel;
	} else {
		row = strtoul(ptr, &endptr, 0);		//マイナス不可			⇒残念ながらstrtoul()は'-'を許容してしまうのでこのではエラー判定出来ないが…
		if(endptr == ptr) {
			flag |= XlsRef_RowType_Rel;	//Rの後に数値が無ければ行相対。変換出来なかった場合strtoul()の戻り値は0なので、既に(row=0)になっているはず。
		} else {
			flag |= XlsRef_RowType_Abs;	//Rの後に数値が有ったら行絶対。
		}
		ptr = endptr;
	}
	if(toupper(*ptr++) != 'C') { goto L_A1; }
	if(*ptr == '[') {
		ptr++;	//'['
		col = strtol(ptr, &endptr, 0);		//マイナス可
		if((endptr == ptr) ||			//数値が無ければエラー。
		  (*endptr != ']')) { goto L_A1; }	//閉じる「]」が無ければエラー。
		ptr = endptr;
		ptr++;	//']'
		flag |= XlsRef_ColType_Rel;
	} else {
		col = strtoul(ptr, &endptr, 0);		//マイナス不可			⇒残念ながらstrtoul()は'-'を許容してしまうのでこのではエラー判定出来ないが…
		if(endptr == ptr) {
			flag |= XlsRef_ColType_Rel;	//Cの後に数値が無ければ行相対。変換出来なかった場合strtoul()の戻り値は0なので、既に(col=0)になっているはず。
		} else {
			flag |= XlsRef_ColType_Abs;	//Cの後に数値が有ったら行絶対。
		}
		ptr = endptr;
	}
	goto L_RET;
L_A1:	//R1C1形式と仮定してパース出来なければ、A1形式と仮定してパースしてみる。
	//│ABC数値		行相対		列相対
	//│$ABC数値		行相対		列絶対
	//│ABC$数値		行絶対		列相対
	//│$ABC$数値		行絶対		列絶対
	flag = XlsRef_Style_A1;
	ptr = _ptr;
	if(*ptr == '$') {
		ptr++;	//'$'
		flag |= XlsRef_ColType_Abs;
	} else {
		flag |= XlsRef_ColType_Rel;
	}
	if(!isalpha(*ptr)) { return 0; }		//ABCが無ければエラー。
	col = 0;
	for(;;) {
		col += (toupper(*ptr++) - 'A' + 1);	//┐
		if(!isalpha(*ptr)) { break; }		//├各桁の値は1～26で、N桁目の重みは26^(N-1)という、変則的な計算です。
		col *= ('Z' - 'A' + 1);			//┘
	}
	if(*ptr == '$') {
		ptr++;	//'$'
		flag |= XlsRef_RowType_Abs;
	} else {
		flag |= XlsRef_RowType_Rel;
	}
	row = strtoul(ptr, &endptr, 0);			//マイナス不可			⇒残念ながらstrtoul()は'-'を許容してしまうのでこのではエラー判定出来ないが…
	if(endptr == ptr) { return 0; }			//数値が無ければエラー。
	ptr = endptr;
L_RET:
	*_endptr = (char*)ptr;
	*_row = row;
	*_col = col;
	return flag;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if 0
//■テストプログラム
static const char* const TBL_ref[][2]={
{	"A1",						"A1?",						},
{	"Sheet1!A1",					"Sheet1!A1?",					},
{	"'Sheet 1'!A1",					"'Sheet 1'!A1?",				},
{	"[Book1.xlsx]Sheet1!A1",			"[Book1.xlsx]Sheet1!A1?",			},
{	"'[Book 1.xlsx]Sheet 1'!A1",			"'[Book 1.xlsx]Sheet 1'!A1?",			},
{	"'C:\\Home\\[Book1.xlsx]Sheet1'!A1",		"'C:\\Home\\[Book1.xlsx]Sheet1'!A1?",		},
{	"'C:\\H''o''me\\[Book1.xlsx]Sheet1'!A1",	"'C:\\H''o''me\\[Book1.xlsx]Sheet1'!A1?",	},
{	"ABC123",					"ABC123?",					},
{	"$ABC123",					"$ABC123?",					},
{	"ABC$123",					"ABC$123?",					},
{	"$ABC$123",					"$ABC$123?",					},
{	"RC",						"RC?",						},
{	"R123C",					"R123C?",					},
{	"RC123",					"RC123?",					},
{	"R123C123",					"R123C123?",					},
{	"R[-123]C",					"R[-123]C?",					},
{	"RC[-123]",					"RC[-123]?",					},
{	"R[-123]C[-123]",				"R[-123]C[-123]?",				},
{	"AAA1",						"WWW1?",					},
};
void test() {
	char *endptr, *book, *sheet;
	int flag, col, row, i, j;
	for(i = 0; i < ARRAY_SIZE(TBL_ref); i++) {
		for(j = 0; j < 2; j++) {
			flag = strtoref(TBL_ref[i][j], &endptr, &book, &sheet, &row, &col);
			printf("%-40s %06b %'8d %'8d %-24s %-24s\n", TBL_ref[i][j], flag, row, col, book ? book : "(NULL)", sheet ? sheet : "(NULL)");
			if(j == 0) { if(endptr != (TBL_ref[i][j] + strlen(TBL_ref[i][j])         )) { DIE(); } }
			if(j == 1) { if(endptr != (TBL_ref[i][j] + strlen(TBL_ref[i][j]) - 1/*?*/)) { DIE(); } }
			free(book);
			free(sheet);
		}
	}
}
//□結果
//│A1                                       101001        1        1 (NULL)                   (NULL)
//│A1?                                      101001        1        1 (NULL)                   (NULL)
//│Sheet1!A1                                101001        1        1 (NULL)                   Sheet1
//│Sheet1!A1?                               101001        1        1 (NULL)                   Sheet1
//│'Sheet 1'!A1                             101001        1        1 (NULL)                   Sheet 1
//│'Sheet 1'!A1?                            101001        1        1 (NULL)                   Sheet 1
//│[Book1.xlsx]Sheet1!A1                    101001        1        1 Book1.xlsx               Sheet1
//│[Book1.xlsx]Sheet1!A1?                   101001        1        1 Book1.xlsx               Sheet1
//│'[Book 1.xlsx]Sheet 1'!A1                101001        1        1 Book 1.xlsx              Sheet 1
//│'[Book 1.xlsx]Sheet 1'!A1?               101001        1        1 Book 1.xlsx              Sheet 1
//│'C:\Home\[Book1.xlsx]Sheet1'!A1          101001        1        1 C:\Home\Book1.xlsx       Sheet1
//│'C:\Home\[Book1.xlsx]Sheet1'!A1?         101001        1        1 C:\Home\Book1.xlsx       Sheet1
//│'C:\H''o''me\[Book1.xlsx]Sheet1'!A1      101001        1        1 C:\H'o'me\Book1.xlsx     Sheet1
//│'C:\H''o''me\[Book1.xlsx]Sheet1'!A1?     101001        1        1 C:\H'o'me\Book1.xlsx     Sheet1
//│ABC123                                   101001      123      731 (NULL)                   (NULL)
//│ABC123?                                  101001      123      731 (NULL)                   (NULL)
//│$ABC123                                  011001      123      731 (NULL)                   (NULL)
//│$ABC123?                                 011001      123      731 (NULL)                   (NULL)
//│ABC$123                                  100101      123      731 (NULL)                   (NULL)
//│ABC$123?                                 100101      123      731 (NULL)                   (NULL)
//│$ABC$123                                 010101      123      731 (NULL)                   (NULL)
//│$ABC$123?                                010101      123      731 (NULL)                   (NULL)
//│RC                                       101010        0        0 (NULL)                   (NULL)
//│RC?                                      101010        0        0 (NULL)                   (NULL)
//│R123C                                    100110      123        0 (NULL)                   (NULL)
//│R123C?                                   100110      123        0 (NULL)                   (NULL)
//│RC123                                    011010        0      123 (NULL)                   (NULL)
//│RC123?                                   011010        0      123 (NULL)                   (NULL)
//│R123C123                                 010110      123      123 (NULL)                   (NULL)
//│R123C123?                                010110      123      123 (NULL)                   (NULL)
//│R[-123]C                                 101010     -123        0 (NULL)                   (NULL)
//│R[-123]C?                                101010     -123        0 (NULL)                   (NULL)
//│RC[-123]                                 101010        0     -123 (NULL)                   (NULL)
//│RC[-123]?                                101010        0     -123 (NULL)                   (NULL)
//│R[-123]C[-123]                           101010     -123     -123 (NULL)                   (NULL)
//│R[-123]C[-123]?                          101010     -123     -123 (NULL)                   (NULL)
//│AAA1                                     101001        1      703 (NULL)                   (NULL)
//│WWW1?                                    101001        1   16,169 (NULL)                   (NULL)		(23*26*26)+(23*26)+23=16169
#endif
/*--------------------------------------------------------------------------*/
static int reftostr_subr1(GString* s, const char* ptr);	//前方参照
static void reftostr_subr2(GString* s, int row);	//前方参照
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
char* reftostr(const char* book/*NULL可*/, const char* sheet/*NULL可*/, int row, int col, int flag) {
	GString* s = g_string_new(NULL);
	int req_quote = 0;	//ブック名,又は,シート名に、Cシンボル文字,又は'.'以外が含まれていたら、req_quote=1にする。
	//ブック名が指定されていたら…
	if(book) {
		//ブック名を、パス名とブック名に分割する。
		char path[MAX_PATH], drive[MAX_DRIVE], dir[MAX_DIR], fname[MAX_FNAME], ext[MAX_EXT];
		splitpath(book, drive, dir, fname, ext);
		//パス名を、出力文字列に追加する。
		makepath(path, drive, dir, NULL, NULL);
		req_quote |= reftostr_subr1(s, path);	//パス名に、Cシンボル文字,又は'.'以外が含まれていたら、req_quote=1にする。
		//ファイル名の前に、'['を追加する。
		g_string_append_c(s, '[');
		//ファイル名を、出力文字列に追加する。
		makepath(path, NULL, NULL, fname, ext);
		req_quote |= reftostr_subr1(s, path);	//ファイル名に、Cシンボル文字,又は'.'以外が含まれていたら、req_quote=1にする。
		//ファイル名の後に、']'を追加する。
		g_string_append_c(s, ']');
	}
	//シート名が指定されていたら…
	if(sheet) {
		//ファイル名を、出力文字列に追加する。
		req_quote |= reftostr_subr1(s, sheet);	//シート名に、Cシンボル文字,又は'.'以外が含まれていたら、req_quote=1にする。
	}
	//ブック名,又は,シート名が指定されていたら…
	if(s->len) {
		//ブック名,又は,シート名に、Cシンボル文字,又は'.'以外が含まれていたら…
		if(req_quote) {
			//ブック名とシート名を、「'」～「'」で囲む。
			g_string_insert_c(s, 0, '\'');
			g_string_append_c(s,    '\'');
		}
		//シート名の前に、'!'を追加する。
		g_string_append_c(s, '!');
	}
	//A1形式,又は,R1C1形式によって…
	switch(flag & XlsRef_Style_Mask) {
	default:DIE();
	//A1形式
	case XlsRef_Style_A1:
		//列絶対,又は,列相対によって…
		switch(flag & XlsRef_ColType_Mask) {
		default:DIE();
		//列絶対
		case XlsRef_ColType_Abs:
			g_string_append_c(s, '$');
			/* FALLTHRU */
		//列相対
		case XlsRef_ColType_Rel:
			if(col < 1) { DIE(); }		//A1形式では、絶対でも相対でも、1未満は不可。
			reftostr_subr2(s, col);
			break;
		}
		//行絶対,又は,行相対によって…
		switch(flag & XlsRef_RowType_Mask) {
		default:DIE();
		//行絶対
		case XlsRef_RowType_Abs:
			g_string_append_c(s, '$');
			/* FALLTHRU */
		//行相対
		case XlsRef_RowType_Rel:
			if(row < 1) { DIE(); }		//A1形式では、絶対でも相対でも、1未満は不可。
			g_string_append_printf(s, "%d", row);
			break;
		}
		break;
	//R1C1形式
	case XlsRef_Style_R1C1:
		//行絶対,又は,行相対によって…
		switch(flag & XlsRef_RowType_Mask) {
		default:DIE();
		//行絶対
		case XlsRef_RowType_Abs:
			if(row < 1) { DIE(); }		//R1C1形式では、絶対の場合のみ、1未満は不可。
			g_string_append_printf(s, "R%d", row);
			break;
		//行相対
		case XlsRef_RowType_Rel:
			if(row) {
				g_string_append_printf(s, "R[%d]", row);
			} else {
				g_string_append_printf(s, "R");
			}
			break;
		}
		//列絶対,又は,列相対によって…
		switch(flag & XlsRef_ColType_Mask) {
		default:DIE();
		//列絶対
		case XlsRef_ColType_Abs:
			if(col < 1) { DIE(); }		//R1C1形式では、絶対の場合のみ、1未満は不可。
			g_string_append_printf(s, "C%d", col);
			break;
		//列相対
		case XlsRef_ColType_Rel:
			if(col) {
				g_string_append_printf(s, "C[%d]", col);
			} else {
				g_string_append_printf(s, "C");
			}
			break;
		}
		break;
	}
	//GByteArrayの構造体を開放し、バッファは開放せずに戻り値とする。
	// - 呼び出し側にてfree()で解放して下さい。
	return g_string_free(s, 0/*free_segment*/);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int reftostr_subr1(GString* s, const char* ptr) {
	int c, req_quote = 0;	//入力文字列に、Cシンボル文字,又は'.'以外が含まれていたら、req_quote=1にする。
	//入力文字列の、各文字について…
	while((c = mbsnextc(ptr))) {					//シフトJIS対応
		ptr = mbsinc(ptr);					//シフトJIS対応
		//出力文字列に追加する。
		if(c > UCHAR_MAX) { g_string_append_c(s, c >> 8); }	//シフトJIS対応
		g_string_append_c(s, c);
		//「'」は「''」にエスケープする。
		if(c == '\'') { g_string_append_c(s, c); }
		//入力文字列に、Cシンボル文字,又は'.'以外が含まれていたら、req_quote=1にする。
		// - '.'が含まれていても、ブック名とシート名を「'」～「'」で囲む必要は無い事に注意して下さい。
		//   もし'.'もクォート対象にしてしまうと、ブック名は拡張子の'.'を含むので、常にクォートが必要になってしまいます。
		//   本物のExcelでも、ブック名やシート名に(拡張子に限らず)'.'が含まれていても、クォートされない事を確認しました。(Excel 2013にて確認)
		if(!(iscsym(c) || (c == '.'))) { req_quote = 1; }
	}
	return req_quote;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void reftostr_subr2(GString* s, int row) {
	//上位桁の値(d.quot=0～)と、この桁の値(d.rem=1～)を求める。
	div_t d = div(row, ('Z' - 'A' + 1));
	//上位桁の値が有れば、上位桁の文字を出力する。
	if(d.quot) { reftostr_subr2(s, d.quot); }
	//この桁の文字を出力する。
	g_string_append_c(s, d.rem + 'A' - 1);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if 0
//■テストプログラム
static struct {
	int		flag, row, col;
	const char	*book, *sheet;
} TBL_ref[]={														//□結果
{	BIN8(101001),		1,		1,		NULL,				NULL		},	//│A1
{	BIN8(101001),		1,		1,		NULL,				"Sheet1"	},	//│Sheet1!A1
{	BIN8(101001),		1,		1,		NULL,				"Sheet 1"	},	//│'Sheet 1'!A1
{	BIN8(101001),		1,		1,		"Book1.xlsx",			"Sheet1"	},	//│[Book1.xlsx]Sheet1!A1
{	BIN8(101001),		1,		1,		"Book 1.xlsx",			"Sheet 1"	},	//│'[Book 1.xlsx]Sheet 1'!A1
{	BIN8(101001),		1,		1,		"C:\\Home\\Book1.xlsx",		"Sheet1"	},	//│'C:\Home\[Book1.xlsx]Sheet1'!A1
{	BIN8(101001),		1,		1,		"C:\\H'o'me\\Book1.xlsx",	"Sheet1"	},	//│'C:\H''o''me\[Book1.xlsx]Sheet1'!A1
{	BIN8(101001),		123,		731,		NULL,				NULL		},	//│ABC123
{	BIN8(011001),		123,		731,		NULL,				NULL		},	//│$ABC123
{	BIN8(100101),		123,		731,		NULL,				NULL		},	//│ABC$123
{	BIN8(010101),		123,		731,		NULL,				NULL		},	//│$ABC$123
{	BIN8(101010),		0,		0,		NULL,				NULL		},	//│RC
{	BIN8(100110),		123,		0,		NULL,				NULL		},	//│R123C
{	BIN8(011010),		0,		123,		NULL,				NULL		},	//│RC123
{	BIN8(010110),		123,		123,		NULL,				NULL		},	//│R123C123
{	BIN8(101010),		-123,		0,		NULL,				NULL		},	//│R[-123]C
{	BIN8(101010),		0,		-123,		NULL,				NULL		},	//│RC[-123]
{	BIN8(101010),		-123,		-123,		NULL,				NULL		},	//│R[-123]C[-123]
{	BIN8(101001),		1,		703,		NULL,				NULL		},	//│AAA1
{	BIN8(101001),		1,		16169,		NULL,				NULL		},	//│WWW1
};
int test() {
	int i;
	for(i = 0; i < ARRAY_SIZE(TBL_ref); i++) {
		char* s = reftostr(TBL_ref[i].book, TBL_ref[i].sheet, TBL_ref[i].row, TBL_ref[i].col, TBL_ref[i].flag);
		puts(s);
		free(s);
	}
	return EXIT_SUCCESS;
}
#endif
