00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #ifdef HAVE_CONFIG_H
00021 # include <config.h>
00022 #endif
00023
00024 #include <stdlib.h>
00025 #include <ctype.h>
00026 #include <string.h>
00027
00028 #include <libxml/xmlmemory.h>
00029 #include <libxml/parser.h>
00030 #include <glib.h>
00031
00032 #include "libuau.h"
00033 #include "util.h"
00034 #include "error.h"
00035 #include "parse.h"
00036 #include "parseupdates.h"
00037
00038 #ifdef WITH_DMALLOC
00039 # include <dmalloc.h>
00040 #endif
00041
00042 G_LOCK_DEFINE_STATIC (parse);
00043
00044 static GPtrArray *updates;
00045 static AUpdate *currUpdate;
00046
00047 static void parseUpdates (xmlDocPtr doc, xmlNodePtr node);
00048 static void parseUpdate (xmlDocPtr doc, xmlNodePtr node, xmlChar *type);
00049
00050 static void parseSoftware (xmlDocPtr doc, xmlNodePtr node);
00051 static void parsePackage (xmlDocPtr doc, xmlNodePtr node);
00052 static void parseMessage (xmlDocPtr doc, xmlNodePtr node);
00053 static void parseLibupdate(xmlDocPtr doc, xmlNodePtr node);
00054 static void parseGenericInfo(xmlDocPtr doc, xmlNodePtr node);
00055 static void parseUpdateInfo(xmlDocPtr doc, xmlNodePtr node, AUpdateType type);
00056
00057 static gboolean parseQuantData(void **data, char *str, AQuantDataType quantDataType);
00058
00059
00060 GPtrArray *
00061 luau_parseUpdateFileXML(char *contents) {
00062 xmlDocPtr doc;
00063 xmlNodePtr node;
00064 xmlChar *interfaceStr;
00065 AInterface xmlInterface, readableInterface;
00066 gboolean result;
00067 GPtrArray *ret;
00068
00069 G_LOCK (parse);
00070
00071 updates = g_ptr_array_new();
00072 doc = xmlParseMemory(contents, strlen(contents));
00073 node = xmlDocGetRootElement(doc);
00074 if (node == NULL) {
00075 ERROR("XML Updates file could not be parsed");
00076 xmlFreeDoc(doc);
00077 return NULL;
00078 }
00079
00080 if (! xmlStrEqual(node->name, (const xmlChar *) "luau-repository")) {
00081 ERROR("XML Updates error: Invalid root element");
00082 xmlFreeDoc(doc);
00083 return NULL;
00084 }
00085
00086 interfaceStr = xmlGetProp(node, "interface");
00087 result = luau_parseInterface(&xmlInterface, interfaceStr);
00088 if (result == FALSE) {
00089 ERROR("XML interface version either not specified or unreadable - will continue, but may not be able to parse");
00090 } else {
00091 readableInterface.major = LUAU_XML_INTERFACE_MAJOR;
00092 readableInterface.minor = LUAU_XML_INTERFACE_MINOR;
00093 if (!luau_satisfiesInterface(&readableInterface, &xmlInterface))
00094 ERROR("Unsupported XML interface specified - will continue, but will most likely encounter errors");
00095 }
00096
00097 parseUpdates(doc, node);
00098
00099 xmlFreeDoc(doc);
00100
00101 ret = updates;
00102
00103 G_UNLOCK (parse);
00104
00105 return ret;
00106 }
00107
00108
00109
00110 static void
00111 parseUpdates(xmlDocPtr doc, xmlNodePtr node) {
00112 xmlChar *type;
00113 gboolean foundProgInfo = FALSE;
00114 for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
00115 if (xmlStrEqual(node->name, (const xmlChar *) "update")) {
00116 type = xmlGetProp(node, "type");
00117 parseUpdate(doc, node, type);
00118 xmlFree(type);
00119 } else if (xmlStrEqual(node->name, (const xmlChar *) "software")) {
00120 parseUpdate(doc, node, "software");
00121 currUpdate->newVersion = xmlGetProp(node, "version");
00122 if (currUpdate->id == NULL)
00123 currUpdate->id = g_strdup(currUpdate->newVersion);
00124 } else if (xmlStrEqual(node->name, (const xmlChar *) "program-info")) {
00125
00126
00127 if (foundProgInfo)
00128 ERROR("Two <program-info> tags found - only one is allowed");
00129 foundProgInfo = TRUE;
00130 } else if (! (xmlStrEqual(node->name, (const xmlChar *) "text")) ) {
00131 ERROR("Only tags allowed in <luau-repository> root tag are <update>, <software>, and <program-info>; found '%s', skipping", (const char*) node->name);
00132 }
00133 }
00134
00135 if (!foundProgInfo)
00136 ERROR("No <program-info> tag found - required by DTD");
00137 }
00138
00139 static void
00140 parseUpdate(xmlDocPtr doc, xmlNodePtr node, xmlChar *type) {
00141
00142 currUpdate = (AUpdate*) g_malloc(sizeof(AUpdate));
00143 memset(currUpdate, 0, sizeof(AUpdate));
00144 currUpdate->keywords = g_ptr_array_new();
00145 currUpdate->packages = g_ptr_array_new();
00146 currUpdate->quantifiers = NULL;
00147 g_ptr_array_add(updates, currUpdate);
00148
00149 if (type == NULL) {
00150 ERROR("No type specified for update: skipping");
00151 }
00152 else if (xmlStrcasecmp(type, "software") == 0) { currUpdate->type = LUAU_SOFTWARE; }
00153 else if (xmlStrcasecmp(type, "message" ) == 0) { currUpdate->type = LUAU_MESSAGE; }
00154 else if (xmlStrcasecmp(type, "luau-config") == 0) { currUpdate->type = LUAU_LIBUPDATE; }
00155 else {
00156 ERROR("Unknown 'update' type specified: %s - Skipping", type);
00157 return;
00158 }
00159 DBUGOUT("Found update of type %s", type);
00160
00161 parseUpdateInfo(doc, node, currUpdate->type);
00162 }
00163
00164 static void
00165 parseUpdateInfo(xmlDocPtr doc, xmlNodePtr node, AUpdateType type) {
00166 parseGenericInfo(doc, node);
00167 if (type == LUAU_SOFTWARE)
00168 parseSoftware(doc, node);
00169 if (type == LUAU_MESSAGE)
00170 parseMessage(doc, node);
00171 else if (type == LUAU_LIBUPDATE)
00172 parseLibupdate(doc, node);
00173 }
00174
00175 static void
00176 parseGenericInfo(xmlDocPtr doc, xmlNodePtr node) {
00177 static const char *symbols[ ] = {"id", "i",
00178 "short", "s",
00179 "long", "l",
00180 "keyword", "k",
00181 "date", "d",
00182 "valid", "v",
00183 NULL };
00184 char result, *temp;
00185 AQuantifier *quant = NULL;
00186 AQuantDataType quantDataType;
00187
00188 for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
00189 result = lutil_parse_parseSymbolArray((const char *)node->name, symbols);
00190 switch (result) {
00191 case 'i':
00192 temp = (char*) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
00193 currUpdate->id = g_strdup(lutil_parse_deleteWhitespace(temp));
00194 xmlFree(temp);
00195 break;
00196 case 's':
00197 temp = (char*) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
00198 currUpdate->shortDesc = g_strdup(lutil_parse_deleteWhitespace(temp));
00199 xmlFree(temp);
00200 break;
00201 case 'l':
00202 temp = (char*) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
00203 currUpdate->fullDesc = g_strdup(lutil_parse_deleteWhitespace(temp));
00204 xmlFree(temp);
00205 break;
00206 case 'k':
00207 temp = (char*) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
00208 g_ptr_array_add(currUpdate->keywords, g_strdup(lutil_parse_deleteWhitespace(temp)));
00209 xmlFree(temp);
00210 break;
00211 case 'd':
00212 temp = (char*) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
00213 currUpdate->date = g_malloc(sizeof(ADate));
00214 luau_parseDate(currUpdate->date, lutil_parse_deleteWhitespace(temp));
00215 xmlFree(temp);
00216 break;
00217 case 'v':
00218 temp = (char*) xmlGetProp(node, "type");
00219
00220 quantDataType = luau_parseQuantDataType(temp);
00221
00222 if (quantDataType == LUAU_QUANT_DATA_INVALID) {
00223 ERROR("Unsupported quantifier data type: '%s'", temp);
00224 xmlFree(temp);
00225 break;
00226 }
00227 xmlFree(temp);
00228
00229 if (currUpdate->quantifiers == NULL)
00230 currUpdate->quantifiers = g_ptr_array_new();
00231
00232 temp = (char*) xmlGetProp(node, "from");
00233 if (temp != NULL) {
00234 quant = g_malloc(sizeof(AQuantifier));
00235 quant->qtype = LUAU_QUANT_FROM;
00236 quant->dtype = quantDataType;
00237 parseQuantData(&(quant->data), temp, quantDataType);
00238
00239 g_ptr_array_add(currUpdate->quantifiers, quant);
00240
00241 xmlFree(temp);
00242 }
00243
00244 temp = (char*) xmlGetProp(node, "to");
00245 if (temp != NULL) {
00246 quant = g_malloc(sizeof(AQuantifier));
00247 quant->qtype = LUAU_QUANT_TO;
00248 quant->dtype = quantDataType;
00249 parseQuantData(&(quant->data), temp, quantDataType);
00250
00251 g_ptr_array_add(currUpdate->quantifiers, quant);
00252
00253 xmlFree(temp);
00254 }
00255
00256 temp = (char*) xmlGetProp(node, "for");
00257 if (temp != NULL) {
00258 quant = g_malloc(sizeof(AQuantifier));
00259 quant->qtype = LUAU_QUANT_FOR;
00260 quant->dtype = quantDataType;
00261 parseQuantData(&(quant->data), temp, quantDataType);
00262
00263 g_ptr_array_add(currUpdate->quantifiers, quant);
00264
00265 xmlFree(temp);
00266 }
00267
00268 break;
00269 }
00270 }
00271 }
00272
00273 static void
00274 parseSoftware(xmlDocPtr doc, xmlNodePtr node) {
00275 static const char *symbols[ ] = {"package", "p",
00276
00277 "interface", "i",
00278 "display-version", "d",
00279 NULL };
00280 char result, *temp;
00281
00282 for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
00283 result = lutil_parse_parseSymbolArray((const char *)node->name, symbols);
00284 switch (result) {
00285 case 'p':
00286 parsePackage(doc, node);
00287 break;
00288 case 'i':
00289 temp = (char*) xmlGetProp(node, "version");
00290 luau_parseInterface(&(currUpdate->interface), temp);
00291 xmlFree(temp);
00292
00293 break;
00294 case 'd':
00295 temp = (char*) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
00296 currUpdate->newDisplayVersion = g_strdup(lutil_parse_deleteWhitespace(temp));
00297 xmlFree(temp);
00298 break;
00299 }
00300 }
00301 }
00302
00303 static void
00304 parsePackage(xmlDocPtr doc, xmlNodePtr node) {
00305 GPtrArray *mirrors;
00306 APackage *pkg;
00307 char *loc, *percentage, *temp;
00308 int i, total;
00309
00310 pkg = (APackage*) g_malloc(sizeof(APackage));
00311 g_ptr_array_add(currUpdate->packages, pkg);
00312
00313 mirrors = g_ptr_array_new();
00314 pkg->mirrors = mirrors;
00315
00316 temp = (char*) xmlGetProp(node, "md5");
00317 if (temp != NULL) {
00318 strncpy(pkg->md5sum, temp, 33);
00319 xmlFree(temp);
00320 } else {
00321 pkg->md5sum[0] = '\0';
00322 }
00323
00324 temp = (char*) xmlGetProp(node, "size");
00325 if (temp != NULL) {
00326 pkg->size = atoi(temp);
00327 xmlFree(temp);
00328 } else {
00329 pkg->size = 0;
00330 }
00331
00332 temp = (char*) xmlGetProp(node, "type");
00333 if (temp != NULL) {
00334 pkg->type = luau_parsePkgType(temp);
00335 xmlFree(temp);
00336 } else {
00337 pkg->type = LUAU_UNKNOWN;
00338 }
00339
00340 currUpdate->availableFormats = (currUpdate->availableFormats | pkg->type);
00341
00342 temp = (char*) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
00343 loc = g_strdup(lutil_parse_deleteWhitespace(temp));
00344 xmlFree(temp);
00345
00346 if (loc == NULL || loc[0] != '\0') {
00347 g_ptr_array_add(mirrors, GINT_TO_POINTER (100));
00348 g_ptr_array_add(mirrors, loc);
00349 } else if (node->xmlChildrenNode != NULL) {
00350 nnull_g_free(loc);
00351
00352 total = 0;
00353 for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
00354 if (lutil_streq(node->name, "mirror")) {
00355 percentage = (char*) xmlGetProp(node, "weight");
00356 if (percentage == NULL) {
00357 DBUGOUT("Weight for mirror not specified - using default");
00358 g_ptr_array_add(mirrors, GINT_TO_POINTER (100));
00359 total += 100;
00360 } else {
00361 i = atoi(percentage);
00362 if (i == 0) i = 1;
00363 g_ptr_array_add(mirrors, GINT_TO_POINTER (i));
00364 xmlFree(percentage);
00365
00366 DBUGOUT("Weight for mirror: %d", i);
00367
00368 total += i;
00369 }
00370
00371 temp = (char*) xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
00372 loc = g_strdup(lutil_parse_deleteWhitespace(temp));
00373 g_ptr_array_add(mirrors, loc);
00374 xmlFree(temp);
00375
00376 DBUGOUT("Mirror location: %s", loc);
00377 } else if (!lutil_streq(node->name, "text")) {
00378 ERROR("Invalid tag '<%s>' in <package> section (only '<mirror>' allowed)", node->name);
00379 }
00380 }
00381
00382 if (total != 100) {
00383 int n, sum;
00384 float factor;
00385
00386 factor = ((float)100 / total);
00387 sum = 0;
00388
00389 DBUGOUT("Rescaling mirror weights by factor of %.01f (total = %d)", factor, total);
00390 for (i = 0; i < mirrors->len; i+=2) {
00391 if (i+2 == mirrors->len)
00392 n = 100 - sum;
00393 else {
00394 n = GPOINTER_TO_INT (g_ptr_array_index(mirrors, i));
00395 n *= factor;
00396 }
00397
00398 mirrors->pdata[i] = GINT_TO_POINTER (n);
00399
00400 sum += n;
00401 }
00402 }
00403
00404 #ifdef DEBUG
00405 for (i = 0; i < pkg->mirrors->len; i += 2)
00406 DBUGOUT("URL: %s; weight: %d\n", (char*)g_ptr_array_index(mirrors, i+1), GPOINTER_TO_INT (g_ptr_array_index(mirrors, i)));
00407 #endif
00408 } else {
00409 nnull_g_free(loc);
00410 ERROR("Package specified in software repository, but no URLs given!");
00411 }
00412 }
00413
00414
00415
00416 static void
00417 parseMessage(xmlDocPtr doc, xmlNodePtr node) {
00418
00419 }
00420
00421 static void
00422 parseLibupdate(xmlDocPtr doc, xmlNodePtr node) {
00423 static const char *symbols[ ] = { "set", "s",
00424 NULL };
00425 char result;
00426
00427 for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
00428 result = lutil_parse_parseSymbolArray((const char *)node->name, symbols);
00429 switch (result) {
00430 case 's':
00431 currUpdate->newURL = (char*) xmlGetProp(node, "url");
00432 break;
00433 }
00434 }
00435 }
00436
00437 static gboolean
00438 parseQuantData(void **data, char *str, AQuantDataType quantDataType) {
00439 gboolean result;
00440
00441 if (quantDataType == LUAU_QUANT_DATA_VERSION || quantDataType == LUAU_QUANT_DATA_KEYWORD) {
00442 *data = g_strdup(str);
00443 result = TRUE;
00444 } else if (quantDataType == LUAU_QUANT_DATA_INTERFACE) {
00445 *data = g_malloc(sizeof(AInterface));
00446 result = luau_parseInterface((AInterface*) *data, str);
00447 } else if (quantDataType == LUAU_QUANT_DATA_DATE) {
00448 *data = g_malloc(sizeof(ADate));
00449 result = luau_parseDate((ADate*) *data, str);
00450 } else {
00451 ERROR("Internal Error: Unrecognized quantifier data type (%d)", quantDataType);
00452 result = FALSE;
00453 }
00454
00455 return result;
00456 }
00457