File: | lib/rpmal.c |
Warning: | line 480, column 2 Value stored to 'ret' is never read |
1 | /** \ingroup rpmdep |
2 | * \file lib/rpmal.c |
3 | */ |
4 | |
5 | #include "system.h" |
6 | |
7 | |
8 | #include <rpm/rpmte.h> |
9 | #include <rpm/rpmfi.h> |
10 | #include <rpm/rpmstrpool.h> |
11 | |
12 | #include "lib/rpmal.h" |
13 | #include "lib/misc.h" |
14 | #include "lib/rpmte_internal.h" |
15 | #include "lib/rpmds_internal.h" |
16 | #include "lib/rpmfi_internal.h" |
17 | |
18 | #include "debug.h" |
19 | |
20 | typedef struct availablePackage_s * availablePackage; |
21 | typedef int rpmalNum; |
22 | |
23 | /** \ingroup rpmdep |
24 | * Info about a single package to be installed. |
25 | */ |
26 | struct availablePackage_s { |
27 | rpmte p; /*!< transaction member */ |
28 | rpmds provides; /*!< Provides: dependencies. */ |
29 | rpmds obsoletes; /*!< Obsoletes: dependencies. */ |
30 | rpmfiles fi; /*!< File info set. */ |
31 | }; |
32 | |
33 | /** \ingroup rpmdep |
34 | * A single available item (e.g. a Provides: dependency). |
35 | */ |
36 | typedef struct availableIndexEntry_s { |
37 | rpmalNum pkgNum; /*!< Containing package index. */ |
38 | unsigned int entryIx; /*!< Dependency index. */ |
39 | } * availableIndexEntry; |
40 | |
41 | #undef HASHTYPErpmalFileHash |
42 | #undef HTKEYTYPErpmsid |
43 | #undef HTDATATYPEstruct availableIndexFileEntry_s |
44 | #define HASHTYPErpmalFileHash rpmalDepHash |
45 | #define HTKEYTYPErpmsid rpmsid |
46 | #define HTDATATYPEstruct availableIndexFileEntry_s struct availableIndexEntry_s |
47 | #include "lib/rpmhash.H" |
48 | #include "lib/rpmhash.C" |
49 | |
50 | typedef struct availableIndexFileEntry_s { |
51 | rpmsid dirName; |
52 | rpmalNum pkgNum; /*!< Containing package index. */ |
53 | unsigned int entryIx; /*!< Dependency index. */ |
54 | } * availableIndexFileEntry; |
55 | |
56 | #undef HASHTYPErpmalFileHash |
57 | #undef HTKEYTYPErpmsid |
58 | #undef HTDATATYPEstruct availableIndexFileEntry_s |
59 | #define HASHTYPErpmalFileHash rpmalFileHash |
60 | #define HTKEYTYPErpmsid rpmsid |
61 | #define HTDATATYPEstruct availableIndexFileEntry_s struct availableIndexFileEntry_s |
62 | #include "lib/rpmhash.H" |
63 | #include "lib/rpmhash.C" |
64 | |
65 | /** \ingroup rpmdep |
66 | * Set of available packages, items, and directories. |
67 | */ |
68 | struct rpmal_s { |
69 | rpmstrPool pool; /*!< String pool */ |
70 | availablePackage list; /*!< Set of packages. */ |
71 | rpmalDepHash providesHash; |
72 | rpmalDepHash obsoletesHash; |
73 | rpmalFileHash fileHash; |
74 | int delta; /*!< Delta for pkg list reallocation. */ |
75 | int size; /*!< No. of pkgs in list. */ |
76 | int alloced; /*!< No. of pkgs allocated for list. */ |
77 | rpmtransFlags tsflags; /*!< Transaction control flags. */ |
78 | rpm_color_t tscolor; /*!< Transaction color. */ |
79 | rpm_color_t prefcolor; /*!< Transaction preferred color. */ |
80 | fingerPrintCache fpc; |
81 | }; |
82 | |
83 | /** |
84 | * Destroy available item index. |
85 | * @param al available list |
86 | */ |
87 | static void rpmalFreeIndex(rpmal al) |
88 | { |
89 | al->providesHash = rpmalDepHashFree(al->providesHash); |
90 | al->obsoletesHash = rpmalDepHashFree(al->obsoletesHash); |
91 | al->fileHash = rpmalFileHashFree(al->fileHash); |
92 | al->fpc = fpCacheFree(al->fpc); |
93 | } |
94 | |
95 | rpmal rpmalCreate(rpmstrPool pool, int delta, rpmtransFlags tsflags, |
96 | rpm_color_t tscolor, rpm_color_t prefcolor) |
97 | { |
98 | rpmal al = xcalloc(1, sizeof(*al))rcalloc((1), (sizeof(*al))); |
99 | |
100 | /* transition time safe-guard */ |
101 | assert(pool != NULL)({ if (pool != ((void*)0)) ; else __assert_fail ("pool != NULL" , "rpmal.c", 101, __PRETTY_FUNCTION__); }); |
102 | |
103 | al->pool = rpmstrPoolLink(pool); |
104 | al->delta = delta; |
105 | al->size = 0; |
106 | al->alloced = al->delta; |
107 | al->list = xmalloc(sizeof(*al->list) * al->alloced)rmalloc((sizeof(*al->list) * al->alloced));; |
108 | |
109 | al->providesHash = NULL((void*)0); |
110 | al->obsoletesHash = NULL((void*)0); |
111 | al->fileHash = NULL((void*)0); |
112 | al->tsflags = tsflags; |
113 | al->tscolor = tscolor; |
114 | al->prefcolor = prefcolor; |
115 | |
116 | return al; |
117 | } |
118 | |
119 | rpmal rpmalFree(rpmal al) |
120 | { |
121 | availablePackage alp; |
122 | int i; |
123 | |
124 | if (al == NULL((void*)0)) |
125 | return NULL((void*)0); |
126 | |
127 | if ((alp = al->list) != NULL((void*)0)) |
128 | for (i = 0; i < al->size; i++, alp++) { |
129 | alp->obsoletes = rpmdsFree(alp->obsoletes); |
130 | alp->provides = rpmdsFree(alp->provides); |
131 | alp->fi = rpmfilesFree(alp->fi); |
132 | } |
133 | al->pool = rpmstrPoolFree(al->pool); |
134 | al->list = _free(al->list)rfree((al->list)); |
135 | al->alloced = 0; |
136 | |
137 | rpmalFreeIndex(al); |
138 | al = _free(al)rfree((al)); |
139 | return NULL((void*)0); |
140 | } |
141 | |
142 | static unsigned int sidHash(rpmsid sid) |
143 | { |
144 | return sid; |
145 | } |
146 | |
147 | static int sidCmp(rpmsid a, rpmsid b) |
148 | { |
149 | return (a != b); |
150 | } |
151 | |
152 | void rpmalDel(rpmal al, rpmte p) |
153 | { |
154 | availablePackage alp; |
155 | rpmalNum pkgNum; |
156 | |
157 | if (al == NULL((void*)0) || al->list == NULL((void*)0)) |
158 | return; /* XXX can't happen */ |
159 | |
160 | // XXX use a search for self provide |
161 | for (pkgNum=0; pkgNum<al->size; pkgNum++) { |
162 | if (al->list[pkgNum].p == p) { |
163 | break; |
164 | } |
165 | } |
166 | if (pkgNum == al->size ) return; // Not found! |
167 | |
168 | alp = al->list + pkgNum; |
169 | // do not actually delete, just set p to NULL |
170 | // and later filter that out of the results |
171 | alp->p = NULL((void*)0); |
172 | } |
173 | |
174 | static void rpmalAddFiles(rpmal al, rpmalNum pkgNum, rpmfiles fi) |
175 | { |
176 | struct availableIndexFileEntry_s fileEntry; |
177 | int fc = rpmfilesFC(fi); |
178 | rpm_color_t ficolor; |
179 | int skipdoc = (al->tsflags & RPMTRANS_FLAG_NODOCS); |
180 | int skipconf = (al->tsflags & RPMTRANS_FLAG_NOCONFIGS); |
181 | |
182 | fileEntry.pkgNum = pkgNum; |
183 | |
184 | for (int i = 0; i < fc; i++) { |
185 | /* Ignore colored provides not in our rainbow. */ |
186 | ficolor = rpmfilesFColor(fi, i); |
187 | if (al->tscolor && ficolor && !(al->tscolor & ficolor)) |
188 | continue; |
189 | |
190 | /* Ignore files that wont be installed */ |
191 | if (skipdoc && (rpmfilesFFlags(fi, i) & RPMFILE_DOC)) |
192 | continue; |
193 | if (skipconf && (rpmfilesFFlags(fi, i) & RPMFILE_CONFIG)) |
194 | continue; |
195 | |
196 | fileEntry.dirName = rpmfilesDNId(fi, rpmfilesDI(fi, i)); |
197 | fileEntry.entryIx = i; |
198 | |
199 | rpmalFileHashAddEntry(al->fileHash, rpmfilesBNId(fi, i), fileEntry); |
200 | } |
201 | } |
202 | |
203 | static void rpmalAddProvides(rpmal al, rpmalNum pkgNum, rpmds provides) |
204 | { |
205 | struct availableIndexEntry_s indexEntry; |
206 | rpm_color_t dscolor; |
207 | int skipconf = (al->tsflags & RPMTRANS_FLAG_NOCONFIGS); |
208 | int dc = rpmdsCount(provides); |
209 | |
210 | indexEntry.pkgNum = pkgNum; |
211 | |
212 | for (int i = 0; i < dc; i++) { |
213 | /* Ignore colored provides not in our rainbow. */ |
214 | dscolor = rpmdsColorIndex(provides, i); |
215 | if (al->tscolor && dscolor && !(al->tscolor & dscolor)) |
216 | continue; |
217 | |
218 | /* Ignore config() provides if the files wont be installed */ |
219 | if (skipconf & (rpmdsFlagsIndex(provides, i) & RPMSENSE_CONFIG)) |
220 | continue; |
221 | |
222 | indexEntry.entryIx = i;; |
223 | rpmalDepHashAddEntry(al->providesHash, |
224 | rpmdsNIdIndex(provides, i), indexEntry); |
225 | } |
226 | } |
227 | |
228 | static void rpmalAddObsoletes(rpmal al, rpmalNum pkgNum, rpmds obsoletes) |
229 | { |
230 | struct availableIndexEntry_s indexEntry; |
231 | rpm_color_t dscolor; |
232 | int dc = rpmdsCount(obsoletes); |
233 | |
234 | indexEntry.pkgNum = pkgNum; |
235 | |
236 | for (int i = 0; i < dc; i++) { |
237 | /* Obsoletes shouldn't be colored but just in case... */ |
238 | dscolor = rpmdsColorIndex(obsoletes, i); |
239 | if (al->tscolor && dscolor && !(al->tscolor & dscolor)) |
240 | continue; |
241 | |
242 | indexEntry.entryIx = i;; |
243 | rpmalDepHashAddEntry(al->obsoletesHash, |
244 | rpmdsNIdIndex(obsoletes, i), indexEntry); |
245 | } |
246 | } |
247 | |
248 | void rpmalAdd(rpmal al, rpmte p) |
249 | { |
250 | rpmalNum pkgNum; |
251 | availablePackage alp; |
252 | |
253 | if (al->size == al->alloced) { |
254 | al->alloced += al->delta; |
255 | al->list = xrealloc(al->list, sizeof(*al->list) * al->alloced)rrealloc((al->list), (sizeof(*al->list) * al->alloced )); |
256 | } |
257 | pkgNum = al->size++; |
258 | |
259 | alp = al->list + pkgNum; |
260 | |
261 | alp->p = p; |
262 | |
263 | alp->provides = rpmdsLink(rpmteDS(p, RPMTAG_PROVIDENAME)); |
264 | alp->obsoletes = rpmdsLink(rpmteDS(p, RPMTAG_OBSOLETENAME)); |
265 | alp->fi = rpmteFiles(p); |
266 | |
267 | /* |
268 | * Transition-time safe-guard to catch private-pool uses. |
269 | * File sets with no files have NULL pool, that's fine. But WTF is up |
270 | * with the provides: every single package should have at least its |
271 | * own name as a provide, and thus never NULL, and normal use matches |
272 | * this expectation. However the test-suite is tripping up on NULL |
273 | * NULL pool from NULL alp->provides in numerous cases? |
274 | */ |
275 | { |
276 | rpmstrPool fipool = rpmfilesPool(alp->fi); |
277 | rpmstrPool dspool = rpmdsPool(alp->provides); |
278 | |
279 | assert(fipool == NULL || fipool == al->pool)({ if (fipool == ((void*)0) || fipool == al->pool) ; else __assert_fail ("fipool == NULL || fipool == al->pool", "rpmal.c", 279, __PRETTY_FUNCTION__ ); }); |
280 | assert(dspool == NULL || dspool == al->pool)({ if (dspool == ((void*)0) || dspool == al->pool) ; else __assert_fail ("dspool == NULL || dspool == al->pool", "rpmal.c", 280, __PRETTY_FUNCTION__ ); }); |
281 | } |
282 | |
283 | /* Try to be lazy as delayed hash creation is cheaper */ |
284 | if (al->providesHash != NULL((void*)0)) |
285 | rpmalAddProvides(al, pkgNum, alp->provides); |
286 | if (al->obsoletesHash != NULL((void*)0)) |
287 | rpmalAddObsoletes(al, pkgNum, alp->obsoletes); |
288 | if (al->fileHash != NULL((void*)0)) |
289 | rpmalAddFiles(al, pkgNum, alp->fi); |
290 | |
291 | assert(((rpmalNum)(alp - al->list)) == pkgNum)({ if (((rpmalNum)(alp - al->list)) == pkgNum) ; else __assert_fail ("((rpmalNum)(alp - al->list)) == pkgNum", "rpmal.c", 291 , __PRETTY_FUNCTION__); }); |
292 | } |
293 | |
294 | static void rpmalMakeFileIndex(rpmal al) |
295 | { |
296 | availablePackage alp; |
297 | int i, fileCnt = 0; |
298 | |
299 | for (i = 0; i < al->size; i++) { |
300 | alp = al->list + i; |
301 | if (alp->fi != NULL((void*)0)) |
302 | fileCnt += rpmfilesFC(alp->fi); |
303 | } |
304 | al->fileHash = rpmalFileHashCreate(fileCnt/4+128, |
305 | sidHash, sidCmp, NULL((void*)0), NULL((void*)0)); |
306 | for (i = 0; i < al->size; i++) { |
307 | alp = al->list + i; |
308 | rpmalAddFiles(al, i, alp->fi); |
309 | } |
310 | } |
311 | |
312 | static void rpmalMakeProvidesIndex(rpmal al) |
313 | { |
314 | availablePackage alp; |
315 | int i, providesCnt = 0; |
316 | |
317 | for (i = 0; i < al->size; i++) { |
318 | alp = al->list + i; |
319 | providesCnt += rpmdsCount(alp->provides); |
320 | } |
321 | |
322 | al->providesHash = rpmalDepHashCreate(providesCnt/4+128, |
323 | sidHash, sidCmp, NULL((void*)0), NULL((void*)0)); |
324 | for (i = 0; i < al->size; i++) { |
325 | alp = al->list + i; |
326 | rpmalAddProvides(al, i, alp->provides); |
327 | } |
328 | } |
329 | |
330 | static void rpmalMakeObsoletesIndex(rpmal al) |
331 | { |
332 | availablePackage alp; |
333 | int i, obsoletesCnt = 0; |
334 | |
335 | for (i = 0; i < al->size; i++) { |
336 | alp = al->list + i; |
337 | obsoletesCnt += rpmdsCount(alp->obsoletes); |
338 | } |
339 | |
340 | al->obsoletesHash = rpmalDepHashCreate(obsoletesCnt/4+128, |
341 | sidHash, sidCmp, NULL((void*)0), NULL((void*)0)); |
342 | for (i = 0; i < al->size; i++) { |
343 | alp = al->list + i; |
344 | rpmalAddObsoletes(al, i, alp->obsoletes); |
345 | } |
346 | } |
347 | |
348 | rpmte * rpmalAllObsoletes(rpmal al, rpmds ds) |
349 | { |
350 | rpmte * ret = NULL((void*)0); |
351 | rpmsid nameId; |
352 | availableIndexEntry result; |
353 | int resultCnt; |
354 | |
355 | if (al == NULL((void*)0) || ds == NULL((void*)0) || (nameId = rpmdsNId(ds)) == 0) |
356 | return ret; |
357 | |
358 | if (al->obsoletesHash == NULL((void*)0)) |
359 | rpmalMakeObsoletesIndex(al); |
360 | |
361 | rpmalDepHashGetEntry(al->obsoletesHash, nameId, &result, &resultCnt, NULL((void*)0)); |
362 | |
363 | if (resultCnt > 0) { |
364 | availablePackage alp; |
365 | int rc, found = 0; |
366 | |
367 | ret = xmalloc((resultCnt+1) * sizeof(*ret))rmalloc(((resultCnt+1) * sizeof(*ret))); |
368 | |
369 | for (int i = 0; i < resultCnt; i++) { |
370 | alp = al->list + result[i].pkgNum; |
371 | if (alp->p == NULL((void*)0)) // deleted |
372 | continue; |
373 | |
374 | rc = rpmdsCompareIndex(alp->obsoletes, result[i].entryIx, |
375 | ds, rpmdsIx(ds)); |
376 | |
377 | if (rc) { |
378 | rpmdsNotify(ds, "(added obsolete)", 0); |
379 | ret[found] = alp->p; |
380 | found++; |
381 | } |
382 | } |
383 | |
384 | if (found) |
385 | ret[found] = NULL((void*)0); |
386 | else |
387 | ret = _free(ret)rfree((ret)); |
388 | } |
389 | |
390 | return ret; |
391 | } |
392 | |
393 | static rpmte * rpmalAllFileSatisfiesDepend(const rpmal al, const char *fileName, const rpmds filterds) |
394 | { |
395 | const char *slash; |
396 | rpmte * ret = NULL((void*)0); |
397 | |
398 | if (al == NULL((void*)0) || fileName == NULL((void*)0) || *fileName != '/') |
399 | return NULL((void*)0); |
400 | |
401 | /* Split path into dirname and basename components for lookup */ |
402 | if ((slash = strrchr(fileName, '/')) != NULL((void*)0)) { |
403 | availableIndexFileEntry result; |
404 | int resultCnt = 0; |
405 | size_t bnStart = (slash - fileName) + 1; |
406 | rpmsid baseName; |
407 | |
408 | if (al->fileHash == NULL((void*)0)) |
409 | rpmalMakeFileIndex(al); |
410 | |
411 | baseName = rpmstrPoolId(al->pool, fileName + bnStart, 0); |
412 | if (!baseName) |
413 | return NULL((void*)0); /* no match possible */ |
414 | |
415 | rpmalFileHashGetEntry(al->fileHash, baseName, &result, &resultCnt, NULL((void*)0)); |
416 | |
417 | if (resultCnt > 0) { |
418 | int i, found; |
419 | ret = xmalloc((resultCnt+1) * sizeof(*ret))rmalloc(((resultCnt+1) * sizeof(*ret))); |
420 | fingerPrint * fp = NULL((void*)0); |
421 | rpmsid dirName = rpmstrPoolIdn(al->pool, fileName, bnStart, 1); |
422 | |
423 | if (!al->fpc) |
424 | al->fpc = fpCacheCreate(1001, NULL((void*)0)); |
425 | fpLookup(al->fpc, rpmstrPoolStr(al->pool, dirName), fileName + bnStart, &fp); |
426 | |
427 | for (found = i = 0; i < resultCnt; i++) { |
428 | availablePackage alp = al->list + result[i].pkgNum; |
429 | if (alp->p == NULL((void*)0)) /* deleted */ |
430 | continue; |
431 | /* ignore self-conflicts/obsoletes */ |
432 | if (filterds && rpmteDS(alp->p, rpmdsTagN(filterds)) == filterds) |
433 | continue; |
434 | if (result[i].dirName != dirName && |
435 | !fpLookupEquals(al->fpc, fp, rpmstrPoolStr(al->pool, result[i].dirName), fileName + bnStart)) |
436 | continue; |
437 | |
438 | ret[found] = alp->p; |
439 | found++; |
440 | } |
441 | _free(fp)rfree((fp)); |
442 | ret[found] = NULL((void*)0); |
443 | } |
444 | } |
445 | |
446 | return ret; |
447 | } |
448 | |
449 | rpmte * rpmalAllSatisfiesDepend(const rpmal al, const rpmds ds) |
450 | { |
451 | rpmte * ret = NULL((void*)0); |
452 | int i, ix, found; |
453 | rpmsid nameId; |
454 | const char *name; |
455 | availableIndexEntry result; |
456 | int resultCnt; |
457 | int obsolete; |
458 | rpmTagVal dtag; |
459 | rpmds filterds = NULL((void*)0); |
460 | |
461 | availablePackage alp; |
462 | int rc; |
463 | |
464 | if (al == NULL((void*)0) || ds == NULL((void*)0) || (nameId = rpmdsNId(ds)) == 0) |
465 | return ret; |
466 | |
467 | dtag = rpmdsTagN(ds); |
468 | obsolete = (dtag == RPMTAG_OBSOLETENAME); |
469 | if (dtag == RPMTAG_OBSOLETENAME || dtag == RPMTAG_CONFLICTNAME) |
470 | filterds = ds; |
471 | name = rpmstrPoolStr(al->pool, nameId); |
472 | if (!obsolete && *name == '/') { |
473 | /* First, look for files "contained" in package ... */ |
474 | ret = rpmalAllFileSatisfiesDepend(al, name, filterds); |
475 | if (ret != NULL((void*)0) && *ret != NULL((void*)0)) { |
476 | rpmdsNotify(ds, "(added files)", 0); |
477 | return ret; |
478 | } |
479 | /* ... then, look for files "provided" by package. */ |
480 | ret = _free(ret)rfree((ret)); |
Value stored to 'ret' is never read | |
481 | } |
482 | |
483 | if (al->providesHash == NULL((void*)0)) |
484 | rpmalMakeProvidesIndex(al); |
485 | |
486 | rpmalDepHashGetEntry(al->providesHash, nameId, &result, |
487 | &resultCnt, NULL((void*)0)); |
488 | |
489 | if (resultCnt==0) return NULL((void*)0); |
490 | |
491 | ret = xmalloc((resultCnt+1) * sizeof(*ret))rmalloc(((resultCnt+1) * sizeof(*ret))); |
492 | |
493 | for (found=i=0; i<resultCnt; i++) { |
494 | alp = al->list + result[i].pkgNum; |
495 | if (alp->p == NULL((void*)0)) /* deleted */ |
496 | continue; |
497 | /* ignore self-conflicts/obsoletes */ |
498 | if (filterds && rpmteDS(alp->p, rpmdsTagN(filterds)) == filterds) |
499 | continue; |
500 | ix = result[i].entryIx; |
501 | |
502 | if (obsolete) { |
503 | /* Obsoletes are on package NEVR only */ |
504 | rpmds thisds; |
505 | if (!rstreq(rpmdsNIndex(alp->provides, ix), rpmteN(alp->p))) |
506 | continue; |
507 | thisds = rpmteDS(alp->p, RPMTAG_NAME); |
508 | rc = rpmdsCompareIndex(thisds, rpmdsIx(thisds), ds, rpmdsIx(ds)); |
509 | } else { |
510 | rc = rpmdsCompareIndex(alp->provides, ix, ds, rpmdsIx(ds)); |
511 | } |
512 | |
513 | if (rc) |
514 | ret[found++] = alp->p; |
515 | } |
516 | |
517 | if (found) { |
518 | rpmdsNotify(ds, "(added provide)", 0); |
519 | ret[found] = NULL((void*)0); |
520 | } else { |
521 | ret = _free(ret)rfree((ret)); |
522 | } |
523 | |
524 | return ret; |
525 | } |
526 | |
527 | rpmte |
528 | rpmalSatisfiesDepend(const rpmal al, const rpmte te, const rpmds ds) |
529 | { |
530 | rpmte *providers = rpmalAllSatisfiesDepend(al, ds); |
531 | rpmte best = NULL((void*)0); |
532 | int bestscore = 0; |
533 | |
534 | if (providers) { |
535 | rpm_color_t dscolor = rpmdsColor(ds); |
536 | for (rpmte *p = providers; *p; p++) { |
537 | int score = 0; |
538 | |
539 | /* |
540 | * For colored dependencies, prefer a matching colored provider. |
541 | * Otherwise prefer provider of ts preferred color. |
542 | */ |
543 | if (al->tscolor) { |
544 | rpm_color_t tecolor = rpmteColor(*p); |
545 | if (dscolor) { |
546 | if (dscolor == tecolor) score += 2; |
547 | } else if (al->prefcolor) { |
548 | if (al->prefcolor == tecolor) score += 2; |
549 | } |
550 | } |
551 | |
552 | /* Being self-provided is a bonus */ |
553 | if (*p == te) |
554 | score += 1; |
555 | |
556 | if (score > bestscore) { |
557 | bestscore = score; |
558 | best = *p; |
559 | } |
560 | } |
561 | /* if not decided by now, just pick first match */ |
562 | if (!best) best = providers[0]; |
563 | free(providers); |
564 | } |
565 | return best; |
566 | } |