--- /dev/null
+// SoftEther UT-VPN SourceCode\r
+// \r
+// Copyright (C) 2004-2010 SoftEther Corporation.\r
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.\r
+// Copyright (C) 2003-2010 Daiyuu Nobori.\r
+// All Rights Reserved.\r
+// \r
+// http://utvpn.tsukuba.ac.jp/\r
+// \r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// version 2 as published by the Free Software Foundation.\r
+// \r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+// \r
+// You should have received a copy of the GNU General Public License version 2\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
+// \r
+// このファイルは GPL バージョン 2 ライセンスで公開されています。\r
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布\r
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示\r
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の\r
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。\r
+// \r
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の\r
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )\r
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって\r
+// ホストされています。\r
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、\r
+// および、試験または研究のために利用が行われることを想定して配布\r
+// しています。\r
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に\r
+// あります。\r
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード\r
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して\r
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して\r
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース\r
+// に組み込みさせていただきます。\r
+// \r
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する\r
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。\r
+// \r
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社\r
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。\r
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの\r
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意\r
+// ください。\r
+// \r
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の\r
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、\r
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの\r
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社\r
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、\r
+// 公益保護にご協力いただきますようお願い申し上げます。\r
+// \r
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を\r
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客\r
+// を保護するための努力を行います。\r
+// \r
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/\r
+// 日本国内の脆弱性情報届出受付公的機関:\r
+// 独立行政法人 情報処理推進機構\r
+// http://www.ipa.go.jp/security/vuln/report/\r
+// \r
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。\r
+// 連絡先: http://www.softether.co.jp/jp/contact/\r
+\r
+// -----------------------------------------------\r
+// [ChangeLog]\r
+// 2010.05.20\r
+// 新規リリース by SoftEther\r
+// -----------------------------------------------\r
+\r
+// Pack.c\r
+// データパッケージコード\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <wchar.h>\r
+#include <stdarg.h>\r
+#include <time.h>\r
+#include <errno.h>\r
+#include <Mayaqua/Mayaqua.h>\r
+\r
+// BUF を PACK に変換\r
+PACK *BufToPack(BUF *b)\r
+{\r
+ PACK *p;\r
+ // 引数チェック\r
+ if (b == NULL)\r
+ {\r
+ return NULL;\r
+ }\r
+\r
+ p = NewPack();\r
+ if (ReadPack(b, p) == false)\r
+ {\r
+ FreePack(p);\r
+ return NULL;\r
+ }\r
+\r
+ return p;\r
+}\r
+\r
+// PACK を BUF に変換\r
+BUF *PackToBuf(PACK *p)\r
+{\r
+ BUF *b;\r
+ // 引数チェック\r
+ if (p == NULL)\r
+ {\r
+ return NULL;\r
+ }\r
+\r
+ b = NewBuf();\r
+ WritePack(b, p);\r
+\r
+ return b;\r
+}\r
+\r
+// PACK を読み込む\r
+bool ReadPack(BUF *b, PACK *p)\r
+{\r
+ UINT i, num;\r
+ // 引数チェック\r
+ if (b == NULL || p == NULL)\r
+ {\r
+ return false;\r
+ }\r
+\r
+ // ELEMENT 数\r
+ num = ReadBufInt(b);\r
+ if (num > MAX_ELEMENT_NUM)\r
+ {\r
+ // 個数オーバー\r
+ return false;\r
+ }\r
+\r
+ // ELEMENT を読み込む\r
+ for (i = 0;i < num;i++)\r
+ {\r
+ ELEMENT *e;\r
+ e = ReadElement(b);\r
+ if (AddElement(p, e) == false)\r
+ {\r
+ // 追加エラー\r
+ return false;\r
+ }\r
+ }\r
+\r
+ return true;\r
+}\r
+\r
+// PACK を書き出す\r
+void WritePack(BUF *b, PACK *p)\r
+{\r
+ UINT i;\r
+ // 引数チェック\r
+ if (b == NULL || p == NULL)\r
+ {\r
+ return;\r
+ }\r
+\r
+ // ELEMENT 数\r
+ WriteBufInt(b, LIST_NUM(p->elements));\r
+\r
+ // ELEMENT を書き出す\r
+ for (i = 0;i < LIST_NUM(p->elements);i++)\r
+ {\r
+ ELEMENT *e = LIST_DATA(p->elements, i);\r
+ WriteElement(b, e);\r
+ }\r
+}\r
+\r
+// ELEMENT を読み込む\r
+ELEMENT *ReadElement(BUF *b)\r
+{\r
+ UINT i;\r
+ char name[MAX_ELEMENT_NAME_LEN + 1];\r
+ UINT type, num_value;\r
+ VALUE **values;\r
+ ELEMENT *e;\r
+ // 引数チェック\r
+ if (b == NULL)\r
+ {\r
+ return NULL;\r
+ }\r
+\r
+ // 名前\r
+ if (ReadBufStr(b, name, sizeof(name)) == false)\r
+ {\r
+ return NULL;\r
+ }\r
+\r
+ // 項目の種類\r
+ type = ReadBufInt(b);\r
+\r
+ // 項目数\r
+ num_value = ReadBufInt(b);\r
+ if (num_value > MAX_VALUE_NUM)\r
+ {\r
+ // 個数オーバー\r
+ return NULL;\r
+ }\r
+\r
+ // VALUE\r
+ values = (VALUE **)Malloc(sizeof(VALUE *) * num_value);\r
+ for (i = 0;i < num_value;i++)\r
+ {\r
+ values[i] = ReadValue(b, type);\r
+ }\r
+\r
+ // ELEMENT を作成\r
+ e = NewElement(name, type, num_value, values);\r
+\r
+ Free(values);\r
+\r
+ return e;\r
+}\r
+\r
+// ELEMENT を書き出す\r
+void WriteElement(BUF *b, ELEMENT *e)\r
+{\r
+ UINT i;\r
+ // 引数チェック\r
+ if (b == NULL || e == NULL)\r
+ {\r
+ return;\r
+ }\r
+\r
+ // 名前\r
+ WriteBufStr(b, e->name);\r
+ // 項目の種類\r
+ WriteBufInt(b, e->type);\r
+ // 項目数\r
+ WriteBufInt(b, e->num_value);\r
+ // VALUE\r
+ for (i = 0;i < e->num_value;i++)\r
+ {\r
+ VALUE *v = e->values[i];\r
+ WriteValue(b, v, e->type);\r
+ }\r
+}\r
+\r
+// VALUE を読み込む\r
+VALUE *ReadValue(BUF *b, UINT type)\r
+{\r
+ UINT len;\r
+ BYTE *u;\r
+ void *data;\r
+ char *str;\r
+ wchar_t *unistr;\r
+ UINT unistr_size;\r
+ UINT size;\r
+ UINT u_size;\r
+ VALUE *v = NULL;\r
+ // 引数チェック\r
+ if (b == NULL)\r
+ {\r
+ return NULL;\r
+ }\r
+\r
+ // データ項目\r
+ switch (type)\r
+ {\r
+ case VALUE_INT: // 整数\r
+ v = NewIntValue(ReadBufInt(b));\r
+ break;\r
+ case VALUE_INT64:\r
+ v = NewInt64Value(ReadBufInt64(b));\r
+ break;\r
+ case VALUE_DATA: // データ\r
+ size = ReadBufInt(b);\r
+ if (size > MAX_VALUE_SIZE)\r
+ {\r
+ // サイズオーバー\r
+ break;\r
+ }\r
+ data = Malloc(size);\r
+ if (ReadBuf(b, data, size) != size)\r
+ {\r
+ // 読み込み失敗\r
+ Free(data);\r
+ break;\r
+ }\r
+ v = NewDataValue(data, size);\r
+ Free(data);\r
+ break;\r
+ case VALUE_STR: // ANSI 文字列\r
+ len = ReadBufInt(b);\r
+ if ((len + 1) > MAX_VALUE_SIZE)\r
+ {\r
+ // サイズオーバー\r
+ break;\r
+ }\r
+ str = Malloc(len + 1);\r
+ // 文字列本体\r
+ if (ReadBuf(b, str, len) != len)\r
+ {\r
+ // 読み込み失敗\r
+ Free(str);\r
+ break;\r
+ }\r
+ str[len] = 0;\r
+ v = NewStrValue(str);\r
+ Free(str);\r
+ break;\r
+ case VALUE_UNISTR: // Unicode 文字列\r
+ u_size = ReadBufInt(b);\r
+ if (u_size > MAX_VALUE_SIZE)\r
+ {\r
+ // サイズオーバー\r
+ break;\r
+ }\r
+ // UTF-8 の読み込み\r
+ u = ZeroMalloc(u_size + 1);\r
+ if (ReadBuf(b, u, u_size) != u_size)\r
+ {\r
+ // 読み込み失敗\r
+ Free(u);\r
+ break;\r
+ }\r
+ // Unicode 文字列に変換\r
+ unistr_size = CalcUtf8ToUni(u, u_size);\r
+ if (unistr_size == 0)\r
+ {\r
+ Free(u);\r
+ break;\r
+ }\r
+ unistr = Malloc(unistr_size);\r
+ Utf8ToUni(unistr, unistr_size, u, u_size);\r
+ Free(u);\r
+ v = NewUniStrValue(unistr);\r
+ Free(unistr);\r
+ break;\r
+ }\r
+\r
+ return v;\r
+}\r
+\r
+// VALUE を書き出す\r
+void WriteValue(BUF *b, VALUE *v, UINT type)\r
+{\r
+ UINT len;\r
+ BYTE *u;\r
+ UINT u_size;\r
+ // 引数チェック\r
+ if (b == NULL || v == NULL)\r
+ {\r
+ return;\r
+ }\r
+\r
+ // データ項目\r
+ switch (type)\r
+ {\r
+ case VALUE_INT: // 整数\r
+ WriteBufInt(b, v->IntValue);\r
+ break;\r
+ case VALUE_INT64: // 64 bit 整数\r
+ WriteBufInt64(b, v->Int64Value);\r
+ break;\r
+ case VALUE_DATA: // データ\r
+ // サイズ\r
+ WriteBufInt(b, v->Size);\r
+ // 本体\r
+ WriteBuf(b, v->Data, v->Size);\r
+ break;\r
+ case VALUE_STR: // ANSI 文字列\r
+ len = StrLen(v->Str);\r
+ // 長さ\r
+ WriteBufInt(b, len);\r
+ // 文字列本体\r
+ WriteBuf(b, v->Str, len);\r
+ break;\r
+ case VALUE_UNISTR: // Unicode 文字列\r
+ // UTF-8 に変換する\r
+ u_size = CalcUniToUtf8(v->UniStr) + 1;\r
+ u = ZeroMalloc(u_size);\r
+ UniToUtf8(u, u_size, v->UniStr);\r
+ // サイズ\r
+ WriteBufInt(b, u_size);\r
+ // UTF-8 文字列本体\r
+ WriteBuf(b, u, u_size);\r
+ Free(u);\r
+ break;\r
+ }\r
+}\r
+\r
+// データサイズの取得\r
+UINT GetDataValueSize(ELEMENT *e, UINT index)\r
+{\r
+ // 引数チェック\r
+ if (e == NULL)\r
+ {\r
+ return 0;\r
+ }\r
+ if (e->values == NULL)\r
+ {\r
+ return 0;\r
+ }\r
+ if (index >= e->num_value)\r
+ {\r
+ return 0;\r
+ }\r
+ if (e->values[index] == NULL)\r
+ {\r
+ return 0;\r
+ }\r
+\r
+ return e->values[index]->Size;\r
+}\r
+\r
+// データの取得\r
+void *GetDataValue(ELEMENT *e, UINT index)\r
+{\r
+ // 引数チェック\r
+ if (e == NULL)\r
+ {\r
+ return NULL;\r
+ }\r
+ if (e->values == NULL)\r
+ {\r
+ return NULL;\r
+ }\r
+ if (index >= e->num_value)\r
+ {\r
+ return NULL;\r
+ }\r
+ if (e->values[index] == NULL)\r
+ {\r
+ return NULL;\r
+ }\r
+\r
+ return e->values[index]->Data;\r
+}\r
+\r
+// Unicode 文字列型の取得\r
+wchar_t *GetUniStrValue(ELEMENT *e, UINT index)\r
+{\r
+ // 引数チェック\r
+ if (e == NULL)\r
+ {\r
+ return 0;\r
+ }\r
+ if (index >= e->num_value)\r
+ {\r
+ return 0;\r
+ }\r
+\r
+ return e->values[index]->UniStr;\r
+}\r
+\r
+// ANSI 文字列型の取得\r
+char *GetStrValue(ELEMENT *e, UINT index)\r
+{\r
+ // 引数チェック\r
+ if (e == NULL)\r
+ {\r
+ return 0;\r
+ }\r
+ if (index >= e->num_value)\r
+ {\r
+ return 0;\r
+ }\r
+\r
+ return e->values[index]->Str;\r
+}\r
+\r
+// 64 bit 整数型値の取得\r
+UINT64 GetInt64Value(ELEMENT *e, UINT index)\r
+{\r
+ // 引数チェック\r
+ if (e == NULL)\r
+ {\r
+ return 0;\r
+ }\r
+ if (index >= e->num_value)\r
+ {\r
+ return 0;\r
+ }\r
+\r
+ return e->values[index]->Int64Value;\r
+}\r
+\r
+// 整数型値の取得\r
+UINT GetIntValue(ELEMENT *e, UINT index)\r
+{\r
+ // 引数チェック\r
+ if (e == NULL)\r
+ {\r
+ return 0;\r
+ }\r
+ if (index >= e->num_value)\r
+ {\r
+ return 0;\r
+ }\r
+\r
+ return e->values[index]->IntValue;\r
+}\r
+\r
+// PACK のソート関数\r
+int ComparePackName(void *p1, void *p2)\r
+{\r
+ ELEMENT *o1, *o2;\r
+ if (p1 == NULL || p2 == NULL)\r
+ {\r
+ return 0;\r
+ }\r
+ o1 = *(ELEMENT **)p1;\r
+ o2 = *(ELEMENT **)p2;\r
+ if (o1 == NULL || o2 == NULL)\r
+ {\r
+ return 0;\r
+ }\r
+\r
+ return StrCmpi(o1->name, o2->name);\r
+}\r
+\r
+// VALUE の削除\r
+void FreeValue(VALUE *v, UINT type)\r
+{\r
+ // 引数チェック\r
+ if (v == NULL)\r
+ {\r
+ return;\r
+ }\r
+\r
+ switch (type)\r
+ {\r
+ case VALUE_INT:\r
+ case VALUE_INT64:\r
+ break;\r
+ case VALUE_DATA:\r
+ Free(v->Data);\r
+ break;\r
+ case VALUE_STR:\r
+ Free(v->Str);\r
+ break;\r
+ case VALUE_UNISTR:\r
+ Free(v->UniStr);\r
+ break;\r
+ }\r
+\r
+ // メモリ解放\r
+ Free(v);\r
+}\r
+\r
+// Unicode 文字列型の VALUE の作成\r
+VALUE *NewUniStrValue(wchar_t *str)\r
+{\r
+ VALUE *v;\r
+ // 引数チェック\r
+ if (str == NULL)\r
+ {\r
+ return NULL;\r
+ }\r
+\r
+ // メモリ確保\r
+ v = Malloc(sizeof(VALUE));\r
+\r
+ // 文字列コピー\r
+ v->Size = UniStrSize(str);\r
+ v->UniStr = Malloc(v->Size);\r
+ UniStrCpy(v->UniStr, v->Size, str);\r
+\r
+ UniTrim(v->UniStr);\r
+\r
+ return v;\r
+}\r
+\r
+// ANSI 文字列型の VALUE の作成\r
+VALUE *NewStrValue(char *str)\r
+{\r
+ VALUE *v;\r
+ // 引数チェック\r
+ if (str == NULL)\r
+ {\r
+ return NULL;\r
+ }\r
+\r
+ // メモリ確保\r
+ v = Malloc(sizeof(VALUE));\r
+\r
+ // 文字列コピー\r
+ v->Size = StrLen(str) + 1;\r
+ v->Str = Malloc(v->Size);\r
+ StrCpy(v->Str, v->Size, str);\r
+\r
+ Trim(v->Str);\r
+\r
+ return v;\r
+}\r
+\r
+// データ型の VALUE の作成\r
+VALUE *NewDataValue(void *data, UINT size)\r
+{\r
+ VALUE *v;\r
+ // 引数チェック\r
+ if (data == NULL)\r
+ {\r
+ return NULL;\r
+ }\r
+\r
+ // メモリ確保\r
+ v = Malloc(sizeof(VALUE));\r
+\r
+ // データコピー\r
+ v->Size = size;\r
+ v->Data = Malloc(v->Size);\r
+ Copy(v->Data, data, size);\r
+\r
+ return v;\r
+}\r
+\r
+// 64 bit 整数型の VALUE の作成\r
+VALUE *NewInt64Value(UINT64 i)\r
+{\r
+ VALUE *v;\r
+\r
+ v = Malloc(sizeof(VALUE));\r
+ v->Int64Value = i;\r
+ v->Size = sizeof(UINT64);\r
+\r
+ return v;\r
+}\r
+\r
+// 整数型の VALUE の作成\r
+VALUE *NewIntValue(UINT i)\r
+{\r
+ VALUE *v;\r
+\r
+ // メモリ確保\r
+ v = Malloc(sizeof(VALUE));\r
+ v->IntValue = i;\r
+ v->Size = sizeof(UINT);\r
+\r
+ return v;\r
+}\r
+\r
+// ELEMENT の削除\r
+void FreeElement(ELEMENT *e)\r
+{\r
+ UINT i;\r
+ // 引数チェック\r
+ if (e == NULL)\r
+ {\r
+ return;\r
+ }\r
+\r
+ for (i = 0;i < e->num_value;i++)\r
+ {\r
+ FreeValue(e->values[i], e->type);\r
+ }\r
+ Free(e->values);\r
+\r
+ Free(e);\r
+}\r
+\r
+// ELEMENT の作成\r
+ELEMENT *NewElement(char *name, UINT type, UINT num_value, VALUE **values)\r
+{\r
+ ELEMENT *e;\r
+ UINT i;\r
+ // 引数チェック\r
+ if (name == NULL || num_value == 0 || values == NULL)\r
+ {\r
+ return NULL;\r
+ }\r
+\r
+ // メモリ確保\r
+ e = Malloc(sizeof(ELEMENT));\r
+ StrCpy(e->name, sizeof(e->name), name);\r
+ e->num_value = num_value;\r
+ e->type = type;\r
+\r
+ // 要素へのポインタリストのコピー\r
+ e->values = (VALUE **)Malloc(sizeof(VALUE *) * num_value);\r
+ for (i = 0;i < e->num_value;i++)\r
+ {\r
+ e->values[i] = values[i];\r
+ }\r
+\r
+ return e;\r
+}\r
+\r
+// PACK から ELEMENT を検索して取得\r
+ELEMENT *GetElement(PACK *p, char *name, UINT type)\r
+{\r
+ ELEMENT t;\r
+ ELEMENT *e;\r
+ // 引数チェック\r
+ if (p == NULL || name == NULL)\r
+ {\r
+ return NULL;\r
+ }\r
+\r
+ // 検索\r
+ StrCpy(t.name, sizeof(t.name), name);\r
+ e = Search(p->elements, &t);\r
+\r
+ if (e == NULL)\r
+ {\r
+ return NULL;\r
+ }\r
+\r
+ // 型検査\r
+ if (type != INFINITE)\r
+ {\r
+ if (e->type != type)\r
+ {\r
+ return NULL;\r
+ }\r
+ }\r
+\r
+ return e;\r
+}\r
+\r
+// PACK から ELEMENT を削除\r
+void DelElement(PACK *p, char *name)\r
+{\r
+ ELEMENT *e;\r
+ // 引数チェック\r
+ if (p == NULL || name == NULL)\r
+ {\r
+ return;\r
+ }\r
+\r
+ e = GetElement(p, name, INFINITE);\r
+ if (e != NULL)\r
+ {\r
+ Delete(p->elements, e);\r
+\r
+ FreeElement(e);\r
+ }\r
+}\r
+\r
+// PACK に ELEMENT を追加\r
+bool AddElement(PACK *p, ELEMENT *e)\r
+{\r
+ // 引数チェック\r
+ if (p == NULL || e == NULL)\r
+ {\r
+ return false;\r
+ }\r
+\r
+ // サイズチェック\r
+ if (LIST_NUM(p->elements) >= MAX_ELEMENT_NUM)\r
+ {\r
+ // これ以上追加できない\r
+ FreeElement(e);\r
+ return false;\r
+ }\r
+\r
+ // 同じ名前が存在しないかどうかチェック\r
+ if (GetElement(p, e->name, INFINITE))\r
+ {\r
+ // 存在している\r
+ FreeElement(e);\r
+ return false;\r
+ }\r
+\r
+ if (e->num_value == 0)\r
+ {\r
+ // 項目が 1 つも存在していない VALUE は追加できない\r
+ FreeElement(e);\r
+ return false;\r
+ }\r
+\r
+ // 追加\r
+ Add(p->elements, e);\r
+ return true;\r
+}\r
+\r
+// PACK オブジェクトの解放\r
+void FreePack(PACK *p)\r
+{\r
+ UINT i;\r
+ ELEMENT **elements;\r
+ // 引数チェック\r
+ if (p == NULL)\r
+ {\r
+ return;\r
+ }\r
+\r
+ elements = ToArray(p->elements);\r
+ for (i = 0;i < LIST_NUM(p->elements);i++)\r
+ {\r
+ FreeElement(elements[i]);\r
+ }\r
+ Free(elements);\r
+\r
+ ReleaseList(p->elements);\r
+ Free(p);\r
+}\r
+\r
+// PACK オブジェクトの作成\r
+PACK *NewPack()\r
+{\r
+ PACK *p;\r
+\r
+ // メモリ確保\r
+ p = MallocEx(sizeof(PACK), true);\r
+\r
+ // リスト作成\r
+ p->elements = NewListFast(ComparePackName);\r
+\r
+ return p;\r
+}\r
+\r
+\r