/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/

    【機能概要】    : XML解析・データ配列化API
                      C言語標準のlibxml2を用いてXMLの解析を行い、解析結果を配列データ
					  として呼び元プログラムに返却する
					  本プログラムは、C言語（C++ではない）ため、他のAPIとは呼び方や
					  make方法が異なることに注意する。

	【作成日】		: 2021.04.23

	【呼出形式】	: GG_SHM1100 (
						  char *filename					# XMLファイル名称
					  )
	【戻り値】		: char*									# 解析結果配列データ

	【XML記述サンプル】
		<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
		<TableAccess>
			<Table id="M_TEST01" name="M_TEST01">
				<Search id="Search_M_TEST01_001">
					<Define>
						<Sentence where="項目1 = ? or 項目2 = ?" />
						<iData column="項目1"/>
						<iData column="項目2"/>
					</Define>
				</Search>
				<Search id="Index_M_TEST01_002">
					<Define>
						<Sentence>
							<Part where="項目1 = ?" />
							<Part where="and 項目2 = ?" />
						</Sentence>
						<iData column="項目1"/>
						<iData column="項目2"/>
					</Define>
				</Search>
			</Table>
		</TableAccess>

_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */

#include <stdio.h>
#include <stdlib.h>
#include <libxml/xmlerror.h>
#include <libxml/parser.h>
#include <libxml/SAX2.h>
#include <iconv.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>

#define		STR_NULL					""
#define		GG_SHM1100_MaxRow			1000
#define		GG_SHM1100_MaxColumn		100
#define		GG_SHM1100_MaxAttbSz		768
#define		GG_SHM1100_MaxRetSz			1024000

iconv_t cd;

static char ***dataStruct ;				/* XML分析結果の一時保存領域 */
static char *c_data ;					/* 上位APへのデータ返却領域 */

static int row = 0 ;
static int column = 0 ;
static int errorflg = 0 ;

/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
	三次元配列データの作成（テーブルデータの一時保存用に使用）
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
char*** GG_SHM1100_malloc( int rows , int cols , int colsize ) {

	char ***a, **b, *c;
	int i = rows;
	int j = cols;
	int t = sizeof(char) * colsize;

	a = (char***)malloc((sizeof(*a) + sizeof(**a) * j + t * j) * i);
	if(a == NULL){
		return a;
	}

	if(a != NULL){
		b = (char**)(a + i);
		c = (char*)(b + i * j);
		for(int idx1 = 0; idx1 < i; idx1++){
			a[idx1] = b;
			for(int idx2 = 0; idx2 < j; idx2++){
				b[idx2] = c;
				c += t;
			}
			b += j;
		}
	}
	return a;
}

/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
			文字コード変換処理（今後の拡張用）
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
int SHM1100_iconv (char *fptr , size_t fsize , char *tptr , size_t tsize) {
	char *fwork , *twork ;
	size_t fsz , tsz , ret ;
	fwork = fptr ;
	twork = tptr ;
	fsz = fsize ;
	tsz = tsize ;
	ret = iconv(cd, &fwork, &fsz, &twork, &tsz);
	if (ret == (size_t)-1) {
		printf ( "errno=%d\n",errno) ;
		return (-1) ;
	}
	*twork = '\0' ;
	return (0) ;
}

/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
			XMLデータ分析処理メイン
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
static void start_element_ns_callback(void *ctx, const xmlChar *localname,
					const xmlChar *prefix, const xmlChar *URI,
					int nb_namespaces, const xmlChar **namespaces,
					int nb_attributes, int nb_defaulted,
					const xmlChar **attributes)
{
	char		*fptr ;
	char		toLocalname[256] , toAtts[768] , toParm[1024] ;

	#ifdef _DBG_SHM1100
		fprintf(stdout, "nb_namespaces=[%d] nb_attributes=[%d] nb_defaulted=[%d]\n"
				,nb_namespaces,nb_attributes,nb_defaulted) ;
	#endif

	if ( errorflg < 0 ) return ;

	/* タグ名称（localname）処理 */
	if ( localname != NULL ) {
		errorflg = SHM1100_iconv (
					  (char *)localname
					, strlen( (const char *)localname )
					, toLocalname
					, sizeof( toLocalname ) - 1
				);
		if (errorflg == -1) return ;
		#ifdef _DBG_SHM1100
			fprintf(stdout, "localname = <%s>\n", toLocalname);
		#endif
	}
	#ifdef _DBG_SHM1100
		printf("nb_attributes=[%d]\n",nb_attributes) ;
	#endif

	/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
 		Sentenceタグ対応（where句）
		where句を複数行に分割記述する場合、Partタグを用いる。その場合は
		Sentenceタグには何も書かないため、下記のロジックになる
		※ XML記述サンプル参照（上記）
	_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */

	if ( strcmp ( toLocalname , "Sentence" ) == 0 && nb_attributes == 0 ) {
		column = 2 ;
		strcpy ( dataStruct[row][column] , STR_NULL ) ;
		strcpy ( dataStruct[row][column+1] , STR_NULL ) ;
	}

	/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
 		xml / attributes 処理
		例： <Table id="M_TEST01" name="M_TEST01"> という記述は、
			 	・toLocalname に 「Table」
			 	・attributes[n] に 「id」 （nは5の倍数）
			 	・attributes[n+3] に 「M_TEST01」
			 が格納される
	_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */

	for(int i = 0 ; i < nb_attributes ; i++ ) {

		if ( attributes[i*5] == NULL ) continue ;

		/* attributes名称（id等）の取得 */
		errorflg = SHM1100_iconv (
					  (char *)attributes[i*5]
					, strlen( (const char *)attributes[i*5] )
					, toAtts
					, sizeof( toAtts ) - 1
				);
		if (errorflg == -1) return ;
		#ifdef _DBG_SHM1100
			fprintf(stdout, "Atts[%d] = <%s> " , i , toAtts) ;
		#endif

		/* attributes値（M_TEST01等）の取得 */
		fptr = (char *)attributes[i*5+3] ;
		strtok ( fptr , "\"" ) ;
		errorflg = SHM1100_iconv (
					  fptr 
					, strlen( (const char *)fptr )
					, toParm
					, sizeof( toParm ) - 1
				);
		if (errorflg == -1) return ;
		#ifdef _DBG_SHM1100
			fprintf(stdout, "Parm[%d] = <%s>\n" , i , toParm) ;
		#endif

		/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
 			各タグ／アトリビュート／値の処理
		_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */

		/* Table タグ処理 */
		if ( strcmp ( toLocalname , "Table" ) == 0 ) {
			if ( strcmp ( toAtts , "id" ) == 0 ) {
				row = 0 ;
				column = 0 ;
				strcpy ( dataStruct[row][column] , toParm ) ;
				strcpy ( dataStruct[row+1][column] , STR_NULL ) ;
				#ifdef _DBG_SHM1100
					printf("dataStruct[%d][%d]=[%s]\n",row,column,dataStruct[row][column]) ;
				#endif
			} else if ( strcmp ( toAtts , "name" ) == 0 ) {
				/* テーブル名に対する日本語を記述するが、システムでは未使用 */
			} else {
				printf ( "Tableタグに (id or name)以外のタグを検出\n" ) ;
				errorflg = -3 ;
			}
		/* Search タグ処理 */
		} else if ( strcmp ( toLocalname , "Search" ) == 0 ) {
			if ( strcmp ( toAtts , "id" ) == 0 ) {
				if ( row != 0 || column != 0 ) {
					row ++ ;
					strcpy ( dataStruct[row][0] , dataStruct[0][0] ) ;
					strcpy ( dataStruct[row+1][0] , STR_NULL ) ;
				}
				column = 1 ;
				strcpy ( dataStruct[row][column] , toParm ) ;
				#ifdef _DBG_SHM1100
					printf("dataStruct[%d][%d]=[%s]\n",row,column,dataStruct[row][column]) ;
				#endif
			} else {
				printf ( "Searchタグに id 以外のタグを検出\n" ) ;
				errorflg = -4 ;
			}
		/* Sentence タグ処理 */
		} else if ( strcmp ( toLocalname , "Sentence" ) == 0 ) {
			if ( strcmp ( toAtts , "where" ) == 0 ) {
				column = 2 ;
				strcpy ( dataStruct[row][column] , toParm ) ;
				strcpy ( dataStruct[row][column+1] , STR_NULL ) ;
				#ifdef _DBG_SHM1100
					printf("dataStruct[%d][%d]=[%s]\n",row,column,dataStruct[row][column]) ;
				#endif
			} else {
				printf ( "Sentenceタグに where 以外のタグを検出\n" ) ;
				errorflg = -5 ;
			}
		/* iData タグ処理 */
		} else if ( strcmp ( toLocalname , "iData" ) == 0 ) {
			if ( strcmp ( toAtts , "column" ) == 0 ) {
				column ++ ;
				strcpy ( dataStruct[row][column] , toParm ) ;
				strcpy ( dataStruct[row][column+1] , STR_NULL ) ;
				#ifdef _DBG_SHM1100
					printf("dataStruct[%d][%d]=[%s]\n",row,column,dataStruct[row][column]) ;
				#endif
			} else {
				printf ( "iDataタグに column 以外のタグを検出\n" ) ;
				errorflg = -6 ;
			}
		/* Part タグ処理 （Sentence タグの複数行対応用）*/
		} else if ( strcmp ( toLocalname , "Part" ) == 0 ) {
			if ( strcmp ( toAtts , "where" ) == 0 ) {
				column = 2 ;
				if ( strlen ( dataStruct[row][column] ) > 0 ) {
					strcat ( dataStruct[row][column] , " " ) ;
				}
				strcat ( dataStruct[row][column] , toParm ) ;
				#ifdef _DBG_SHM1100
					printf("dataStruct[%d][%d]=[%s]\n",row,column,dataStruct[row][column]) ;
				#endif
			} else {
				printf ( "Partタグに where 以外のタグを検出\n" ) ;
				errorflg = -7 ;
			}
		}
	}
}

/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
			XMLファイル・オープン／読込み処理
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
static int read_callback(void *context, char *buffer, int len)
{
	FILE *fp = (FILE *) context;
	#ifdef _DBG_SHM1100
		fprintf(stderr, "*** read_callback()\n");
	#endif
	return fread(buffer, 1, len, fp);
}

/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
			XMLファイル・クローズ処理
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
static int close_callback(void *context)
{
	FILE *fp = (FILE *) context;
	#ifdef _DBG_SHM1100
		fprintf(stderr, "*** close_callback()\n");
	#endif
	return fclose(fp);
}

/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
			共有メモリアクセス定義情報解析処理
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
char* GG_SHM1100 ( char *filename )
{
	xmlParserCtxtPtr xmlctx;
	xmlSAXHandler saxh;
	FILE *fp;
	struct	stat	stat_buf ;
	char *FileChar ;
	int rc ;
	int i , dquot ;

	errorflg = 0 ;

	/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
 			XMLファイルの文字コード変換オープン処理
			※ encoding値によってコード変換されるため、本処理は今後の拡張用
	_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */

	#ifdef _SJIS_SHM1100
		cd = iconv_open("UTF-8" , "SHIFT-JIS") ;
	#else
		cd = iconv_open("UTF-8" , "UTF-8") ;
	#endif

	if (cd == (iconv_t)-1) {
		fprintf(stderr,"iconv_open(%s) failed\\n", "UTF-8");
		errorflg = -11 ;
		return (char*)NULL ;
	}

	/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
				 		XMLファイルの存在有無チェック
	_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */

	if ( stat ( filename, &stat_buf ) != 0 ) {
		printf ( "xmlファイルがありません\n" ) ;
		errorflg = -8 ;
		return (char*)NULL ;
	}

	/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
				XML要素の最大値チェック
					dataStruct の要素サイズは、GG_SHM1100_MaxAttbSz まで
	_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */

	fp = fopen(filename, "r");
	FileChar = (char *)malloc ( stat_buf.st_size + 1 ) ;
	fread ( FileChar , 1 , stat_buf.st_size , fp ) ;
	fclose ( fp ) ;
	FileChar [ stat_buf.st_size ] = '\0' ;

	dquot = -1 ;
	i = 0 ;
	for ( i=0 ; i < stat_buf.st_size ; i++ ) {
		if ( FileChar[i] == '\"' ) {
			if ( dquot == -1 ) {
				dquot = 0 ;
			} else {
				if ( dquot > GG_SHM1100_MaxAttbSz ) {
					printf ( "xmlファイルの要素が%dバイトを越えています\n",GG_SHM1100_MaxAttbSz ) ;
					errorflg = -9 ;
					return (char*)NULL ;
				}
				dquot = -1 ;
			}
		} else {
			if ( dquot != -1 ) {
				dquot ++ ;
			}
		}
	}
	free ( (void *)FileChar ) ;

	/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
			 		XMLデータの分析結果一時保存領域の確保
	_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */

	dataStruct = GG_SHM1100_malloc (
		  GG_SHM1100_MaxRow + 1
		, GG_SHM1100_MaxColumn + 1
		, GG_SHM1100_MaxAttbSz + 1
	) ;

	/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
			 		上位APへの分析結果返却領域
	_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */

	c_data = (char *) malloc ( GG_SHM1100_MaxRetSz + 1 ) ;

	/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
				 		libxml2の利用手続き部
	_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */

	xmlInitParser();
	xmlDefaultSAXHandlerInit();
	xmlSAX2InitDefaultSAXHandler(&saxh, 1);
	xmlSAXVersion(&saxh, 2);
	saxh.startElementNs = start_element_ns_callback;

	/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
				 	XMLファイルのParser登録
	_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */

	fp = fopen(filename, "r");
	xmlctx = xmlCreateIOParserCtxt(&saxh, NULL, read_callback, close_callback,
						fp, XML_CHAR_ENCODING_NONE);

	/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
						パース開始
	_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */

	if (xmlParseDocument(xmlctx)) {
		xmlParserError(xmlctx, "xmlParseDocument");
		errorflg = -12 ;
		printf ( "xmlファイル解析エラー\n" ) ;
		free ( (void *)dataStruct ) ;
		return (char*)NULL ;
	}

	/* 不要の可能性あり */
	xmlClearParserCtxt(xmlctx);

	/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
			上位APへの返却（c_data）用に編集
				[データ01]\t[データ02]\t[データ03]\t・・・\n
				[データ11]\t[データ12]\t[データ13]\t・・・\n
				※ 上記の繰り返し
	_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */

	i = 0 ;
	for ( row=0 ; strcmp(dataStruct[row][0],STR_NULL)!=0 ; row++ ){
		for ( column=0 ; strcmp(dataStruct[row][column],STR_NULL)!=0 ; column++ ){
			#ifdef _DBG_SHM1100
				printf("dataStruct[%d][%d]=[%s]\n",row,column,dataStruct[row][column]) ;
			#endif
			strcpy ( c_data + i ,dataStruct[row][column] ) ;
			i = i + strlen(dataStruct[row][column]) ;
			c_data [ i ] = '\t' ;
			i ++ ;
		}
		if ( column != 0 ) {
			c_data [ i ] = '\n' ;
			i ++ ;
		}
		if ( i > GG_SHM1100_MaxRetSz ) {
			errorflg = -13 ;
			printf ( "分析結果サイズオーバーエラー[%d バイト]\n" , GG_SHM1100_MaxRetSz ) ;
			free ( (void *)dataStruct ) ;
			return (char*)NULL ;
		}
	}
	c_data[i] = '\0' ;
	free ( (void *)dataStruct ) ;

	if ( errorflg == 0 && strlen(c_data) < 1 ) {
		printf ( "xmlファイルの中がありません\n" ) ;
		errorflg = -10 ;
	}
	if ( errorflg != 0 ) {
		printf ( "errorflg=%d\n",errorflg ) ;
		return (char*)NULL ;
	}

	return c_data ;
}
