/[LeafOK_CVS]/pvpgn-1.7.4/src/bnetd/versioncheck.c
ViewVC logotype

Contents of /pvpgn-1.7.4/src/bnetd/versioncheck.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1.1.1 - (show annotations) (vendor branch)
Tue Jun 6 03:41:37 2006 UTC (19 years, 9 months ago) by sysadm
Branch: GNU, MAIN
CVS Tags: pvpgn_1-7-4-0_MIL, arelease, HEAD
Changes since 1.1: +0 -0 lines
Content type: text/x-csrc
Error occurred while calculating annotation data.
no message

1 /*
2 * Copyright (C) 2000 Onlyer (onlyer@263.net)
3 * Copyright (C) 2001 Ross Combs (ross@bnetd.org)
4 * Copyright (C) 2002 Gianluigi Tiesi (sherpya@netfarm.it)
5 * Copyright (C) 2004 CreepLord (creeplord@pvpgn.org)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21 #define VERSIONCHECK_INTERNAL_ACCESS
22 #include "common/setup_before.h"
23 #include <stdio.h>
24 #ifdef HAVE_STDDEF_H
25 # include <stddef.h>
26 #else
27 # ifndef NULL
28 # define NULL ((void *)0)
29 # endif
30 #endif
31 #ifdef STDC_HEADERS
32 # include <stdlib.h>
33 #else
34 # ifdef HAVE_MALLOC_H
35 # include <malloc.h>
36 # endif
37 #endif
38 #include "compat/strtoul.h"
39 #ifdef HAVE_STRING_H
40 # include <string.h>
41 #else
42 # ifdef HAVE_STRINGS_H
43 # include <strings.h>
44 # endif
45 #endif
46 #ifdef HAVE_MKTIME
47 # ifdef TIME_WITH_SYS_TIME
48 # include <sys/time.h>
49 # include <time.h>
50 # else
51 # ifdef HAVE_SYS_TIME_H
52 # include <sys/time.h>
53 # else
54 # include <time.h>
55 # endif
56 # endif
57 #endif
58 #ifdef HAVE_ASSERT_H
59 # include <assert.h>
60 #endif
61 #include "compat/strchr.h"
62 #include "compat/strdup.h"
63 #include "compat/strcasecmp.h"
64 #include <ctype.h>
65 #include <errno.h>
66 #include "compat/strerror.h"
67 #include "common/eventlog.h"
68 #include "common/list.h"
69 #include "common/util.h"
70 #include "common/proginfo.h"
71 #include "common/token.h"
72 #include "common/field_sizes.h"
73 #include "prefs.h"
74 #include "versioncheck.h"
75 #include "common/tag.h"
76 #include "common/xalloc.h"
77 #include "common/setup_after.h"
78
79
80 static t_list * versioninfo_head=NULL;
81 static t_versioncheck dummyvc={ "A=42 B=42 C=42 4 A=A^S B=B^B C=C^C A=A^S", "IX86ver1.mpq", "NoVC" };
82
83 static int versioncheck_compare_exeinfo(t_parsed_exeinfo * pattern, t_parsed_exeinfo * match);
84
85 extern t_versioncheck * versioncheck_create(t_tag archtag, t_tag clienttag)
86 {
87 t_elem const * curr;
88 t_versioninfo * vi;
89 t_versioncheck * vc;
90 char archtag_str[5];
91 char clienttag_str[5];
92
93 LIST_TRAVERSE_CONST(versioninfo_head,curr)
94 {
95 if (!(vi = elem_get_data(curr))) /* should not happen */
96 {
97 eventlog(eventlog_level_error,__FUNCTION__,"version list contains NULL item");
98 continue;
99 }
100
101 eventlog(eventlog_level_debug,__FUNCTION__,"version check entry archtag=%s, clienttag=%s",
102 tag_uint_to_str(archtag_str,vi->archtag),
103 tag_uint_to_str(clienttag_str,vi->clienttag));
104
105 if (vi->archtag != archtag)
106 continue;
107 if (vi->clienttag != clienttag)
108 continue;
109
110 /* FIXME: randomize the selection if more than one match */
111 vc = xmalloc(sizeof(t_versioncheck));
112 vc->eqn = xstrdup(vi->eqn);
113 vc->mpqfile = xstrdup(vi->mpqfile);
114 vc->versiontag = xstrdup(tag_uint_to_str(clienttag_str,clienttag));
115
116 return vc;
117 }
118
119 /*
120 * No entries in the file that match, return the dummy because we have to send
121 * some equation and auth mpq to the client. The client is not going to pass the
122 * validation later unless skip_versioncheck or allow_unknown_version is enabled.
123 */
124 return &dummyvc;
125 }
126
127
128 extern int versioncheck_destroy(t_versioncheck * vc)
129 {
130 if (!vc)
131 {
132 eventlog(eventlog_level_error,__FUNCTION__,"got NULL vc");
133 return -1;
134 }
135
136 if (vc==&dummyvc)
137 return 0;
138
139 xfree((void *)vc->versiontag);
140 xfree((void *)vc->mpqfile);
141 xfree((void *)vc->eqn);
142 xfree(vc);
143
144 return 0;
145 }
146
147 extern int versioncheck_set_versiontag(t_versioncheck * vc, char const * versiontag)
148 {
149 if (!vc) {
150 eventlog(eventlog_level_error,__FUNCTION__,"got NULL vc");
151 return -1;
152 }
153 if (!versiontag) {
154 eventlog(eventlog_level_error,__FUNCTION__,"got NULL versiontag");
155 return -1;
156 }
157
158 if (vc->versiontag!=NULL) xfree((void *)vc->versiontag);
159 vc->versiontag = xstrdup(versiontag);
160 return 0;
161 }
162
163
164 extern char const * versioncheck_get_versiontag(t_versioncheck const * vc)
165 {
166 if (!vc) {
167 eventlog(eventlog_level_error,__FUNCTION__,"got NULL vc");
168 return NULL;
169 }
170
171 return vc->versiontag;
172 }
173
174
175 extern char const * versioncheck_get_mpqfile(t_versioncheck const * vc)
176 {
177 if (!vc)
178 {
179 eventlog(eventlog_level_error,__FUNCTION__,"got NULL vc");
180 return NULL;
181 }
182
183 return vc->mpqfile;
184 }
185
186
187 extern char const * versioncheck_get_eqn(t_versioncheck const * vc)
188 {
189 if (!vc)
190 {
191 eventlog(eventlog_level_error,__FUNCTION__,"got NULL vc");
192 return NULL;
193 }
194
195 return vc->eqn;
196 }
197
198 t_parsed_exeinfo * parse_exeinfo(char const * exeinfo)
199 {
200 t_parsed_exeinfo * parsed_exeinfo;
201
202 if (!exeinfo) {
203 return NULL;
204 }
205
206 parsed_exeinfo = xmalloc(sizeof(t_parsed_exeinfo));
207 parsed_exeinfo->exe = xstrdup(exeinfo);
208 parsed_exeinfo->time = 0;
209 parsed_exeinfo->size = 0;
210
211 if (strcmp(prefs_get_version_exeinfo_match(),"parse")==0)
212 {
213 #ifdef HAVE_MKTIME
214 struct tm t1;
215 char *exe;
216 char mask[MAX_EXEINFO_STR+1];
217 char * marker;
218 int size;
219 char time_invalid = 0;
220
221 if ((exeinfo[0]=='\0') || //happens when using war3-noCD and having deleted war3.org
222 (strcmp(exeinfo,"badexe")==0)) //happens when AUTHREQ had no owner/exeinfo entry
223 {
224 xfree((void *)parsed_exeinfo->exe);
225 xfree((void *)parsed_exeinfo);
226 eventlog(eventlog_level_error,__FUNCTION__,"found empty exeinfo");
227 return NULL;
228 }
229
230 memset(&t1,0,sizeof(t1));
231 t1.tm_isdst = -1;
232
233 exeinfo = strreverse((char *)exeinfo);
234 if (!(marker = strchr(exeinfo,' ')))
235 {
236 xfree((void *)parsed_exeinfo->exe);
237 xfree((void *)parsed_exeinfo);
238 return NULL;
239 }
240 for (; marker[0]==' ';marker++);
241
242 if (!(marker = strchr(marker,' ')))
243 {
244 xfree((void *)parsed_exeinfo->exe);
245 xfree((void *)parsed_exeinfo);
246 return NULL;
247 }
248 for (; marker[0]==' ';marker++);
249
250 if (!(marker = strchr(marker,' ')))
251 {
252 xfree((void *)parsed_exeinfo->exe);
253 xfree((void *)parsed_exeinfo);
254 return NULL;
255 }
256 for (; marker[0]==' ';marker++);
257 marker--;
258 marker[0] = '\0';
259 marker++;
260
261 exe = xstrdup(marker);
262 xfree((void *)parsed_exeinfo->exe);
263 parsed_exeinfo->exe = strreverse((char *)exe);
264
265 exeinfo = strreverse((char *)exeinfo);
266
267 sprintf(mask,"%%02u/%%02u/%%u %%02u:%%02u:%%02u %%u");
268
269 if (sscanf(exeinfo,mask,&t1.tm_mon,&t1.tm_mday,&t1.tm_year,&t1.tm_hour,&t1.tm_min,&t1.tm_sec,&size)!=7) {
270 if (sscanf(exeinfo,"%*s %*s %u",&size) != 1)
271 {
272
273 eventlog(eventlog_level_warn,__FUNCTION__,"parser error while parsing pattern \"%s\"",exeinfo);
274 xfree((void *)parsed_exeinfo->exe);
275 xfree((void *)parsed_exeinfo);
276 return NULL; /* neq */
277 }
278 time_invalid=1;
279 }
280
281 /* Now we have a Y2K problem :) Thanks for using a 2 digit decimal years, Blizzard. */
282 /* 00-79 -> 2000-2079
283 * * 80-99 -> 1980-1999
284 * * 100+ unchanged */
285 if (t1.tm_year<80)
286 t1.tm_year = t1.tm_year + 100;
287
288 if (time_invalid)
289 parsed_exeinfo->time = -1;
290 else
291 parsed_exeinfo->time = mktime(&t1);
292 parsed_exeinfo->size = size;
293
294 #else
295 eventlog(eventlog_level_error,__FUNCTION__,"Your system does not support mktime(). Please select another exeinfo matching method.");
296 return NULL;
297 #endif
298 }
299
300 return parsed_exeinfo;
301 }
302
303 #define safe_toupper(X) (islower((int)X)?toupper((int)X):(X))
304
305 /* This implements some dumb kind of pattern matching. Any '?'
306 * signs in the pattern are treated as "don't care" signs. This
307 * means that it doesn't matter what's on this place in the match.
308 */
309 //static int versioncheck_compare_exeinfo(char const * pattern, char const * match)
310 static int versioncheck_compare_exeinfo(t_parsed_exeinfo * pattern, t_parsed_exeinfo * match)
311 {
312 assert(pattern);
313 assert(match);
314
315 if (!strcasecmp(prefs_get_version_exeinfo_match(),"none"))
316 return 0; /* ignore exeinfo */
317
318 if (strlen(pattern->exe)!=strlen(match->exe))
319 return 1; /* neq */
320
321 if (strcmp(prefs_get_version_exeinfo_match(),"exact")==0) {
322 return strcasecmp(pattern->exe,match->exe);
323 } else if (strcmp(prefs_get_version_exeinfo_match(),"exactcase")==0) {
324 return strcmp(pattern->exe,match->exe);
325 } else if (strcmp(prefs_get_version_exeinfo_match(),"wildcard")==0) {
326 unsigned int i;
327
328 for (i=0;i<strlen(pattern->exe);i++)
329 if ((pattern->exe[i]!='?')&& /* out "don't care" sign */
330 (safe_toupper(pattern->exe[i])!=safe_toupper(match->exe[i])))
331 return 1; /* neq */
332 return 0; /* ok */
333 } else if (strcmp(prefs_get_version_exeinfo_match(),"parse")==0) {
334
335 if (strcasecmp(pattern->exe,match->exe)!=0)
336 {
337 eventlog(eventlog_level_trace,__FUNCTION__,"filename differs");
338 return 1; /* neq */
339 }
340 if (pattern->size!=match->size)
341 {
342 eventlog(eventlog_level_trace,__FUNCTION__,"size differs");
343 return 1; /* neq */
344 }
345 if ((pattern->time!=-1) && prefs_get_version_exeinfo_maxdiff() && (abs(pattern->time-match->time)>(signed)prefs_get_version_exeinfo_maxdiff()))
346 {
347 eventlog(eventlog_level_trace,__FUNCTION__,"time differs by %i",abs(pattern->time-match->time));
348 return 1;
349 }
350 return 0; /* ok */
351 } else {
352 eventlog(eventlog_level_error,__FUNCTION__,"unknown version exeinfo match method \"%s\"",prefs_get_version_exeinfo_match());
353 return -1; /* neq/fail */
354 }
355 }
356
357 void free_parsed_exeinfo(t_parsed_exeinfo * parsed_exeinfo)
358 {
359 if (parsed_exeinfo)
360 {
361 if (parsed_exeinfo->exe)
362 xfree((void *)parsed_exeinfo->exe);
363 xfree((void *)parsed_exeinfo);
364 }
365 }
366
367 extern int versioncheck_validate(t_versioncheck * vc, t_tag archtag, t_tag clienttag, char const * exeinfo, unsigned long versionid, unsigned long gameversion, unsigned long checksum)
368 {
369 t_elem const * curr;
370 t_versioninfo * vi;
371 int badexe,badcs;
372 t_parsed_exeinfo * parsed_exeinfo;
373
374 if (!vc)
375 {
376 eventlog(eventlog_level_error,__FUNCTION__,"got NULL vc");
377 return -1;
378 }
379
380 badexe=badcs = 0;
381 parsed_exeinfo = parse_exeinfo(exeinfo);
382 LIST_TRAVERSE_CONST(versioninfo_head,curr)
383 {
384 if (!(vi = elem_get_data(curr))) /* should not happen */
385 {
386 eventlog(eventlog_level_error,__FUNCTION__,"version list contains NULL item");
387 continue;
388 }
389
390 if (vi->archtag != archtag)
391 continue;
392 if (vi->clienttag != clienttag)
393 continue;
394 if (strcmp(vi->eqn,vc->eqn)!=0)
395 continue;
396 if (strcmp(vi->mpqfile,vc->mpqfile)!=0)
397 continue;
398
399 if (vi->versionid && vi->versionid != versionid)
400 continue;
401
402 if (vi->gameversion && vi->gameversion != gameversion)
403 continue;
404
405
406 if ((!(parsed_exeinfo)) || (vi->parsed_exeinfo && (versioncheck_compare_exeinfo(vi->parsed_exeinfo,parsed_exeinfo) != 0)))
407 {
408 /*
409 * Found an entry matching but the exeinfo doesn't match.
410 * We need to rember this because if no other matching versions are found
411 * we will return badversion.
412 */
413 badexe = 1;
414 }
415 else
416 badexe = 0;
417
418 if (vi->checksum && vi->checksum != checksum)
419 {
420 /*
421 * Found an entry matching but the checksum doesn't match.
422 * We need to rember this because if no other matching versions are found
423 * we will return badversion.
424 */
425 badcs = 1;
426 }
427 else
428 badcs = 0;
429
430 if (vc->versiontag)
431 xfree((void *)vc->versiontag);
432 vc->versiontag = xstrdup(vi->versiontag);
433
434 if (badexe || badcs)
435 continue;
436
437 /* Ok, version and checksum matches or exeinfo/checksum are disabled
438 * anyway we have found a complete match */
439 eventlog(eventlog_level_info,__FUNCTION__,"got a matching entry: %s",vc->versiontag);
440 free_parsed_exeinfo(parsed_exeinfo);
441 return 1;
442 }
443
444 if (badcs) /* A match was found but the checksum was different */
445 {
446 eventlog(eventlog_level_info,__FUNCTION__,"bad checksum, closest match is: %s",vc->versiontag);
447 free_parsed_exeinfo(parsed_exeinfo);
448 return -1;
449 }
450 if (badexe) /* A match was found but the exeinfo string was different */
451 {
452 eventlog(eventlog_level_info,__FUNCTION__,"bad exeinfo, closest match is: %s",vc->versiontag);
453 free_parsed_exeinfo(parsed_exeinfo);
454 return -1;
455 }
456
457 /* No match in list */
458 eventlog(eventlog_level_info,__FUNCTION__,"no match in list, setting to: %s",vc->versiontag);
459 free_parsed_exeinfo(parsed_exeinfo);
460 return 0;
461 }
462
463 extern int versioncheck_load(char const * filename)
464 {
465 FILE * fp;
466 unsigned int line;
467 unsigned int pos;
468 char * buff;
469 char * temp;
470 char const * eqn;
471 char const * mpqfile;
472 char const * archtag;
473 char const * clienttag;
474 char const * exeinfo;
475 char const * versionid;
476 char const * gameversion;
477 char const * checksum;
478 char const * versiontag;
479 t_versioninfo * vi;
480
481 if (!filename)
482 {
483 eventlog(eventlog_level_error,__FUNCTION__,"got NULL filename");
484 return -1;
485 }
486
487 if (!(versioninfo_head = list_create()))
488 {
489 eventlog(eventlog_level_error,__FUNCTION__,"could create list");
490 return -1;
491 }
492 if (!(fp = fopen(filename,"r")))
493 {
494 eventlog(eventlog_level_error,__FUNCTION__,"could not open file \"%s\" for reading (fopen: %s)",filename,pstrerror(errno));
495 list_destroy(versioninfo_head);
496 versioninfo_head = NULL;
497 return -1;
498 }
499
500 line = 1;
501 for (; (buff = file_get_line(fp)); line++)
502 {
503 for (pos=0; buff[pos]=='\t' || buff[pos]==' '; pos++);
504 if (buff[pos]=='\0' || buff[pos]=='#')
505 {
506 continue;
507 }
508 if ((temp = strrchr(buff,'#')))
509 {
510 unsigned int len;
511 unsigned int endpos;
512
513 *temp = '\0';
514 len = strlen(buff)+1;
515 for (endpos=len-1; buff[endpos]=='\t' || buff[endpos]==' '; endpos--);
516 buff[endpos+1] = '\0';
517 }
518
519 if (!(eqn = next_token(buff,&pos)))
520 {
521 eventlog(eventlog_level_error,__FUNCTION__,"missing eqn near line %u of file \"%s\"",line,filename);
522 continue;
523 }
524 line++;
525 if (!(mpqfile = next_token(buff,&pos)))
526 {
527 eventlog(eventlog_level_error,__FUNCTION__,"missing mpqfile near line %u of file \"%s\"",line,filename);
528 continue;
529 }
530 line++;
531 if (!(archtag = next_token(buff,&pos)))
532 {
533 eventlog(eventlog_level_error,__FUNCTION__,"missing archtag near line %u of file \"%s\"",line,filename);
534 continue;
535 }
536 line++;
537 if (!(clienttag = next_token(buff,&pos)))
538 {
539 eventlog(eventlog_level_error,__FUNCTION__,"missing clienttag near line %u of file \"%s\"",line,filename);
540 continue;
541 }
542 line++;
543 if (!(exeinfo = next_token(buff,&pos)))
544 {
545 eventlog(eventlog_level_error,__FUNCTION__,"missing exeinfo near line %u of file \"%s\"",line,filename);
546 continue;
547 }
548 line++;
549 if (!(versionid = next_token(buff,&pos)))
550 {
551 eventlog(eventlog_level_error,__FUNCTION__,"missing versionid near line %u of file \"%s\"",line,filename);
552 continue;
553 }
554 line++;
555 if (!(gameversion = next_token(buff,&pos)))
556 {
557 eventlog(eventlog_level_error,__FUNCTION__,"missing gameversion near line %u of file \"%s\"",line,filename);
558 continue;
559 }
560 line++;
561 if (!(checksum = next_token(buff,&pos)))
562 {
563 eventlog(eventlog_level_error,__FUNCTION__,"missing checksum near line %u of file \"%s\"",line,filename);
564 continue;
565 }
566 line++;
567 if (!(versiontag = next_token(buff,&pos)))
568 {
569 versiontag = NULL;
570 }
571
572 vi = xmalloc(sizeof(t_versioninfo));
573 vi->eqn = xstrdup(eqn);
574 vi->mpqfile = xstrdup(mpqfile);
575 if (strlen(archtag)!=4)
576 {
577 eventlog(eventlog_level_error,__FUNCTION__,"invalid arch tag on line %u of file \"%s\"",line,filename);
578 xfree((void *)vi->mpqfile); /* avoid warning */
579 xfree((void *)vi->eqn); /* avoid warning */
580 xfree(vi);
581 continue;
582 }
583 if (!tag_check_arch((vi->archtag = tag_str_to_uint(archtag))))
584 {
585 eventlog(eventlog_level_error,__FUNCTION__,"got unknown archtag \"%s\"",archtag);
586 xfree((void *)vi->mpqfile); /* avoid warning */
587 xfree((void *)vi->eqn); /* avoid warning */
588 xfree(vi);
589 continue;
590 }
591 if (strlen(clienttag)!=4)
592 {
593 eventlog(eventlog_level_error,__FUNCTION__,"invalid client tag on line %u of file \"%s\"",line,filename);
594 xfree((void *)vi->mpqfile); /* avoid warning */
595 xfree((void *)vi->eqn); /* avoid warning */
596 xfree(vi);
597 continue;
598 }
599 if (!tag_check_client((vi->clienttag = tag_str_to_uint(clienttag))))
600 {
601 eventlog(eventlog_level_error,__FUNCTION__,"got unknown clienttag\"%s\"",clienttag);
602 xfree((void *)vi->mpqfile); /* avoid warning */
603 xfree((void *)vi->eqn); /* avoid warning */
604 xfree(vi);
605 continue;
606 }
607 if (strcmp(exeinfo, "NULL") == 0)
608 vi->parsed_exeinfo = NULL;
609 else
610 {
611 if (!(vi->parsed_exeinfo = parse_exeinfo(exeinfo)))
612 {
613 eventlog(eventlog_level_error,__FUNCTION__,"encountered an error while parsing exeinfo");
614 xfree((void *)vi->mpqfile); /* avoid warning */
615 xfree((void *)vi->eqn); /* avoid warning */
616 xfree(vi);
617 continue;
618 }
619 }
620
621 vi->versionid = strtoul(versionid,NULL,0);
622 if (verstr_to_vernum(gameversion,&vi->gameversion)<0)
623 {
624 eventlog(eventlog_level_error,__FUNCTION__,"malformed version on line %u of file \"%s\"",line,filename);
625 xfree((void *)vi->parsed_exeinfo); /* avoid warning */
626 xfree((void *)vi->mpqfile); /* avoid warning */
627 xfree((void *)vi->eqn); /* avoid warning */
628 xfree(vi);
629 continue;
630 }
631
632 vi->checksum = strtoul(checksum,NULL,0);
633 if (versiontag)
634 vi->versiontag = xstrdup(versiontag);
635 else
636 vi->versiontag = NULL;
637
638
639 list_append_data(versioninfo_head,vi);
640 }
641
642 file_get_line(NULL); // clear file_get_line buffer
643 if (fclose(fp)<0)
644 eventlog(eventlog_level_error,__FUNCTION__,"could not close versioncheck file \"%s\" after reading (fclose: %s)",filename,pstrerror(errno));
645
646 return 0;
647 }
648
649
650 extern int versioncheck_unload(void)
651 {
652 t_elem * curr;
653 t_versioninfo * vi;
654
655 if (versioninfo_head)
656 {
657 LIST_TRAVERSE(versioninfo_head,curr)
658 {
659 if (!(vi = elem_get_data(curr))) /* should not happen */
660 {
661 eventlog(eventlog_level_error,__FUNCTION__,"version list contains NULL item");
662 continue;
663 }
664
665 if (list_remove_elem(versioninfo_head,&curr)<0)
666 eventlog(eventlog_level_error,__FUNCTION__,"could not remove item from list");
667
668 if (vi->parsed_exeinfo)
669 {
670 if (vi->parsed_exeinfo->exe)
671 xfree((void *)vi->parsed_exeinfo->exe);
672 xfree((void *)vi->parsed_exeinfo); /* avoid warning */
673 }
674 xfree((void *)vi->mpqfile); /* avoid warning */
675 xfree((void *)vi->eqn); /* avoid warning */
676 if (vi->versiontag)
677 xfree((void *)vi->versiontag); /* avoid warning */
678 xfree(vi);
679 }
680
681 if (list_destroy(versioninfo_head)<0)
682 return -1;
683 versioninfo_head = NULL;
684 }
685
686 return 0;
687 }

webmaster@leafok.com
ViewVC Help
Powered by ViewVC 1.3.0-beta1