librazor/dump.c
author J. Ali Harlow <ali@juiblex.co.uk>
Tue Apr 24 19:27:29 2018 +0100 (2018-04-24)
changeset 496 203fa998c6df
parent 457 51a084acef49
permissions -rw-r--r--
Support expat v2.2
ali@457
     1
/*
ali@475
     2
 * Copyright (C) 2014, 2016  J. Ali Harlow <ali@juiblex.co.uk>
ali@457
     3
 *
ali@457
     4
 * This program is free software; you can redistribute it and/or modify
ali@457
     5
 * it under the terms of the GNU General Public License as published by
ali@457
     6
 * the Free Software Foundation; either version 2 of the License, or
ali@457
     7
 * (at your option) any later version.
ali@457
     8
 *
ali@457
     9
 * This program is distributed in the hope that it will be useful,
ali@457
    10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
ali@457
    11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
ali@457
    12
 * GNU General Public License for more details.
ali@457
    13
 *
ali@457
    14
 * You should have received a copy of the GNU General Public License along
ali@457
    15
 * with this program; if not, write to the Free Software Foundation, Inc.,
ali@457
    16
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
ali@457
    17
 */
ali@457
    18
ali@457
    19
#include "config.h"
ali@457
    20
ali@457
    21
#include <stdlib.h>
ali@457
    22
#include <stdio.h>
ali@457
    23
#include <string.h>
ali@457
    24
ali@457
    25
#include "razor.h"
ali@457
    26
#include "razor-internal.h"
ali@457
    27
ali@457
    28
static size_t
ali@457
    29
dump_raw(FILE *fp, void *raw, char *what, size_t length, size_t offset)
ali@457
    30
{
ali@457
    31
	size_t i, n = 0;
ali@457
    32
	unsigned char *bytes = raw;
ali@457
    33
ali@457
    34
	while (length) {
ali@457
    35
		fprintf(fp, "%05lX %s", (unsigned long)(offset + n), what);
ali@457
    36
		for (i = 0; (i < 16) && (length > 0);) {
ali@457
    37
			if (length >= 4) {
ali@457
    38
				fprintf(fp, " %08X", *(uint32_t *)bytes);
ali@457
    39
				bytes += 4;
ali@457
    40
				n += 4;
ali@457
    41
				length -= 4;
ali@457
    42
				i += 4;
ali@457
    43
			} else if (length >= 2) {
ali@457
    44
				fprintf(fp, " %04X", *(unsigned short *)bytes);
ali@457
    45
				bytes += 2;
ali@457
    46
				n += 2;
ali@457
    47
				length -= 2;
ali@457
    48
				i += 2;
ali@457
    49
			} else {
ali@457
    50
				fprintf(fp, " %02X", *bytes);
ali@457
    51
				bytes++;
ali@457
    52
				n++;
ali@457
    53
				length--;
ali@457
    54
				i++;
ali@457
    55
			}
ali@457
    56
		}
ali@457
    57
		fprintf(fp, "\n");
ali@457
    58
	}
ali@457
    59
ali@457
    60
	return n;
ali@457
    61
}
ali@457
    62
ali@457
    63
static size_t
ali@457
    64
dump_string(FILE *fp, char *pool, size_t length, size_t offset)
ali@457
    65
{
ali@457
    66
	size_t n = 0;
ali@457
    67
	unsigned char c;
ali@457
    68
ali@457
    69
	fprintf(fp, "\"");
ali@457
    70
	while (offset + n < length) {
ali@457
    71
		c = pool[offset + n++];
ali@457
    72
		if (c == '\0') {
ali@457
    73
			fprintf(fp, "\\0");
ali@457
    74
			break;
ali@457
    75
		}
ali@457
    76
		else if (c == '\\' || c == '"')
ali@457
    77
			fprintf(fp, "\\%c", c);
ali@457
    78
		else if (c >= ' ' && c <= '~')
ali@457
    79
			fprintf(fp, "%c", c);
ali@457
    80
		else
ali@457
    81
			fprintf(fp, "\\x%02X", c);
ali@457
    82
	}
ali@457
    83
	fprintf(fp, "\"");
ali@457
    84
ali@457
    85
	return n;
ali@457
    86
}
ali@457
    87
ali@457
    88
static size_t
ali@457
    89
dump_pool(FILE *fp, char *pool, char *what, size_t length, size_t offset)
ali@457
    90
{
ali@457
    91
	size_t n = 0;
ali@457
    92
ali@457
    93
	fprintf(fp, "%05lX %s {\n", (unsigned long)offset, what);
ali@457
    94
ali@457
    95
	while (n < length) {
ali@457
    96
		fprintf(fp, "    %08X ", (uint32_t)n);
ali@457
    97
		n += dump_string(fp, pool, length, n);
ali@457
    98
		fprintf(fp, "\n");
ali@457
    99
	}
ali@457
   100
ali@457
   101
	fprintf(fp, "}\n");
ali@457
   102
ali@457
   103
	return n;
ali@457
   104
}
ali@457
   105
ali@457
   106
static size_t
ali@457
   107
dump_header(FILE *fp, struct razor_set_header *header, size_t length,
ali@457
   108
	    size_t offset)
ali@457
   109
{
ali@457
   110
	if (length >= sizeof(*header)) {
ali@457
   111
		fprintf(fp, "%05lX header ", (unsigned long)offset);
ali@457
   112
		fprintf(fp, "{ magic=%08X version=%08X num_sections=%08X }\n",
ali@457
   113
			header->magic, header->version, header->num_sections);
ali@457
   114
		return sizeof(*header);
ali@457
   115
	} else
ali@457
   116
		return dump_raw(fp, header, "unknown", length, offset);
ali@457
   117
}
ali@457
   118
ali@457
   119
static size_t
ali@457
   120
dump_section(FILE *fp, struct razor_set_section *section, size_t length,
ali@457
   121
	     size_t offset, int indx)
ali@457
   122
{
ali@457
   123
	if (length >= sizeof(*section)) {
ali@457
   124
		fprintf(fp, "%05lX section %X ", (unsigned long)offset, indx);
ali@457
   125
		fprintf(fp, "{ name=%08X offset=%08X size=%08X }\n",
ali@457
   126
			section->name, section->offset, section->size);
ali@457
   127
		return sizeof(*section);
ali@457
   128
	} else
ali@457
   129
		return dump_raw(fp, section, "unknown", length, offset);
ali@457
   130
}
ali@457
   131
ali@457
   132
static void
ali@457
   133
dump_list_head(FILE *fp, struct list_head *head)
ali@457
   134
{
ali@457
   135
	fprintf(fp, "{ list_ptr=%06X flags=%02X ",
ali@457
   136
		head->list_ptr, head->flags);
ali@457
   137
	if (head->flags == RAZOR_EMPTY_LIST)
ali@457
   138
		fprintf(fp, "(EMPTY_LIST) ");
ali@457
   139
	else if (head->flags == RAZOR_IMMEDIATE)
ali@457
   140
		fprintf(fp, "(IMMEDIATE) ");
ali@457
   141
	fprintf(fp,"}");
ali@457
   142
}
ali@457
   143
ali@457
   144
static void
ali@457
   145
dump_list_link(FILE *fp, struct list *list)
ali@457
   146
{
ali@457
   147
	fprintf(fp, "{ data=%06X flags=%02X ",
ali@457
   148
		list->data, list->flags);
ali@457
   149
	if (list->flags == RAZOR_ENTRY_LAST)
ali@457
   150
		fprintf(fp, "(ENTRY_LAST) ");
ali@457
   151
	else if (list->flags == RAZOR_ENTRY_FIRST)
ali@457
   152
		fprintf(fp, "(ENTRY_FIRST) ");
ali@457
   153
	fprintf(fp,"}");
ali@457
   154
}
ali@457
   155
ali@457
   156
static void
ali@457
   157
expand_pooled_string(FILE *fp, struct array *pool, uint32_t str)
ali@457
   158
{
ali@457
   159
	if (pool->data && str < pool->size) {
ali@457
   160
		fprintf(fp, "(");
ali@457
   161
		dump_string(fp, pool->data, pool->size, str);
ali@457
   162
		fprintf(fp, ") ");
ali@457
   163
	}
ali@457
   164
}
ali@457
   165
ali@457
   166
static size_t
ali@457
   167
dump_package(FILE *fp, struct razor_package *package, size_t length,
ali@457
   168
	     size_t offset, struct array *string_pool, int indx)
ali@457
   169
{
ali@457
   170
	if (length >= sizeof(*package)) {
ali@457
   171
		fprintf(fp, "    %05lX package %X ", (unsigned long)offset,
ali@457
   172
			indx);
ali@457
   173
		fprintf(fp, "{ name=%06X ", package->name);
ali@457
   174
		expand_pooled_string(fp, string_pool, package->name);
ali@457
   175
		fprintf(fp, "flags=%02X\n            version=%08X ",
ali@457
   176
			package->flags, package->version);
ali@457
   177
		expand_pooled_string(fp, string_pool, package->version);
ali@457
   178
		fprintf(fp, "arch=%08X ", package->arch);
ali@457
   179
		expand_pooled_string(fp, string_pool, package->arch);
ali@457
   180
		fprintf(fp, "\n            "
ali@457
   181
			"summary=%08X description=%08X url=%08X license=%08X\n",
ali@457
   182
			package->summary, package->description,
ali@457
   183
			package->url, package->license);
ali@457
   184
		fprintf(fp,"            properties=");
ali@457
   185
		dump_list_head(fp, &package->properties);
ali@457
   186
		fprintf(fp,"\n");
ali@457
   187
		fprintf(fp,"            files=");
ali@457
   188
		dump_list_head(fp, &package->files);
ali@457
   189
		fprintf(fp,"\n");
ali@457
   190
		fprintf(fp,"            install_prefixes=");
ali@457
   191
		dump_list_head(fp, &package->install_prefixes);
ali@457
   192
		fprintf(fp,"\n");
ali@457
   193
		fprintf(fp,"            preun={ program=%08X body=%08X }\n",
ali@457
   194
		        package->preun.program, package->preun.body);
ali@457
   195
		fprintf(fp,"            postun={ program=%08X body=%08X }\n",
ali@457
   196
		        package->postun.program, package->postun.body);
ali@457
   197
		fprintf(fp,"    }\n");
ali@457
   198
		return sizeof(*package);
ali@457
   199
	} else
ali@457
   200
		return dump_raw(fp, package, "unknown", length, offset);
ali@457
   201
}
ali@457
   202
ali@457
   203
static size_t
ali@457
   204
dump_packages(FILE *fp, struct razor_package *packages, size_t length,
ali@457
   205
	      size_t offset, struct array *string_pool)
ali@457
   206
{
ali@457
   207
	size_t n = 0;
ali@457
   208
	int i = 0;
ali@457
   209
ali@457
   210
	fprintf(fp, "%05lX packages {\n", (unsigned long)offset);
ali@457
   211
ali@457
   212
	while (n < length) {
ali@457
   213
		n += dump_package(fp, packages, length - n, n, string_pool, i);
ali@457
   214
		packages++;
ali@457
   215
		i++;
ali@457
   216
	}
ali@457
   217
ali@457
   218
	fprintf(fp, "}\n");
ali@457
   219
ali@457
   220
	return n;
ali@457
   221
}
ali@457
   222
ali@457
   223
static size_t
ali@457
   224
dump_property(FILE *fp, struct razor_property *property, size_t length,
ali@457
   225
	      size_t offset, struct array *string_pool)
ali@457
   226
{
ali@457
   227
	if (length >= sizeof(*property)) {
ali@457
   228
		fprintf(fp, "    %05lX property ", (unsigned long)offset);
ali@457
   229
		fprintf(fp, "{ name=%08X ", property->name);
ali@457
   230
		expand_pooled_string(fp, string_pool, property->name);
ali@457
   231
		fprintf(fp, "\n            flags=%08X (", property->flags);
ali@457
   232
		if (property->flags & RAZOR_PROPERTY_LESS)
ali@457
   233
			fprintf(fp, "<");
ali@457
   234
		if (property->flags & RAZOR_PROPERTY_GREATER)
ali@457
   235
			fprintf(fp, ">");
ali@457
   236
		if (property->flags & RAZOR_PROPERTY_EQUAL)
ali@457
   237
			fprintf(fp, "=");
ali@457
   238
		switch (property->flags & RAZOR_PROPERTY_TYPE_MASK) {
ali@457
   239
		case RAZOR_PROPERTY_REQUIRES:
ali@457
   240
			fprintf(fp, " requires");
ali@457
   241
			break;
ali@457
   242
		case RAZOR_PROPERTY_PROVIDES:
ali@457
   243
			fprintf(fp, " provides");
ali@457
   244
			break;
ali@457
   245
		case RAZOR_PROPERTY_CONFLICTS:
ali@457
   246
			fprintf(fp, " conflicts");
ali@457
   247
			break;
ali@457
   248
		case RAZOR_PROPERTY_OBSOLETES:
ali@457
   249
			fprintf(fp, " obsoletes");
ali@457
   250
			break;
ali@457
   251
		}
ali@457
   252
		if (property->flags & RAZOR_PROPERTY_PRE)
ali@457
   253
			fprintf(fp, " pre");
ali@457
   254
		if (property->flags & RAZOR_PROPERTY_POST)
ali@457
   255
			fprintf(fp, " post");
ali@457
   256
		if (property->flags & RAZOR_PROPERTY_PREUN)
ali@457
   257
			fprintf(fp, " preun");
ali@457
   258
		if (property->flags & RAZOR_PROPERTY_POSTUN)
ali@457
   259
			fprintf(fp, " postun");
ali@457
   260
		fprintf(fp, ") version=%08X ", property->version);
ali@457
   261
		expand_pooled_string(fp, string_pool, property->version);
ali@457
   262
		fprintf(fp,"\n            packages=");
ali@457
   263
		dump_list_head(fp, &property->packages);
ali@457
   264
		fprintf(fp,"\n");
ali@457
   265
		fprintf(fp,"    }\n");
ali@457
   266
		return sizeof(*property);
ali@457
   267
	} else
ali@457
   268
		return dump_raw(fp, property, "unknown", length, offset);
ali@457
   269
}
ali@457
   270
ali@457
   271
static size_t
ali@457
   272
dump_properties(FILE *fp, struct razor_property *properties, size_t length,
ali@457
   273
		size_t offset, struct array *string_pool)
ali@457
   274
{
ali@457
   275
	size_t n = 0;
ali@457
   276
ali@457
   277
	fprintf(fp, "%05lX properties {\n", (unsigned long)offset);
ali@457
   278
ali@457
   279
	while (n < length) {
ali@457
   280
		n += dump_property(fp, properties, length - n, n, string_pool);
ali@457
   281
		properties++;
ali@457
   282
	}
ali@457
   283
ali@457
   284
	fprintf(fp, "}\n");
ali@457
   285
ali@457
   286
	return n;
ali@457
   287
}
ali@457
   288
ali@457
   289
static size_t
ali@457
   290
dump_entry(FILE *fp, struct razor_entry *entry, size_t length, size_t offset,
ali@457
   291
	   struct array *string_pool, int indx)
ali@457
   292
{
ali@457
   293
	if (length >= sizeof(*entry)) {
ali@457
   294
		fprintf(fp, "    %05lX entry %X ", (unsigned long)offset, indx);
ali@457
   295
		fprintf(fp, "{ name=%06X ", entry->name);
ali@457
   296
		expand_pooled_string(fp, string_pool, entry->name);
ali@457
   297
		fprintf(fp, "\n            flags=%02X ", entry->flags);
ali@457
   298
		if (entry->flags == RAZOR_ENTRY_LAST)
ali@457
   299
			fprintf(fp, "(LAST) ");
ali@457
   300
		fprintf(fp, "start=%08X", entry->start);
ali@457
   301
		if (!entry->start)
ali@457
   302
			fprintf(fp, " (NONE)");
ali@457
   303
		fprintf(fp,"\n            packages=");
ali@457
   304
		dump_list_head(fp, &entry->packages);
ali@457
   305
		fprintf(fp,"\n");
ali@457
   306
		fprintf(fp,"    }\n");
ali@457
   307
		return sizeof(*entry);
ali@457
   308
	} else
ali@457
   309
		return dump_raw(fp, entry, "unknown", length, offset);
ali@457
   310
}
ali@457
   311
ali@457
   312
static size_t
ali@457
   313
dump_files(FILE *fp, struct razor_entry *entries, size_t length, size_t offset,
ali@457
   314
	   struct array *string_pool)
ali@457
   315
{
ali@457
   316
	size_t n = 0;
ali@457
   317
	int i = 0;
ali@457
   318
ali@457
   319
	fprintf(fp, "%05lX files {\n", (unsigned long)offset);
ali@457
   320
ali@457
   321
	while (n < length) {
ali@457
   322
		n += dump_entry(fp, entries, length - n, n, string_pool, i);
ali@457
   323
		i++;
ali@457
   324
		entries++;
ali@457
   325
	}
ali@457
   326
ali@457
   327
	fprintf(fp, "}\n");
ali@457
   328
ali@457
   329
	return n;
ali@457
   330
}
ali@457
   331
ali@457
   332
static size_t
ali@457
   333
dump_lists(FILE *fp, struct list *lists, char *what, size_t length,
ali@457
   334
	   size_t offset)
ali@457
   335
{
ali@457
   336
	int i = 0;
ali@457
   337
	size_t n = 0;
ali@457
   338
ali@457
   339
	fprintf(fp, "%05lX %s {\n", (unsigned long)offset, what);
ali@457
   340
ali@457
   341
	while (n + sizeof(*lists) <= length) {
ali@457
   342
		fprintf(fp, "    list %06X ", i);
ali@457
   343
		dump_list_link(fp, lists);
ali@457
   344
		fprintf(fp, "\n");
ali@457
   345
		n += sizeof(*lists);
ali@457
   346
		lists++;
ali@457
   347
		i++;
ali@457
   348
	}
ali@457
   349
ali@457
   350
	if (length - n)
ali@457
   351
		n += dump_raw(fp, lists, "unknown", length - n, offset + n);
ali@457
   352
ali@457
   353
	fprintf(fp, "}\n");
ali@457
   354
ali@457
   355
	return n;
ali@457
   356
}
ali@457
   357
ali@457
   358
RAZOR_EXPORT int
ali@475
   359
razor_dump_database(FILE *fp, const char *root_uri, const char *filename,
ali@457
   360
		    struct razor_error **error)
ali@457
   361
{
ali@457
   362
	int i;
ali@475
   363
	char *s, *uri, *data;
ali@457
   364
	struct array pool = { 0, };
ali@457
   365
	struct array string_pool = { 0, };
ali@457
   366
	struct array file_string_pool = { 0, };
ali@457
   367
	struct array details_string_pool = { 0, };
ali@457
   368
	unsigned char *contents;
ali@457
   369
	size_t length, len, n, min_offset;
ali@457
   370
	struct razor_set_header *header;
ali@457
   371
	struct razor_set_section *section, *sections;
ali@457
   372
	struct razor_package *package;
ali@457
   373
	struct razor_property *prop;
ali@457
   374
	struct razor_entry *entry;
ali@457
   375
	struct list *list;
ali@457
   376
ali@457
   377
	if (!filename)
ali@457
   378
		filename = "system.rzdb";
ali@457
   379
ali@475
   380
	uri = razor_resolve_database_file(root_uri, filename, error);
ali@475
   381
ali@475
   382
	if (!uri)
ali@475
   383
		return -1;
ali@475
   384
ali@475
   385
	contents = razor_uri_get_contents(uri, &length, 0, error);
ali@475
   386
ali@475
   387
	free(uri);
ali@457
   388
ali@457
   389
	if (!contents)
ali@457
   390
		return -1;
ali@457
   391
ali@457
   392
	n = 0;
ali@457
   393
ali@457
   394
	header = (void *)contents;
ali@457
   395
ali@457
   396
	if (length - n)
ali@457
   397
		n += dump_header(fp, header, length - n, n);
ali@457
   398
ali@457
   399
	min_offset = length;
ali@457
   400
	sections = (void *)(contents + n);
ali@457
   401
	for (i = 0; i < header->num_sections; i++) {
ali@457
   402
		if (n == length)
ali@457
   403
			break;
ali@457
   404
		section = (void *)(contents + n);
ali@457
   405
		if (section->offset < min_offset)
ali@457
   406
			min_offset = section->offset;
ali@457
   407
		n += dump_section(fp, section, length - n, n, i);
ali@457
   408
	}
ali@457
   409
ali@457
   410
	if (length - n) {
ali@457
   411
		pool.data = (char *)(contents + n);
ali@457
   412
		pool.size = min_offset - n;
ali@457
   413
		n += dump_pool(fp, pool.data, "pool", pool.size, n);
ali@457
   414
	}
ali@457
   415
ali@457
   416
	if ((length - n) && PADDING(n, 4))
ali@457
   417
		n += dump_raw(fp, contents + n, "padding", PADDING(n, 4), n);
ali@457
   418
ali@457
   419
	for (i = 0; length - n && i < header->num_sections; i++) {
ali@457
   420
		if (sections[i].name < pool.size &&
ali@457
   421
		    ((char *)pool.data)[pool.size - 1] == '\0')
ali@457
   422
			s = pool.data + sections[i].name;
ali@457
   423
		else
ali@457
   424
			continue;
ali@457
   425
ali@457
   426
		data = (char *)(contents + sections[i].offset);
ali@457
   427
ali@457
   428
		if (!strcmp(s, "string_pool")) {
ali@457
   429
			string_pool.data = data;
ali@457
   430
			string_pool.size = sections[i].size;
ali@457
   431
		} else if (!strcmp(s, "file_string_pool")) {
ali@457
   432
			file_string_pool.data = data;
ali@457
   433
			file_string_pool.size = sections[i].size;
ali@457
   434
		} else if (!strcmp(s, "details_string_pool")) {
ali@457
   435
			details_string_pool.data = data;
ali@457
   436
			details_string_pool.size = sections[i].size;
ali@457
   437
		}
ali@457
   438
	}
ali@457
   439
ali@457
   440
	for (i = 0; i < header->num_sections; i++) {
ali@457
   441
		if (n == length)
ali@457
   442
			break;
ali@457
   443
		section = sections + i;
ali@457
   444
		if (section->offset != n)
ali@457
   445
			continue;
ali@457
   446
		len = section->size;
ali@457
   447
		if (len > length - n)
ali@457
   448
			len = length - n;
ali@457
   449
ali@457
   450
		if (section->name < pool.size &&
ali@457
   451
		    ((char *)pool.data)[pool.size - 1] == '\0')
ali@457
   452
			s = pool.data + section->name;
ali@457
   453
		else
ali@457
   454
			s = "unknown";
ali@457
   455
ali@457
   456
		if (!strcmp(s, "string_pool") ||
ali@457
   457
		    !strcmp(s, "file_string_pool") ||
ali@457
   458
		    !strcmp(s, "details_string_pool"))
ali@457
   459
			n += dump_pool(fp, (char *)(contents + n), s, len, n);
ali@457
   460
		else if (!strcmp(s, "packages")) {
ali@457
   461
			package = (void *)(contents + n);
ali@457
   462
			n += dump_packages(fp, package, len, n, &string_pool);
ali@457
   463
		} else if (!strcmp(s, "properties")) {
ali@457
   464
			prop = (void *)(contents + n);
ali@457
   465
			n += dump_properties(fp, prop, len, n, &string_pool);
ali@457
   466
		} else if (!strcmp(s, "files")) {
ali@457
   467
			entry = (void *)(contents + n);
ali@457
   468
			n += dump_files(fp, entry, len, n, &file_string_pool);
ali@457
   469
		} else if (!strcmp(s, "package_pool") ||
ali@457
   470
			   !strcmp(s, "property_pool") ||
ali@457
   471
			   !strcmp(s, "prefix_pool") ||
ali@457
   472
			   !strcmp(s, "file_pool")) {
ali@457
   473
			list = (void *)(contents + n);
ali@457
   474
			n += dump_lists(fp, list, s, len, n);
ali@457
   475
		} else
ali@457
   476
			n += dump_raw(fp, contents + n, s, len, n);
ali@457
   477
ali@457
   478
		if ((length - n) && PADDING(n, 4))
ali@457
   479
			n += dump_raw(fp, contents + n, "padding",
ali@457
   480
				      PADDING(n, 4), n);
ali@457
   481
	}
ali@457
   482
ali@457
   483
	if (length - n)
ali@457
   484
		n += dump_raw(fp, contents + n, "unknown", length - n, n);
ali@457
   485
ali@457
   486
	fprintf(fp, "%05lX EOF\n", (unsigned long)n);
ali@457
   487
ali@475
   488
	razor_uri_free_contents(contents, length);
ali@457
   489
ali@457
   490
	return 0;
ali@457
   491
}