OSDN Git Service

Windows上でwxWidgets2.9.1に移行。
[tpi/lychee.git] / src / plugin / cuiWrapper / cuiWrapper.cpp
1 /*******************************************************************************\r
2   TPI - flexible but useless plug-in framework.\r
3   Copyright (C) 2002-2009 Silky\r
4 \r
5   This library is free software; you can redistribute it and/or modify it under\r
6   the terms of the GNU Lesser General Public License as published by the Free\r
7   Software Foundation; either version 2.1 of the License, or (at your option)\r
8   any later version.\r
9 \r
10   This library is distributed in the hope that it will be useful, but WITHOUT\r
11   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or \r
12   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License\r
13   for more details.\r
14 \r
15   You should have received a copy of the GNU Lesser General Public License along\r
16   with this library; if not, write to the Free Software Foundation, Inc.,\r
17   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
18 \r
19   $Id$\r
20 *******************************************************************************/\r
21 \r
22 //******************************************************************************\r
23 //    Includes\r
24 //******************************************************************************\r
25 \r
26 #include "../../common/header/plugin.h"\r
27 #include "../../common/header/plugin-extra.h"\r
28 #include "../../common/library/library.h"\r
29 #include <wx/config.h>\r
30 #include <wx/stdpaths.h>\r
31 #include <wx/xml/xml.h>\r
32 #include <wx/tokenzr.h>\r
33 #include "cuiWrapper.h"\r
34 \r
35 //******************************************************************************\r
36 //    Global varients\r
37 //******************************************************************************\r
38 \r
39 struct g_LibInfo\r
40 {\r
41         wxString szExeFile;\r
42         wxString szExeFileAlt;\r
43         wxString szListCommand;\r
44         wxULongLong_t nLibIndex;\r
45         wxXmlNode node;\r
46 }       g_LibInfo;\r
47 \r
48 TPI_PROC g_prProc;\r
49 wxString g_szCurrentArchive;\r
50 wxArrayString g_asOutput;\r
51 \r
52 //******************************************************************************\r
53 //    Inside Functions\r
54 //******************************************************************************\r
55 \r
56 int myExecute(wxString szCommandLine, wxString * szOutput, wxString szCwd)\r
57 {\r
58 #ifdef __LINUX__\r
59         wxString sz = ::wxGetCwd();\r
60         ::wxSetWorkingDirectory(szCwd);\r
61         FILE * fp = popen(szCommandLine.ToUTF8(), "r");\r
62         ::wxSetWorkingDirectory(sz);\r
63         if (fp == NULL)\r
64         {\r
65                 wxLogError(L"Error :\n\nCommandLine:\n%s", szCommandLine.c_str());\r
66                 return TPI_ERROR_U_USE_LIBRARY;\r
67         }\r
68 \r
69         if (szOutput != NULL)\r
70         {\r
71                 char sz[32769];\r
72                 while (! feof(fp))\r
73                 {\r
74                         memset(sz, 0, sizeof(sz));\r
75                         fread(sz, sizeof(char), sizeof(sz) - 1, fp);\r
76                         * szOutput += UTF82String(sz);\r
77                 }\r
78 //              ::wxMessageBox(* szOutput);\r
79         }\r
80 \r
81         // 127を返した場合はコマンドが存在しない。\r
82         int nErrorCode = pclose(fp);\r
83         return (WIFEXITED(nErrorCode) && WEXITSTATUS(nErrorCode) != 127) ? TPI_ERROR_SUCCESS : TPI_ERROR_U_USE_LIBRARY;\r
84 #else\r
85         SECURITY_ATTRIBUTES sa;\r
86         memset(& sa, 0, sizeof(SECURITY_ATTRIBUTES));\r
87         sa.bInheritHandle = TRUE;\r
88         sa.nLength = sizeof(SECURITY_ATTRIBUTES);\r
89         HANDLE hRead, hWrite;\r
90         if (! ::CreatePipe(& hRead, & hWrite, & sa, 0))\r
91         {\r
92                 return TPI_ERROR_U_USE_LIBRARY;\r
93         }\r
94         STARTUPINFO si;\r
95         memset(& si, 0, sizeof(STARTUPINFO));\r
96         si.cb = sizeof(STARTUPINFO);\r
97         si.dwFlags = STARTF_USESTDHANDLES;\r
98         si.hStdOutput = hWrite;\r
99         si.hStdError = hWrite;\r
100         PROCESS_INFORMATION pi;\r
101         if (! ::CreateProcess(NULL, szCommandLine.wchar_str(), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, szCwd.IsEmpty() ? NULL : szCwd.wchar_str(), & si, & pi))\r
102         {\r
103                 return TPI_ERROR_U_USE_LIBRARY;\r
104         }\r
105         ::CloseHandle(pi.hThread);\r
106 \r
107         if (szOutput != NULL)\r
108         {\r
109                 char sz[32768];\r
110                 while (true)\r
111                 {\r
112                         DWORD dwSize = 0;\r
113                         if (! ::PeekNamedPipe(hRead, NULL, 0, NULL, & dwSize, NULL))\r
114                         {\r
115                                 continue;\r
116                         }\r
117 \r
118                         if (dwSize > 0)\r
119                         {\r
120                                 memset(sz, 0, sizeof(sz));\r
121                                 ::ReadFile(hRead, & sz, sizeof(sz), & dwSize, NULL);\r
122                                 * szOutput += UTF82String(sz);\r
123 //                              ::MessageBoxA(NULL, sz, NULL, 0);\r
124                         }\r
125                         else if (::WaitForSingleObject(pi.hProcess, 0) == WAIT_OBJECT_0)\r
126                         {\r
127                                 break;\r
128                         }\r
129                 }\r
130         }\r
131 \r
132         ::CloseHandle(pi.hProcess);\r
133         ::CloseHandle(hRead);\r
134         ::CloseHandle(hWrite);\r
135         return TPI_ERROR_SUCCESS;\r
136 #endif\r
137 }\r
138 \r
139 PosInfo MakePosInfo(wxString szPrefix)\r
140 {\r
141         PosInfo pi;\r
142         g_LibInfo.node.GetAttribute(wxT("list-") + szPrefix + wxT("-s"), wxEmptyString).ToULongLong(& pi.nStart);\r
143         g_LibInfo.node.GetAttribute(wxT("list-") + szPrefix + wxT("-c"), wxEmptyString).ToULongLong(& pi.nCount);\r
144         g_LibInfo.node.GetAttribute(wxT("list-") + szPrefix + wxT("-l"), wxT("0")).ToULongLong(& pi.nLine);\r
145         return pi;\r
146 }\r
147 \r
148 wxULongLong_t GetSize(PosInfo & pi, wxULongLong_t nCurrent, wxArrayString & as)\r
149 {\r
150         if (pi.nStart == 0 && pi.nCount == 0)\r
151         {\r
152                 return 0;\r
153         }\r
154 \r
155         wxULongLong_t nTemp, nPos = nCurrent + pi.nLine;\r
156         wxString sz = pi.nCount == 0 ? as[nPos].Mid(pi.nStart) : as[nPos].Mid(pi.nStart, pi.nCount);\r
157         sz.ToULongLong(& nTemp);\r
158         return nTemp;\r
159 }\r
160 \r
161 //******************************************************************************\r
162 //    Functions\r
163 //******************************************************************************\r
164 \r
165 #ifdef __cplusplus\r
166 extern "C"\r
167 {\r
168 #endif\r
169 \r
170 int __stdcall GetPluginInformation\r
171 (\r
172         unsigned int _uInfoId,\r
173         wxULongLong_t,\r
174         void * _pPtr\r
175 )\r
176 {\r
177         if (_pPtr == NULL)\r
178         {\r
179                 return TPI_ERROR_D_PARAMETER;\r
180         }\r
181         switch (_uInfoId)\r
182         {\r
183         case TPI_INFO_VERSION_MAJOR:\r
184         case TPI_INFO_VERSION_MINOR:\r
185                 * (int *) _pPtr = 0;\r
186                 break;\r
187         case TPI_INFO_VERSION_API:\r
188                 * (int *) _pPtr = 2;\r
189                 break;\r
190         default:\r
191                 return TPI_ERROR_D_UNSUPPORTED;\r
192         }\r
193         return TPI_ERROR_SUCCESS;\r
194 }\r
195 \r
196 int __stdcall GetFormatInformation(TPI_FORMATINFO * _fiInfo, bool _bFirst)\r
197 {\r
198         static wxULongLong_t s_nFileId;\r
199         wxStandardPaths p;\r
200         wxXmlDocument config(wxPathOnly(p.GetExecutablePath()) + wxT("/lib/cuiWrapper.xml"));\r
201         // 一気に先頭のライブラリの情報を取得。\r
202         wxXmlNode * xmlLibrary = config.GetRoot()->GetChildren();\r
203 \r
204         if (_bFirst)\r
205         {\r
206                 // xml解析開始。\r
207                 s_nFileId = 0;\r
208         }\r
209         else\r
210         {\r
211                 for (wxULongLong_t i = 0; i < s_nFileId && xmlLibrary != NULL; i++)\r
212                 {\r
213                         xmlLibrary = xmlLibrary->GetNext();\r
214                 }\r
215         }\r
216 \r
217         if (xmlLibrary == NULL || xmlLibrary->GetName() != wxT("library"))\r
218         {\r
219                 // データの終端に達した場合。\r
220                 return TPI_ERROR_S_ENDOFDATA;\r
221         }\r
222 \r
223         MakeFormatInfo(wxT("cuiWrapper"), _fiInfo, xmlLibrary, s_nFileId++);\r
224         if (myExecute(xmlLibrary->GetAttribute(wxT("name"), wxEmptyString), NULL, wxEmptyString) != TPI_ERROR_SUCCESS)\r
225         {\r
226                 _fiInfo->eSupportedCommand = 0;\r
227         }\r
228 \r
229         return TPI_ERROR_SUCCESS;\r
230 }\r
231 \r
232 int __stdcall LoadPlugin\r
233 (\r
234         const wxString & _szArcName,\r
235         wxULongLong_t _llTypeId\r
236 )\r
237 {\r
238         // xml解析開始。\r
239         wxStandardPaths p;\r
240         wxXmlDocument config(wxPathOnly(p.GetExecutablePath()) + wxT("/lib/cuiWrapper.xml"));\r
241         if (! config.IsOk())\r
242         {\r
243                 return TPI_ERROR_UNDEFINED;\r
244         }\r
245         // 一気に先頭のライブラリの情報を取得。\r
246         wxXmlNode * xmlLibrary = config.GetRoot()->GetChildren();\r
247 \r
248         // 対象が存在するならば対応するライブラリを調査、\r
249         // 対象が存在しないならば指示されたライブラリをロード。\r
250         if (! ::wxFileExists(_szArcName))\r
251         {\r
252                 // 適当な位置まで移動。\r
253                 for (g_LibInfo.nLibIndex = 0; g_LibInfo.nLibIndex < _llTypeId && xmlLibrary != NULL; g_LibInfo.nLibIndex++)\r
254                 {\r
255                         xmlLibrary = xmlLibrary->GetNext();\r
256                 }\r
257                 if (xmlLibrary == NULL || xmlLibrary->GetName() != wxT("library"))\r
258                 {\r
259                         // xml文法エラー。\r
260                         return TPI_ERROR_UNDEFINED;\r
261                 }\r
262                 g_LibInfo.szExeFile = xmlLibrary->GetAttribute(wxT("name"), wxEmptyString);\r
263                 g_LibInfo.szExeFileAlt = xmlLibrary->GetAttribute(wxT("name-alt"), wxEmptyString);\r
264                 g_LibInfo.node = * xmlLibrary;\r
265 \r
266                 return TPI_ERROR_SUCCESS;\r
267         }\r
268 \r
269         // 無限ループに陥らないよう上限を設定。\r
270         for (g_LibInfo.nLibIndex = 0; g_LibInfo.nLibIndex < 300 && xmlLibrary != NULL; g_LibInfo.nLibIndex++)\r
271         {\r
272                 // ライブラリをロード。\r
273                 g_LibInfo.szExeFile = xmlLibrary->GetAttribute(wxT("name"), wxEmptyString);\r
274                 g_LibInfo.szExeFileAlt = xmlLibrary->GetAttribute(wxT("name-alt"), wxEmptyString);\r
275                 g_LibInfo.node = * xmlLibrary;\r
276 \r
277                 // 書庫に対応しているかチェック。\r
278                 if (CheckArchive(_szArcName, NULL) == TPI_ERROR_SUCCESS)\r
279                 {\r
280                         // 対応していれば処理を終了。\r
281                         return TPI_ERROR_SUCCESS;\r
282                 }\r
283 \r
284                 xmlLibrary = xmlLibrary->GetNext();\r
285         }\r
286 \r
287         return TPI_ERROR_U_LOAD_LIBRARY;\r
288 }\r
289 \r
290 int __stdcall FreePlugin\r
291 (\r
292         void * // _pReserved\r
293 )\r
294 {\r
295         return TPI_ERROR_SUCCESS;\r
296 }\r
297 \r
298 int __stdcall CheckArchive\r
299 (\r
300         const wxString & _szArcName,\r
301         wxULongLong_t * _llFileCount\r
302 )\r
303 {\r
304         wxFileName fnArchive(_szArcName);\r
305         wxArrayString asExt = ::wxStringTokenize(g_LibInfo.node.GetAttribute(wxT("suffix"), wxEmptyString), wxT(";"));\r
306         if (! g_LibInfo.node.HasAttribute(wxT("list")))\r
307         {\r
308                 return TPI_ERROR_ARC_UNSUPPORTED;\r
309         }\r
310 \r
311         for (size_t i = 0; i < asExt.GetCount(); i++)\r
312         {\r
313                 // .tar.XXXなど二重判定への対応。\r
314 //              if (asExt[i].IsSameAs(fnArchive.GetExt(), false))\r
315                 if (fnArchive.GetFullName().EndsWith(wxT('.') + asExt[i]))\r
316                 {\r
317                         // 開いて確認。先行してデータを取得しておく。\r
318                         int nErrorCode = OpenArchive(_szArcName, NULL);\r
319 \r
320                         // 対応。\r
321                         if (_llFileCount != NULL)\r
322                         {\r
323                                 // ファイル数は多めに取っておく。\r
324                                 * _llFileCount = g_asOutput.GetCount();\r
325                         }\r
326                         return nErrorCode;\r
327                 }\r
328         }\r
329 \r
330         return TPI_ERROR_ARC_UNSUPPORTED;\r
331 }\r
332 \r
333 int __stdcall OpenArchive\r
334 (\r
335         const wxString & _szArcName,\r
336         void * * _hArchive\r
337 )\r
338 {\r
339         if (g_szCurrentArchive != _szArcName)\r
340         {\r
341                 wxString szOutput;\r
342                 wxArrayString as;\r
343                 if (myExecute(g_LibInfo.szExeFile + wxT(" ") + MakeCommandLineSend(g_LibInfo.node.GetAttribute(wxT("list"), wxEmptyString), _szArcName, NULL, as, wxEmptyString), & szOutput, wxEmptyString) != TPI_ERROR_SUCCESS)\r
344                 {\r
345                         return TPI_ERROR_U_USE_LIBRARY;\r
346                 }\r
347                 g_szCurrentArchive = _szArcName;\r
348                 g_asOutput = ::wxStringTokenize(szOutput, wxT("\r\n"));\r
349                 g_asOutput.Shrink();\r
350         }\r
351         if (_hArchive != NULL)\r
352         {\r
353                 * _hArchive = & g_asOutput;\r
354         }\r
355         return g_asOutput.IsEmpty() ? TPI_ERROR_UNDEFINED : TPI_ERROR_SUCCESS;\r
356 }\r
357 \r
358 int __stdcall CloseArchive\r
359 (\r
360         void * _hArchive\r
361 )\r
362 {\r
363         ((wxArrayString *) _hArchive)->Clear();\r
364         return TPI_ERROR_SUCCESS;\r
365 }\r
366 \r
367 int __stdcall GetFileInformation\r
368 (\r
369         void * _hArchive,\r
370         TPI_FILEINFO * _fiInfo,\r
371         bool _bFirst\r
372 )\r
373 {\r
374         static size_t s_nCurrentLine;\r
375         static wxULongLong_t s_nFileId;\r
376         if (_hArchive == NULL)\r
377         {\r
378                 return TPI_ERROR_UNDEFINED;\r
379         }\r
380         wxArrayString asOutput = * (wxArrayString *) _hArchive;\r
381 \r
382         // XMLからの読み込みは初回に行う。\r
383         static wxString szEndLine, szDateFormat;\r
384         static wxULongLong_t nProcessPerLine;\r
385         static PosInfo piFName, piPSize, piUSize, piDate;\r
386         if (_bFirst)\r
387         {\r
388                 s_nFileId = 0;\r
389                 wxString szStartLine = g_LibInfo.node.GetAttribute(wxT("list-line-s"), wxEmptyString);\r
390                 if (! szStartLine.IsEmpty())\r
391                 {\r
392                         // 開始行の次の行にセット。\r
393                         s_nCurrentLine = asOutput.Index(szStartLine) + 1;\r
394                         if (s_nCurrentLine == wxNOT_FOUND + 1)\r
395                         {\r
396                                 // 書庫が読み込めなかった?\r
397                                 return TPI_ERROR_ARC_UNSUPPORTED;\r
398                         }\r
399                 }\r
400 \r
401                 // 初期設定。\r
402                 g_LibInfo.node.GetAttribute(wxT("list-line-c"), wxT("1")).ToULongLong(& nProcessPerLine);\r
403                 szEndLine = g_LibInfo.node.GetAttribute(wxT("list-line-e"), szStartLine);\r
404                 szDateFormat = g_LibInfo.node.GetAttribute(wxT("list-date-f"), wxDefaultDateTimeFormat);\r
405                 piFName = MakePosInfo(wxT("fname"));\r
406                 piPSize = MakePosInfo(wxT("psize"));\r
407                 piUSize = MakePosInfo(wxT("usize"));\r
408                 piDate  = MakePosInfo(wxT("date"));\r
409         }\r
410 \r
411         if (s_nCurrentLine >= asOutput.GetCount())\r
412         {\r
413                 // 空行で終わるとき以外はエラーとする。\r
414                 return szEndLine.IsEmpty() ? TPI_ERROR_S_ENDOFDATA : TPI_ERROR_ARC_UNSUPPORTED;\r
415         }\r
416 \r
417         // 最終行かどうか確認。\r
418         if (asOutput[s_nCurrentLine] == szEndLine)\r
419         {\r
420                 return TPI_ERROR_S_ENDOFDATA;\r
421         }\r
422 \r
423         // ファイル名を取得。\r
424         _fiInfo->szStoredName = piFName.nCount == 0 ? asOutput[s_nCurrentLine + piFName.nLine].Mid(piFName.nStart) : asOutput[s_nCurrentLine + piFName.nLine].Mid(piFName.nStart, piFName.nCount);\r
425         _fiInfo->szStoredName.Trim();\r
426         _fiInfo->fnFileName = wxFileName::wxFileName(_fiInfo->szStoredName);\r
427 \r
428         // サイズ取得。\r
429         _fiInfo->nPackedSize   = GetSize(piPSize, s_nCurrentLine, asOutput);\r
430         _fiInfo->nUnpackedSize = GetSize(piUSize, s_nCurrentLine, asOutput);\r
431 \r
432         // 更新時刻取得。\r
433         if (piDate.nStart != 0 || piDate.nCount != 0)\r
434         {\r
435                 _fiInfo->tmModified.ParseFormat(piDate.nCount == 0 ? asOutput[s_nCurrentLine + piDate.nLine].Mid(piDate.nStart) : asOutput[s_nCurrentLine + piDate.nLine].Mid(piDate.nStart, piDate.nCount), szDateFormat);\r
436         }\r
437 \r
438         // 最後に次の行へ進めておく。\r
439         _fiInfo->nFileId = s_nFileId++;\r
440         s_nCurrentLine += nProcessPerLine;\r
441 \r
442         return TPI_ERROR_SUCCESS;\r
443 }\r
444 \r
445 int __stdcall GetArchiveInformation\r
446 (\r
447         void *,\r
448         TPI_ARCHIVEINFO * _aiInfo\r
449 )\r
450 {\r
451         // 形式に関する情報を取得。\r
452         MakeFormatInfo(wxT("cuiWrapper"), & _aiInfo->fiInfo, & g_LibInfo.node, 0);\r
453         return TPI_ERROR_SUCCESS;\r
454 }\r
455 \r
456 int __stdcall Command\r
457 (\r
458         wxULongLong_t _eCommand,\r
459         TPI_SWITCHES * _swInfo,\r
460         const wxString & _szArcName,\r
461         const wxArrayString & _szFiles\r
462 )\r
463 {\r
464         // xmlからコマンドラインを取得。\r
465         wxString szPath, szCommandLine;\r
466 \r
467         // APIアドレス取得。\r
468         if (! g_LibInfo.node.GetAttribute(\r
469                         _eCommand == TPI_COMMAND_CREATE  ? wxT("create") :\r
470                         _eCommand == TPI_COMMAND_ADD     ? wxT("add") :\r
471                         _eCommand == TPI_COMMAND_EXTRACT ? wxT("extract") : \r
472                         _eCommand == TPI_COMMAND_DELETE  ? wxT("delete") : \r
473                         _eCommand == TPI_COMMAND_UPDATE  ? wxT("update") : \r
474                         _eCommand == TPI_COMMAND_TEST    ? wxT("test") : \r
475                         _eCommand == TPI_COMMAND_REPAIR  ? wxT("repair") : \r
476                         _eCommand == TPI_COMMAND_MOVE    ? wxT("move") : \r
477                         _eCommand == TPI_COMMAND_SFX     ? wxT("sfx") : \r
478                         _eCommand == TPI_COMMAND_UNSFX   ? wxT("unsfx") : wxEmptyString, & szCommandLine))\r
479         {\r
480                 g_LibInfo.node.GetAttribute(\r
481                         _eCommand == TPI_COMMAND_CREATE  ? wxT("create-alt") :\r
482                         _eCommand == TPI_COMMAND_ADD     ? wxT("add-alt") :\r
483                         _eCommand == TPI_COMMAND_EXTRACT ? wxT("extract-alt") : \r
484                         _eCommand == TPI_COMMAND_DELETE  ? wxT("delete-alt") : \r
485                         _eCommand == TPI_COMMAND_UPDATE  ? wxT("update-alt") : \r
486                         _eCommand == TPI_COMMAND_TEST    ? wxT("test-alt") : \r
487                         _eCommand == TPI_COMMAND_REPAIR  ? wxT("repair-alt") : \r
488                         _eCommand == TPI_COMMAND_MOVE    ? wxT("move-alt") : \r
489                         _eCommand == TPI_COMMAND_SFX     ? wxT("sfx-alt") : \r
490                         _eCommand == TPI_COMMAND_UNSFX   ? wxT("unsfx-alt") : wxEmptyString, & szCommandLine);\r
491         }\r
492 \r
493         if (szCommandLine.IsEmpty())\r
494         {\r
495                 return TPI_ERROR_U_USE_LIBRARY;\r
496         }\r
497 \r
498         // コマンドライン・レスポンスファイル作成。\r
499         wxString\r
500                 szResponceFileName = MakeResponceFile(_szFiles, g_LibInfo.node.GetAttribute(wxT("quote-resp"), wxT("1")) == wxT("1")),\r
501                 szCommandLineSend  = MakeCommandLineSend(szCommandLine, _szArcName, _swInfo, _szFiles, szResponceFileName);\r
502 \r
503         // コマンドライン実行。\r
504         wxString szOutput;\r
505         int nErrorCode = myExecute(g_LibInfo.szExeFile + wxT(" ") + szCommandLineSend, & szOutput, _swInfo->fnDestinationDirectory.GetFullPath());\r
506 \r
507         // レスポンスファイル削除。\r
508         ::wxRemoveFile(szResponceFileName);\r
509 \r
510         if (nErrorCode != TPI_ERROR_SUCCESS)\r
511         {\r
512                 wxLogError(L"Error :\n%x\n\nCommandLine:\n%s\n\nOutput:\n%s", nErrorCode, szCommandLineSend.c_str(), szOutput.c_str());\r
513         }\r
514         return nErrorCode;\r
515 }\r
516 \r
517 int __stdcall SetCallbackProc\r
518 (\r
519         TPI_PROC _prArcProc\r
520 )\r
521 {\r
522         // ポインタを保存。\r
523         if (_prArcProc == NULL)\r
524         {\r
525                 return TPI_ERROR_D_PARAMETER;\r
526         }\r
527         g_prProc = * _prArcProc;\r
528 \r
529         return TPI_ERROR_SUCCESS;\r
530 }\r
531 \r
532 #ifdef __cplusplus\r
533 }\r
534 #endif\r