plover/comps.c
author J. Ali Harlow <ali@juiblex.co.uk>
Thu Jul 16 19:54:45 2020 +0100 (2020-07-16)
changeset 99 0121592e2512
parent 71 bd272d15bea4
permissions -rw-r--r--
Fix most compiler warnings
ali@0
     1
/*
ali@0
     2
 * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
ali@0
     3
 * Copyright (C) 2008  Red Hat, Inc
ali@43
     4
 * Copyright (C) 2009, 2016  J. Ali Harlow <ali@juiblex.co.uk>
ali@0
     5
 *
ali@0
     6
 * This program is free software; you can redistribute it and/or modify
ali@0
     7
 * it under the terms of the GNU General Public License as published by
ali@0
     8
 * the Free Software Foundation; either version 2 of the License, or
ali@0
     9
 * (at your option) any later version.
ali@0
    10
 *
ali@0
    11
 * This program is distributed in the hope that it will be useful,
ali@0
    12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
ali@0
    13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
ali@0
    14
 * GNU General Public License for more details.
ali@0
    15
 *
ali@0
    16
 * You should have received a copy of the GNU General Public License along
ali@0
    17
 * with this program; if not, write to the Free Software Foundation, Inc.,
ali@0
    18
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
ali@0
    19
 */
ali@0
    20
ali@99
    21
#include "config.h"
ali@0
    22
#include <string.h>
ali@0
    23
#include <stdio.h>
ali@0
    24
#include <errno.h>
ali@0
    25
#include <expat.h>
ali@0
    26
#include <assert.h>
ali@0
    27
#include "plover/plover.h"
ali@44
    28
#include "plover/uri-handler.h"
ali@0
    29
ali@0
    30
/* Parse a comps.xml package group file. */
ali@0
    31
ali@61
    32
#define PLOVER_XML_NS "http://project.juiblex.co.uk/plover/ns/2009"
ali@61
    33
ali@0
    34
static struct comps_requirement *comps_package_requirement_new(void)
ali@0
    35
{
ali@0
    36
    struct comps_requirement *req;
ali@0
    37
    req=calloc(sizeof(*req),1);
ali@0
    38
    assert(req != NULL);
ali@0
    39
    req->type=COMPS_REQUIREMENT_OPTIONAL;
ali@0
    40
    return req;
ali@0
    41
}
ali@0
    42
ali@0
    43
static void comps_package_requirement_free_1(struct comps_requirement *req)
ali@0
    44
{
ali@0
    45
    if (req)
ali@0
    46
    {
ali@0
    47
	free(req->requires);
ali@0
    48
	free(req->name);
ali@0
    49
	free(req);
ali@0
    50
    }
ali@0
    51
}
ali@0
    52
ali@0
    53
static void comps_package_requirement_free(struct comps_requirement *req)
ali@0
    54
{
ali@0
    55
    struct comps_requirement *next;
ali@0
    56
    while(req)
ali@0
    57
    {
ali@0
    58
	next=req->next;
ali@0
    59
	comps_package_requirement_free_1(req);
ali@0
    60
	req=next;
ali@0
    61
    }
ali@0
    62
}
ali@0
    63
ali@38
    64
static struct comps_group *comps_group_new(void)
ali@0
    65
{
ali@0
    66
    struct comps_group *group;
ali@0
    67
    group=calloc(sizeof(*group),1);
ali@0
    68
    assert(group != NULL);
ali@0
    69
    group->bydefault=1;
ali@0
    70
    group->uservisible=1;
ali@0
    71
    return group;
ali@0
    72
}
ali@0
    73
ali@0
    74
static void comps_group_free_1(struct comps_group *group)
ali@0
    75
{
ali@0
    76
    if (group)
ali@0
    77
    {
ali@0
    78
	free(group->id);
ali@0
    79
	free(group->name);
ali@0
    80
	free(group->description);
ali@0
    81
	comps_package_requirement_free(group->packages);
ali@0
    82
	free(group);
ali@0
    83
    }
ali@0
    84
}
ali@38
    85
ali@0
    86
static void comps_group_free(struct comps_group *group)
ali@0
    87
{
ali@0
    88
    struct comps_group *next;
ali@0
    89
    while(group)
ali@0
    90
    {
ali@0
    91
	next=group->next;
ali@0
    92
	comps_group_free_1(group);
ali@0
    93
	group=next;
ali@0
    94
    }
ali@0
    95
}
ali@0
    96
ali@0
    97
struct comps *plover_comps_new(void)
ali@0
    98
{
ali@0
    99
    struct comps *comps;
ali@0
   100
    comps=calloc(sizeof(*comps),1);
ali@0
   101
    assert(comps != NULL);
ali@0
   102
    return comps;
ali@0
   103
}
ali@0
   104
ali@0
   105
void plover_comps_free(struct comps *comps)
ali@0
   106
{
ali@0
   107
    if (comps)
ali@0
   108
    {
ali@0
   109
	free(comps->vendor);
ali@61
   110
	free(comps->distribution);
ali@0
   111
	comps_group_free(comps->groups);
ali@0
   112
	free(comps);
ali@0
   113
    }
ali@0
   114
}
ali@0
   115
ali@0
   116
enum comps_state {
ali@0
   117
    COMPS_STATE_BEGIN,
ali@0
   118
    COMPS_STATE_ROOT,
ali@0
   119
    COMPS_STATE_GROUP,
ali@0
   120
    COMPS_STATE_GROUP_ID,
ali@0
   121
    COMPS_STATE_GROUP_NAME,
ali@0
   122
    COMPS_STATE_GROUP_DESCRIPTION,
ali@0
   123
    COMPS_STATE_GROUP_DEFAULT,
ali@0
   124
    COMPS_STATE_GROUP_USERVISIBLE,
ali@0
   125
    COMPS_STATE_GROUP_PACKAGELIST,
ali@0
   126
    COMPS_STATE_GROUP_PACKAGELIST_REQ,
ali@0
   127
};
ali@0
   128
ali@0
   129
struct comps_context {
ali@0
   130
    XML_Parser parser;
ali@0
   131
    enum comps_state state;
ali@0
   132
    int unknown_elements;
ali@61
   133
    char *vendor,*distribution;
ali@61
   134
    enum comps_database_setting database;
ali@0
   135
    struct comps_group *group;
ali@0
   136
    void *p;
ali@0
   137
};
ali@0
   138
ali@0
   139
static enum comps_requirement_type comps_to_requirement_type(const char *flags)
ali@0
   140
{
ali@0
   141
    if (strcmp(flags, "conditional") == 0)
ali@0
   142
	return COMPS_REQUIREMENT_CONDITIONAL;
ali@0
   143
    else if (strcmp(flags, "mandatory") == 0)
ali@0
   144
	return COMPS_REQUIREMENT_MANDATORY;
ali@0
   145
    else if (strcmp(flags, "default") == 0)
ali@0
   146
	return COMPS_REQUIREMENT_DEFAULT;
ali@0
   147
    else
ali@0
   148
	return COMPS_REQUIREMENT_OPTIONAL;
ali@0
   149
}
ali@0
   150
ali@0
   151
static void comps_start_element(void *data,const char *name,const char **atts)
ali@0
   152
{
ali@0
   153
    struct comps_context *ctx = data;
ali@0
   154
    struct comps_group *group;
ali@0
   155
    struct comps_requirement *packages;
ali@0
   156
    int i;
ali@0
   157
#if 0
ali@0
   158
    fprintf(stderr,"Starting element '");
ali@0
   159
    switch(ctx->state)
ali@0
   160
    {
ali@0
   161
	case COMPS_STATE_BEGIN:
ali@0
   162
	    fprintf(stderr,"/");
ali@0
   163
	    break;
ali@0
   164
	case COMPS_STATE_ROOT:
ali@0
   165
	    fprintf(stderr,"/comps/");
ali@0
   166
	    break;
ali@0
   167
	case COMPS_STATE_GROUP:
ali@0
   168
	    fprintf(stderr,"/comps/group/");
ali@0
   169
	    break;
ali@0
   170
	case COMPS_STATE_GROUP_ID:
ali@0
   171
	    fprintf(stderr,"/comps/group/id/");
ali@0
   172
	    break;
ali@0
   173
	case COMPS_STATE_GROUP_NAME:
ali@0
   174
	    fprintf(stderr,"/comps/group/name/");
ali@0
   175
	    break;
ali@0
   176
	case COMPS_STATE_GROUP_DESCRIPTION:
ali@0
   177
	    fprintf(stderr,"/comps/group/description/");
ali@0
   178
	    break;
ali@0
   179
	case COMPS_STATE_GROUP_DEFAULT:
ali@0
   180
	    fprintf(stderr,"/comps/group/default/");
ali@0
   181
	    break;
ali@0
   182
	case COMPS_STATE_GROUP_USERVISIBLE:
ali@0
   183
	    fprintf(stderr,"/comps/group/uservisible/");
ali@0
   184
	    break;
ali@0
   185
	case COMPS_STATE_GROUP_PACKAGELIST:
ali@0
   186
	    fprintf(stderr,"/comps/group/packagelist/");
ali@0
   187
	    break;
ali@0
   188
	case COMPS_STATE_GROUP_PACKAGELIST_REQ:
ali@0
   189
	    fprintf(stderr,"/comps/group/packagelist/packagereq/");
ali@0
   190
	    break;
ali@0
   191
ali@0
   192
    }
ali@0
   193
    for(i=0;i<ctx->unknown_elements;i++)
ali@0
   194
	fprintf(stderr,"*/");
ali@0
   195
    fprintf(stderr,"%s'\n",name);
ali@0
   196
#endif
ali@0
   197
    if (ctx->unknown_elements)
ali@0
   198
	ctx->unknown_elements++;
ali@0
   199
    else if (ctx->state==COMPS_STATE_BEGIN && !strcmp(name,"comps"))
ali@0
   200
    {
ali@0
   201
	ctx->state=COMPS_STATE_ROOT;
ali@0
   202
	for (i=0;atts[i];i+=2)
ali@0
   203
	{
ali@61
   204
	    if (!strcmp(atts[i],PLOVER_XML_NS "\xFF" "vendor"))
ali@0
   205
		ctx->vendor=strdup(atts[i+1]);
ali@61
   206
	    else if (!strcmp(atts[i],PLOVER_XML_NS "\xFF" "distribution"))
ali@61
   207
		ctx->distribution=strdup(atts[i+1]);
ali@61
   208
	    else if (!strcmp(atts[i],PLOVER_XML_NS "\xFF" "database"))
ali@61
   209
	    {
ali@61
   210
		if (!g_strcmp0(atts[i+1],"distribution-local"))
ali@61
   211
		    ctx->database=COMPS_DATABASE_DISTRIBUTION_LOCAL;
ali@61
   212
		else
ali@61
   213
		    ctx->database=COMPS_DATABASE_GLOBAL;
ali@61
   214
	    }
ali@0
   215
	}
ali@0
   216
    }
ali@0
   217
    else if (ctx->state==COMPS_STATE_ROOT && !strcmp(name,"group"))
ali@0
   218
    {
ali@0
   219
	ctx->state=COMPS_STATE_GROUP;
ali@0
   220
	group=comps_group_new();
ali@0
   221
	group->next=ctx->group;
ali@0
   222
	ctx->group=group;
ali@0
   223
    }
ali@0
   224
    else if (ctx->state==COMPS_STATE_GROUP && !strcmp(name,"id"))
ali@0
   225
    {
ali@0
   226
	ctx->state=COMPS_STATE_GROUP_ID;
ali@0
   227
	ctx->p=&ctx->group->id;
ali@0
   228
    }
ali@0
   229
    else if (ctx->state==COMPS_STATE_GROUP && !strcmp(name,"name"))
ali@0
   230
    {
ali@0
   231
	ctx->state=COMPS_STATE_GROUP_NAME;
ali@0
   232
	ctx->p=&ctx->group->name;
ali@0
   233
    }
ali@0
   234
    else if (ctx->state==COMPS_STATE_GROUP && !strcmp(name,"description"))
ali@0
   235
    {
ali@0
   236
	ctx->state=COMPS_STATE_GROUP_DESCRIPTION;
ali@0
   237
	ctx->p=&ctx->group->description;
ali@0
   238
    }
ali@0
   239
    else if (ctx->state==COMPS_STATE_GROUP && !strcmp(name,"default"))
ali@0
   240
    {
ali@0
   241
	ctx->state=COMPS_STATE_GROUP_DEFAULT;
ali@0
   242
	ctx->p=&ctx->group->bydefault;
ali@0
   243
    }
ali@0
   244
    else if (ctx->state==COMPS_STATE_GROUP && !strcmp(name,"uservisible"))
ali@0
   245
    {
ali@0
   246
	ctx->state=COMPS_STATE_GROUP_USERVISIBLE;
ali@0
   247
	ctx->p=&ctx->group->uservisible;
ali@0
   248
    }
ali@0
   249
    else if (ctx->state==COMPS_STATE_GROUP && !strcmp(name,"packagelist"))
ali@0
   250
	ctx->state=COMPS_STATE_GROUP_PACKAGELIST;
ali@0
   251
    else if (ctx->state==COMPS_STATE_GROUP_PACKAGELIST &&
ali@0
   252
      !strcmp(name,"packagereq"))
ali@0
   253
    {
ali@0
   254
	packages=comps_package_requirement_new();
ali@0
   255
	packages->next=ctx->group->packages;
ali@0
   256
	ctx->group->packages=packages;
ali@0
   257
	for (i=0;atts[i];i+=2)
ali@0
   258
	{
ali@0
   259
	    if (!strcmp(atts[i],"type"))
ali@0
   260
		packages->type=comps_to_requirement_type(atts[i+1]);
ali@0
   261
	    else if (!strcmp(atts[i],"requires"))
ali@0
   262
	    {
ali@0
   263
		packages->requires=strdup(atts[i+1]);
ali@0
   264
		packages->type=COMPS_REQUIREMENT_CONDITIONAL;
ali@0
   265
	    }
ali@0
   266
	}
ali@0
   267
	ctx->state=COMPS_STATE_GROUP_PACKAGELIST_REQ;
ali@0
   268
	ctx->p=&packages->name;
ali@0
   269
    }
ali@0
   270
    else
ali@0
   271
	ctx->unknown_elements++;
ali@0
   272
}
ali@0
   273
ali@0
   274
struct comps_list
ali@0
   275
{
ali@0
   276
    struct comps_list *next;
ali@0
   277
    /* body */
ali@0
   278
};
ali@0
   279
ali@0
   280
void *comps_list_reverse(void *list)
ali@0
   281
{
ali@0
   282
    struct comps_list *link=list,*prev=NULL,*next;
ali@0
   283
    while(link)
ali@0
   284
    {
ali@0
   285
	next=link->next;
ali@0
   286
	link->next=prev;
ali@0
   287
	prev=link;
ali@0
   288
	link=next;
ali@0
   289
    }
ali@0
   290
    return prev;
ali@0
   291
}
ali@0
   292
ali@0
   293
static void comps_end_element(void *data,const char *name)
ali@0
   294
{
ali@0
   295
    struct comps_context *ctx=data;
ali@0
   296
    if (ctx->unknown_elements)
ali@0
   297
	ctx->unknown_elements--;
ali@0
   298
    else
ali@0
   299
	switch (ctx->state)
ali@0
   300
	{
ali@99
   301
	    case COMPS_STATE_BEGIN:
ali@99
   302
		g_assert_not_reached();
ali@99
   303
		break;
ali@0
   304
	    case COMPS_STATE_ROOT:
ali@0
   305
		ctx->group=comps_list_reverse(ctx->group);
ali@0
   306
		ctx->state=COMPS_STATE_BEGIN;
ali@0
   307
		break;
ali@0
   308
	    case COMPS_STATE_GROUP:
ali@0
   309
		ctx->group->packages=comps_list_reverse(ctx->group->packages);
ali@0
   310
		ctx->state=COMPS_STATE_ROOT;
ali@0
   311
		break;
ali@0
   312
	    case COMPS_STATE_GROUP_ID:
ali@0
   313
	    case COMPS_STATE_GROUP_NAME:
ali@0
   314
	    case COMPS_STATE_GROUP_DESCRIPTION:
ali@0
   315
	    case COMPS_STATE_GROUP_DEFAULT:
ali@0
   316
	    case COMPS_STATE_GROUP_USERVISIBLE:
ali@0
   317
	    case COMPS_STATE_GROUP_PACKAGELIST:
ali@0
   318
		ctx->state=COMPS_STATE_GROUP;
ali@0
   319
		break;
ali@0
   320
	    case COMPS_STATE_GROUP_PACKAGELIST_REQ:
ali@0
   321
		ctx->state=COMPS_STATE_GROUP_PACKAGELIST;
ali@0
   322
		break;
ali@0
   323
	}
ali@0
   324
#if 0
ali@0
   325
    fprintf(stderr,"Ending element '");
ali@0
   326
    switch(ctx->state)
ali@0
   327
    {
ali@0
   328
	case COMPS_STATE_BEGIN:
ali@0
   329
	    fprintf(stderr,"/");
ali@0
   330
	    break;
ali@0
   331
	case COMPS_STATE_ROOT:
ali@0
   332
	    fprintf(stderr,"/comps/");
ali@0
   333
	    break;
ali@0
   334
	case COMPS_STATE_GROUP:
ali@0
   335
	    fprintf(stderr,"/comps/group/");
ali@0
   336
	    break;
ali@0
   337
	case COMPS_STATE_GROUP_ID:
ali@0
   338
	    fprintf(stderr,"/comps/group/id/");
ali@0
   339
	    break;
ali@0
   340
	case COMPS_STATE_GROUP_NAME:
ali@0
   341
	    fprintf(stderr,"/comps/group/name/");
ali@0
   342
	    break;
ali@0
   343
	case COMPS_STATE_GROUP_DESCRIPTION:
ali@0
   344
	    fprintf(stderr,"/comps/group/description/");
ali@0
   345
	    break;
ali@0
   346
	case COMPS_STATE_GROUP_DEFAULT:
ali@0
   347
	    fprintf(stderr,"/comps/group/default/");
ali@0
   348
	    break;
ali@0
   349
	case COMPS_STATE_GROUP_USERVISIBLE:
ali@0
   350
	    fprintf(stderr,"/comps/group/uservisible/");
ali@0
   351
	    break;
ali@0
   352
	case COMPS_STATE_GROUP_PACKAGELIST:
ali@0
   353
	    fprintf(stderr,"/comps/group/packagelist/");
ali@0
   354
	    break;
ali@0
   355
	case COMPS_STATE_GROUP_PACKAGELIST_REQ:
ali@0
   356
	    fprintf(stderr,"/comps/group/packagelist/packagereq/");
ali@0
   357
	    break;
ali@0
   358
ali@0
   359
    }
ali@0
   360
    {
ali@0
   361
	int i;
ali@0
   362
	for(i=0;i<ctx->unknown_elements;i++)
ali@0
   363
	    fprintf(stderr,"*/");
ali@0
   364
    }
ali@0
   365
    fprintf(stderr,"%s'\n",name);
ali@0
   366
#endif
ali@0
   367
}
ali@0
   368
ali@0
   369
static void comps_character_data(void *data,const XML_Char *s,int len)
ali@0
   370
{
ali@0
   371
    struct comps_context *ctx=data;
ali@0
   372
    char *str;
ali@0
   373
    switch (ctx->state)
ali@0
   374
    {
ali@99
   375
	case COMPS_STATE_BEGIN:
ali@99
   376
	case COMPS_STATE_ROOT:
ali@99
   377
	case COMPS_STATE_GROUP:
ali@99
   378
	case COMPS_STATE_GROUP_PACKAGELIST:
ali@99
   379
	    break;
ali@0
   380
	case COMPS_STATE_GROUP_ID:
ali@0
   381
	case COMPS_STATE_GROUP_NAME:
ali@0
   382
	case COMPS_STATE_GROUP_DESCRIPTION:
ali@0
   383
	case COMPS_STATE_GROUP_PACKAGELIST_REQ:
ali@0
   384
	    *(char **)ctx->p=str=malloc(len+1);
ali@0
   385
	    memcpy(str,s,len);
ali@0
   386
	    str[len]='\0';
ali@0
   387
	    break;
ali@0
   388
	case COMPS_STATE_GROUP_DEFAULT:
ali@0
   389
	case COMPS_STATE_GROUP_USERVISIBLE:
ali@0
   390
	    *(int *)ctx->p=*s=='T'||*s=='t';
ali@0
   391
	    break;
ali@0
   392
    }
ali@0
   393
}
ali@0
   394
ali@61
   395
struct comps *plover_comps_new_from_uri(const char *uri,GError **error)
ali@0
   396
{
ali@0
   397
    struct comps_context ctx={0};
ali@61
   398
    struct razor_error *tmp_error=NULL;
ali@61
   399
    void *contents;
ali@61
   400
    size_t length;
ali@0
   401
    struct comps *comps;
ali@71
   402
    g_return_val_if_fail(plover__uri_validate(uri),NULL);
ali@44
   403
    plover__uri_handler_init();
ali@61
   404
    contents=razor_uri_get_contents(uri,&length,FALSE,&tmp_error);
ali@61
   405
    if (!contents)
ali@61
   406
    {
ali@61
   407
	plover_propagate_razor_error(error,tmp_error);
ali@0
   408
	return NULL;
ali@61
   409
    }
ali@0
   410
    ctx.state=COMPS_STATE_BEGIN;
ali@0
   411
    ctx.parser=XML_ParserCreateNS(NULL,'\xFF');
ali@0
   412
    XML_SetUserData(ctx.parser,&ctx);
ali@0
   413
    XML_SetElementHandler(ctx.parser,comps_start_element,comps_end_element);
ali@0
   414
    XML_SetCharacterDataHandler(ctx.parser,comps_character_data);
ali@61
   415
    if (XML_Parse(ctx.parser,contents,length,TRUE)==XML_STATUS_ERROR)
ali@0
   416
    {
ali@61
   417
	g_set_error(error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED,
ali@99
   418
	  "%s on line %lu of '%s'\n",
ali@61
   419
	  XML_ErrorString(XML_GetErrorCode(ctx.parser)),
ali@99
   420
	  (unsigned long)XML_GetCurrentLineNumber(ctx.parser),uri);
ali@61
   421
	XML_ParserFree(ctx.parser);
ali@61
   422
	razor_uri_free_contents(contents,length);
ali@61
   423
	return NULL;
ali@61
   424
    }
ali@0
   425
    XML_ParserFree(ctx.parser);
ali@61
   426
    razor_uri_free_contents(contents,length);
ali@0
   427
    comps=plover_comps_new();
ali@0
   428
    comps->vendor=ctx.vendor;
ali@61
   429
    comps->distribution=ctx.distribution;
ali@61
   430
    comps->database=ctx.database;
ali@61
   431
    if (comps->database==COMPS_DATABASE_DISTRIBUTION_LOCAL &&
ali@61
   432
      (!comps->vendor || !comps->distribution))
ali@61
   433
    {
ali@61
   434
	g_warning("%s: database setting of distribution-local ignored",uri);
ali@61
   435
	comps->database=COMPS_DATABASE_GLOBAL;
ali@61
   436
    }
ali@0
   437
    comps->groups=ctx.group;
ali@0
   438
    return comps;
ali@0
   439
}
ali@0
   440
ali@43
   441
struct comps *plover_comps_new_from_file(const char *filename)
ali@43
   442
{
ali@43
   443
    gchar *uri;
ali@43
   444
    GFile *file;
ali@43
   445
    struct comps *comps;
ali@43
   446
    file=g_file_new_for_path(filename);
ali@43
   447
    uri=g_file_get_uri(file);
ali@43
   448
    g_object_unref(file);
ali@61
   449
    comps=plover_comps_new_from_uri(uri,NULL);
ali@43
   450
    g_free(uri);
ali@43
   451
    return comps;
ali@43
   452
}
ali@43
   453
ali@0
   454
struct comps_group *plover_comps_lookup_group(struct comps *comps,
ali@0
   455
  const char *id)
ali@0
   456
{
ali@0
   457
    struct comps_group *group;
ali@0
   458
    for(group=comps->groups;group;group=group->next)
ali@0
   459
	if (!strcmp(group->id,id))
ali@0
   460
	    return group;
ali@0
   461
    return NULL;
ali@0
   462
}
ali@61
   463
ali@61
   464
void plover_comps_set_vendor(struct comps *comps,const char *vendor)
ali@61
   465
{
ali@61
   466
    free(comps->vendor);
ali@61
   467
    comps->vendor=strdup(vendor);
ali@61
   468
}
ali@61
   469
ali@61
   470
void plover_comps_set_distribution(struct comps *comps,const char *distribution)
ali@61
   471
{
ali@61
   472
    free(comps->distribution);
ali@61
   473
    comps->distribution=strdup(distribution);
ali@61
   474
}