Annotation of libwww/Library/src/HTFormat.c, revision 1.75
1.55 frystyk 1: /* HTFormat.c
2: ** MANAGE DIFFERENT FILE FORMATS
3: **
1.62 frystyk 4: ** (c) COPYRIGHT MIT 1995.
1.55 frystyk 5: ** Please first read the full copyright statement in the file COPYRIGH.
1.1 timbl 6: **
7: ** Bugs:
8: ** Assumes the incoming stream is ASCII, rather than a local file
9: ** format, and so ALWAYS converts from ASCII on non-ASCII machines.
10: ** Therefore, non-ASCII machines can't read local files.
1.2 timbl 11: **
1.45 duns 12: ** HISTORY:
1.52 frystyk 13: ** 8 Jul 94 FM Insulate free() from _free structure element.
14: ** 8 Nov 94 HFN Changed a lot to make reentrant
1.2 timbl 15: */
16:
1.58 frystyk 17: /* Library Include files */
18: #include "tcp.h"
1.52 frystyk 19: #include "HTUtils.h"
1.58 frystyk 20: #include "HTString.h"
1.52 frystyk 21: #include "HTTCP.h"
1.58 frystyk 22: #include "HTFWrite.h"
1.52 frystyk 23: #include "HTGuess.h"
1.68 frystyk 24: #include "HTNetMan.h"
1.52 frystyk 25: #include "HTError.h"
1.67 frystyk 26: #include "HTReqMan.h"
1.52 frystyk 27: #include "HTFormat.h" /* Implemented here */
1.2 timbl 28:
1.72 frystyk 29: #define NO_VALUE_FOUND -1e30 /* Stream Stack Value if none found */
1.63 frystyk 30:
1.72 frystyk 31: PRIVATE HTList * HTConversions = NULL;
32: PRIVATE HTList * HTCharsets = NULL;
33: PRIVATE HTList * HTEncodings = NULL;
34: PRIVATE HTList * HTLanguages = NULL;
1.63 frystyk 35:
36: PRIVATE double HTMaxSecs = 1e10; /* No effective limit */
1.17 luotonen 37:
1.60 frystyk 38: struct _HTStream {
39: CONST HTStreamClass * isa;
40: };
41:
1.52 frystyk 42: /* ------------------------------------------------------------------------- */
1.63 frystyk 43: /* ACCEPT LISTS OF CONVERSIONS, ENCODINGS, LANGUAGE, AND CHARSET */
1.61 frystyk 44: /* ------------------------------------------------------------------------- */
1.17 luotonen 45:
1.52 frystyk 46: /*
1.63 frystyk 47: ** For all `accept lists' there is a local list and a global list. The
48: ** local list is a part of the request structure and the global list is
49: ** internal to the HTFormat module. The global lists can be used when
50: ** specifying accept lists for ALL requests and the local list can be
51: ** used to add specific accept headers to the request.
52: */
53:
1.61 frystyk 54:
1.2 timbl 55: /* Define a presentation system command for a content-type
56: ** -------------------------------------------------------
1.52 frystyk 57: ** INPUT:
58: ** conversions: The list of conveters and presenters
59: ** representation: the MIME-style format name
60: ** command: the MAILCAP-style command template
61: ** quality: A degradation faction [0..1]
62: ** maxbytes: A limit on the length acceptable as input (0 infinite)
63: ** maxsecs: A limit on the time user will wait (0 for infinity)
1.2 timbl 64: */
1.72 frystyk 65: PUBLIC void HTPresentation_add (HTList * conversions,
66: CONST char * representation,
67: CONST char * command,
68: CONST char * test_command,
69: double quality,
70: double secs,
71: double secs_per_byte)
1.52 frystyk 72: {
1.63 frystyk 73: HTPresentation * pres = (HTPresentation *)calloc(1,sizeof(HTPresentation));
1.2 timbl 74: if (pres == NULL) outofmem(__FILE__, "HTSetPresentation");
75:
76: pres->rep = HTAtom_for(representation);
77: pres->rep_out = WWW_PRESENT; /* Fixed for now ... :-) */
78: pres->converter = HTSaveAndExecute; /* Fixed for now ... */
79: pres->quality = quality;
80: pres->secs = secs;
81: pres->secs_per_byte = secs_per_byte;
82: pres->rep = HTAtom_for(representation);
1.49 howcome 83: pres->command = NULL;
1.2 timbl 84: StrAllocCopy(pres->command, command);
1.49 howcome 85: pres->test_command = NULL;
86: StrAllocCopy(pres->test_command, test_command);
1.12 timbl 87: HTList_addObject(conversions, pres);
1.2 timbl 88: }
89:
1.73 frystyk 90: PUBLIC void HTPresentation_deleteAll (HTList * list)
91: {
92: if (list) {
93: HTList *cur = list;
94: HTPresentation *pres;
95: while ((pres = (HTPresentation*) HTList_nextObject(cur))) {
96: FREE(pres->command);
97: free(pres);
98: }
99: HTList_delete(list);
100: }
101: }
1.2 timbl 102:
103: /* Define a built-in function for a content-type
104: ** ---------------------------------------------
105: */
1.72 frystyk 106: PUBLIC void HTConversion_add (HTList * conversions,
107: CONST char * representation_in,
108: CONST char * representation_out,
109: HTConverter * converter,
110: double quality,
111: double secs,
112: double secs_per_byte)
1.52 frystyk 113: {
1.63 frystyk 114: HTPresentation * pres = (HTPresentation *)calloc(1,sizeof(HTPresentation));
1.2 timbl 115: if (pres == NULL) outofmem(__FILE__, "HTSetPresentation");
116:
117: pres->rep = HTAtom_for(representation_in);
118: pres->rep_out = HTAtom_for(representation_out);
119: pres->converter = converter;
120: pres->command = NULL; /* Fixed */
1.49 howcome 121: pres->test_command = NULL;
1.2 timbl 122: pres->quality = quality;
123: pres->secs = secs;
124: pres->secs_per_byte = secs_per_byte;
1.12 timbl 125: HTList_addObject(conversions, pres);
1.56 frystyk 126: }
127:
1.73 frystyk 128: PUBLIC void HTConversion_deleteAll (HTList * list)
129: {
130: HTPresentation_deleteAll(list);
131: }
132:
133: PUBLIC void HTEncoding_add (HTList * list,
134: CONST char * enc,
135: double quality)
136: {
137: HTAcceptNode * node;
138: if (!list || !enc || !*enc) {
139: if (WWWTRACE)
1.75 ! frystyk 140: TTYPrint(TDEST, "Encodings... Bad argument\n");
1.73 frystyk 141: return;
142: }
143: node = (HTAcceptNode*)calloc(1, sizeof(HTAcceptNode));
144: if (!node) outofmem(__FILE__, "HTAcceptEncoding");
145: HTList_addObject(list, (void*)node);
146:
147: node->atom = HTAtom_for(enc);
148: node->quality = quality;
149: }
150:
151: PUBLIC void HTEncoding_deleteAll (HTList * list)
152: {
153: if (list) {
154: HTList *cur = list;
155: HTAcceptNode *pres;
156: while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) {
157: free(pres);
158: }
159: HTList_delete(list);
160: }
161: }
162:
163: PUBLIC void HTLanguage_add (HTList * list,
164: CONST char * lang,
165: double quality)
166: {
167: HTAcceptNode * node;
168: if (!list || !lang || !*lang) {
169: if (WWWTRACE)
1.75 ! frystyk 170: TTYPrint(TDEST, "Languages... Bad argument\n");
1.73 frystyk 171: return;
172: }
173: node = (HTAcceptNode*)calloc(1, sizeof(HTAcceptNode));
174: if (!node) outofmem(__FILE__, "HTAcceptLanguage");
175:
176: HTList_addObject(list, (void*)node);
177: node->atom = HTAtom_for(lang);
178: node->quality = quality;
179: }
180:
181: PUBLIC void HTLanguage_deleteAll (HTList * list)
182: {
183: HTEncoding_deleteAll(list);
184: }
185:
186: PUBLIC void HTCharset_add (HTList * list,
187: CONST char * charset,
188: double quality)
189: {
190: HTAcceptNode * node;
191: if (!list || !charset || !*charset) {
192: if (WWWTRACE)
1.75 ! frystyk 193: TTYPrint(TDEST, "Charset..... Bad argument\n");
1.73 frystyk 194: return;
195: }
196: node = (HTAcceptNode*)calloc(1, sizeof(HTAcceptNode));
197: if (!node) outofmem(__FILE__, "HTAcceptCharsetuage");
198:
199: HTList_addObject(list, (void*)node);
200: node->atom = HTAtom_for(charset);
201: node->quality = quality;
202: }
203:
204: PUBLIC void HTCharset_deleteAll (HTList * list)
205: {
206: HTEncoding_deleteAll(list);
207: }
208:
209: /* ------------------------------------------------------------------------- */
210: /* GLOBAL LIST OF CONVERTERS ETC. */
211: /* ------------------------------------------------------------------------- */
212:
1.72 frystyk 213: /*
214: ** Global Accept Format Types Conversions
215: ** list can be NULL
216: */
1.73 frystyk 217: PUBLIC void HTFormat_setConversion (HTList * list)
1.72 frystyk 218: {
219: HTConversions = list;
220: }
221:
222: PUBLIC HTList * HTFormat_conversion (void)
223: {
224: return HTConversions;
225: }
226:
227: /*
228: ** Global Accept Encodings
229: ** list can be NULL
230: */
231: PUBLIC void HTFormat_setEncoding (HTList *list)
232: {
233: HTEncodings = list;
234: }
235:
236: PUBLIC HTList * HTFormat_encoding (void)
237: {
238: return HTEncodings;
239: }
240:
241: /*
242: ** Global Accept Languages
243: ** list can be NULL
244: */
245: PUBLIC void HTFormat_setLanguage (HTList *list)
246: {
247: HTLanguages = list;
248: }
249:
250: PUBLIC HTList * HTFormat_language (void)
251: {
252: return HTLanguages;
253: }
254:
255: /*
256: ** Global Accept Charsets
257: ** list can be NULL
258: */
259: PUBLIC void HTFormat_setCharset (HTList *list)
260: {
261: HTCharsets = list;
262: }
263:
264: PUBLIC HTList * HTFormat_charset (void)
265: {
266: return HTCharsets;
267: }
1.56 frystyk 268:
1.73 frystyk 269: /*
270: ** Convenience function to clean up
271: */
272: PUBLIC void HTFormat_deleteAll (void)
1.17 luotonen 273: {
1.73 frystyk 274: if (HTConversions) {
275: HTConversion_deleteAll(HTConversions);
276: HTConversions = NULL;
277: }
278: if (HTLanguages) {
279: HTLanguage_deleteAll(HTLanguages);
280: HTLanguages = NULL;
1.63 frystyk 281: }
1.73 frystyk 282: if (HTEncodings) {
283: HTEncoding_deleteAll(HTEncodings);
284: HTEncodings = NULL;
1.63 frystyk 285: }
1.73 frystyk 286: if (HTCharsets) {
287: HTCharset_deleteAll(HTCharsets);
288: HTCharsets = NULL;
1.63 frystyk 289: }
1.17 luotonen 290: }
291:
1.63 frystyk 292: /* ------------------------------------------------------------------------- */
293: /* FORMAT NEGOTIATION */
294: /* ------------------------------------------------------------------------- */
295:
296: PRIVATE BOOL better_match ARGS2(HTFormat, f,
297: HTFormat, g)
298: {
299: CONST char *p, *q;
300:
301: if (f && g && (p = HTAtom_name(f)) && (q = HTAtom_name(g))) {
302: int i,j;
303: for(i=0 ; *p; p++) if (*p == '*') i++;
304: for(j=0 ; *q; q++) if (*q == '*') j++;
305: if (i < j) return YES;
306: }
307: return NO;
308: }
309:
310:
1.48 frystyk 311: PRIVATE BOOL wild_match ARGS2(HTAtom *, tmplate,
1.17 luotonen 312: HTAtom *, actual)
313: {
314: char *t, *a, *st, *sa;
315: BOOL match = NO;
316:
1.48 frystyk 317: if (tmplate && actual && (t = HTAtom_name(tmplate))) {
1.22 luotonen 318: if (!strcmp(t, "*"))
319: return YES;
1.17 luotonen 320:
1.22 luotonen 321: if (strchr(t, '*') &&
322: (a = HTAtom_name(actual)) &&
323: (st = strchr(t, '/')) && (sa = strchr(a,'/'))) {
1.17 luotonen 324:
1.22 luotonen 325: *sa = 0;
326: *st = 0;
327:
328: if ((*(st-1)=='*' &&
329: (*(st+1)=='*' || !strcasecomp(st+1, sa+1))) ||
330: (*(st+1)=='*' && !strcasecomp(t,a)))
331: match = YES;
332:
333: *sa = '/';
334: *st = '/';
335: }
336: }
1.23 luotonen 337: return match;
1.17 luotonen 338: }
339:
1.36 luotonen 340: /*
341: * Added by takada@seraph.ntt.jp (94/04/08)
342: */
1.48 frystyk 343: PRIVATE BOOL lang_match ARGS2(HTAtom *, tmplate,
1.36 luotonen 344: HTAtom *, actual)
345: {
346: char *t, *a, *st, *sa;
347: BOOL match = NO;
348:
1.48 frystyk 349: if (tmplate && actual &&
350: (t = HTAtom_name(tmplate)) && (a = HTAtom_name(actual))) {
1.36 luotonen 351: st = strchr(t, '_');
352: sa = strchr(a, '_');
353: if ((st != NULL) && (sa != NULL)) {
354: if (!strcasecomp(t, a))
355: match = YES;
356: else
357: match = NO;
358: }
359: else {
360: if (st != NULL) *st = 0;
361: if (sa != NULL) *sa = 0;
362: if (!strcasecomp(t, a))
363: match = YES;
364: else
365: match = NO;
366: if (st != NULL) *st = '_';
367: if (sa != NULL) *sa = '_';
368: }
369: }
370: return match;
371: }
372: /* end of addition */
373:
374:
1.17 luotonen 375:
1.59 frystyk 376: PRIVATE double type_value ARGS2(HTAtom *, content_type,
1.17 luotonen 377: HTList *, accepted)
378: {
379: HTList * cur = accepted;
380: HTPresentation * pres;
381: HTPresentation * wild = NULL;
382:
383: if (!content_type || !accepted) return -1;
384:
385: while ((pres = (HTPresentation*)HTList_nextObject(cur))) {
386: if (pres->rep == content_type)
387: return pres->quality;
388: else if (wild_match(pres->rep, content_type))
389: wild = pres;
390: }
391: if (wild) return wild->quality;
392: else return -1;
393: }
394:
395:
1.59 frystyk 396: PRIVATE double lang_value ARGS2(HTAtom *, language,
1.17 luotonen 397: HTList *, accepted)
398: {
399: HTList * cur = accepted;
400: HTAcceptNode * node;
401: HTAcceptNode * wild = NULL;
402:
403: if (!language || !accepted || HTList_isEmpty(accepted)) {
404: return 0.1;
405: }
406:
407: while ((node = (HTAcceptNode*)HTList_nextObject(cur))) {
408: if (node->atom == language) {
409: return node->quality;
410: }
1.36 luotonen 411: /*
412: * patch by takada@seraph.ntt.jp (94/04/08)
413: * the original line was
414: * else if (wild_match(node->atom, language)) {
415: * and the new line is
416: */
417: else if (lang_match(node->atom, language)) {
1.17 luotonen 418: wild = node;
419: }
420: }
421:
422: if (wild) {
423: return wild->quality;
424: }
425: else {
426: return 0.1;
427: }
428: }
429:
430:
1.59 frystyk 431: PRIVATE double encoding_value ARGS2(HTAtom *, encoding,
1.17 luotonen 432: HTList *, accepted)
433: {
434: HTList * cur = accepted;
435: HTAcceptNode * node;
436: HTAcceptNode * wild = NULL;
437: char * e;
438:
439: if (!encoding || !accepted || HTList_isEmpty(accepted))
440: return 1;
441:
442: e = HTAtom_name(encoding);
443: if (!strcmp(e, "7bit") || !strcmp(e, "8bit") || !strcmp(e, "binary"))
444: return 1;
445:
446: while ((node = (HTAcceptNode*)HTList_nextObject(cur))) {
447: if (node->atom == encoding)
448: return node->quality;
449: else if (wild_match(node->atom, encoding))
450: wild = node;
451: }
452: if (wild) return wild->quality;
453: else return 1;
454: }
455:
456:
457: PUBLIC BOOL HTRank ARGS4(HTList *, possibilities,
458: HTList *, accepted_content_types,
459: HTList *, accepted_languages,
460: HTList *, accepted_encodings)
461: {
462: int accepted_cnt = 0;
463: HTList * accepted;
464: HTList * sorted;
465: HTList * cur;
466: HTContentDescription * d;
467:
468: if (!possibilities) return NO;
469:
470: accepted = HTList_new();
471: cur = possibilities;
472: while ((d = (HTContentDescription*)HTList_nextObject(cur))) {
1.59 frystyk 473: double tv = type_value(d->content_type, accepted_content_types);
474: double lv = lang_value(d->content_language, accepted_languages);
475: double ev = encoding_value(d->content_encoding, accepted_encodings);
1.17 luotonen 476:
477: if (tv > 0) {
478: d->quality *= tv * lv * ev;
479: HTList_addObject(accepted, d);
480: accepted_cnt++;
481: }
1.18 luotonen 482: else {
483: if (d->filename) free(d->filename);
484: free(d);
485: }
1.17 luotonen 486: }
487:
1.75 ! frystyk 488: if (PROT_TRACE) TTYPrint(TDEST, "Ranking.....\n");
! 489: if (PROT_TRACE) TTYPrint(TDEST,
1.18 luotonen 490: "\nRANK QUALITY CONTENT-TYPE LANGUAGE ENCODING FILE\n");
1.17 luotonen 491:
492: sorted = HTList_new();
493: while (accepted_cnt-- > 0) {
494: HTContentDescription * worst = NULL;
495: cur = accepted;
496: while ((d = (HTContentDescription*)HTList_nextObject(cur))) {
497: if (!worst || d->quality < worst->quality)
498: worst = d;
499: }
500: if (worst) {
1.58 frystyk 501: if (PROT_TRACE)
1.75 ! frystyk 502: TTYPrint(TDEST, "%d. %.4f %-20.20s %-8.8s %-10.10s %s\n",
1.58 frystyk 503: accepted_cnt+1,
504: worst->quality,
505: (worst->content_type
1.17 luotonen 506: ? HTAtom_name(worst->content_type) : "-"),
1.58 frystyk 507: (worst->content_language
1.17 luotonen 508: ? HTAtom_name(worst->content_language) :"-"),
1.58 frystyk 509: (worst->content_encoding
1.17 luotonen 510: ? HTAtom_name(worst->content_encoding) :"-"),
1.58 frystyk 511: (worst->filename
1.17 luotonen 512: ? worst->filename :"-"));
513: HTList_removeObject(accepted, (void*)worst);
514: HTList_addObject(sorted, (void*)worst);
515: }
516: }
1.75 ! frystyk 517: if (PROT_TRACE) TTYPrint(TDEST, "\n");
1.17 luotonen 518: HTList_delete(accepted);
519: HTList_delete(possibilities->next);
520: possibilities->next = sorted->next;
521: sorted->next = NULL;
522: HTList_delete(sorted);
523:
524: if (!HTList_isEmpty(possibilities)) return YES;
525: else return NO;
526: }
527:
528:
1.2 timbl 529: /* Create a filter stack
530: ** ---------------------
531: **
1.7 secret 532: ** If a wildcard match is made, a temporary HTPresentation
1.2 timbl 533: ** structure is made to hold the destination format while the
534: ** new stack is generated. This is just to pass the out format to
535: ** MIME so far. Storing the format of a stream in the stream might
536: ** be a lot neater.
1.10 timbl 537: **
1.29 frystyk 538: ** The star/star format is special, in that if you can take
1.40 frystyk 539: ** that you can take anything.
1.2 timbl 540: */
1.52 frystyk 541: PUBLIC HTStream * HTStreamStack ARGS5(HTFormat, rep_in,
542: HTFormat, rep_out,
543: HTStream *, output_stream,
1.34 luotonen 544: HTRequest *, request,
545: BOOL, guess)
1.2 timbl 546: {
1.14 timbl 547: HTList * conversion[2];
548: int which_list;
1.59 frystyk 549: double best_quality = -1e30; /* Pretty bad! */
1.65 frystyk 550: HTPresentation *pres, *best_match=NULL;
1.14 timbl 551:
1.74 frystyk 552: if (guess && rep_in == WWW_UNKNOWN) {
1.75 ! frystyk 553: if (STREAM_TRACE)TTYPrint(TDEST,"StreamStack. Using guessing stream\n");
1.52 frystyk 554: return HTGuess_new(request, NULL, rep_in, rep_out, output_stream);
1.34 luotonen 555: }
1.47 frystyk 556: if (rep_out == WWW_SOURCE || rep_out == rep_in) {
1.74 frystyk 557: if (STREAM_TRACE)
1.75 ! frystyk 558: TTYPrint(TDEST,"StreamStack. Identical in/out format: %s\n",
1.74 frystyk 559: HTAtom_name(rep_in));
1.69 frystyk 560: return output_stream ? output_stream : HTBlackHole();
1.74 frystyk 561: }
562: if (STREAM_TRACE) {
1.75 ! frystyk 563: TTYPrint(TDEST, "StreamStack. Constructing stream stack for %s to %s\n",
1.74 frystyk 564: HTAtom_name(rep_in), HTAtom_name(rep_out));
1.47 frystyk 565: }
1.2 timbl 566:
1.14 timbl 567: conversion[0] = request->conversions;
568: conversion[1] = HTConversions;
1.17 luotonen 569:
1.15 luotonen 570: for(which_list = 0; which_list<2; which_list++) {
571: HTList * cur = conversion[which_list];
572:
573: while ((pres = (HTPresentation*)HTList_nextObject(cur))) {
1.65 frystyk 574: if ((pres->rep==rep_in || wild_match(pres->rep, rep_in)) &&
575: (pres->rep_out==rep_out || wild_match(pres->rep_out,rep_out))){
576: if (!best_match || better_match(pres->rep, best_match->rep) ||
1.33 luotonen 577: (!better_match(best_match->rep, pres->rep) &&
578: pres->quality > best_quality)) {
1.58 frystyk 579: #ifdef GOT_SYSTEM
1.65 frystyk 580: int result=0;
581: if (pres->test_command) {
582: result = system(pres->test_command);
583: if (STREAM_TRACE)
1.75 ! frystyk 584: TTYPrint(TDEST, "StreamStack. system(%s) returns %d\n", pres->test_command, result);
1.65 frystyk 585: }
586: if (!result) {
1.49 howcome 587: best_match = pres;
588: best_quality = pres->quality;
589: }
1.65 frystyk 590: #else
591: best_match = pres;
592: best_quality = pres->quality;
1.58 frystyk 593: #endif /* GOT_SYSTEM */
1.10 timbl 594: }
595: }
1.2 timbl 596: }
597: }
1.65 frystyk 598: if (best_match)
599: return (*best_match->converter)(request, best_match->command,
600: rep_in, rep_out, output_stream);
601: if (STREAM_TRACE)
1.75 ! frystyk 602: TTYPrint(TDEST, "StreamStack. No match found, dumping to local file\n");
1.65 frystyk 603: return HTSaveLocally(request, NULL, rep_in, rep_out, output_stream);
1.2 timbl 604: }
605:
606:
607: /* Find the cost of a filter stack
608: ** -------------------------------
609: **
610: ** Must return the cost of the same stack which StreamStack would set up.
611: **
612: ** On entry,
613: ** length The size of the data to be converted
614: */
1.59 frystyk 615: PUBLIC double HTStackValue ARGS5(
1.14 timbl 616: HTList *, theseConversions,
1.10 timbl 617: HTFormat, rep_in,
1.2 timbl 618: HTFormat, rep_out,
1.59 frystyk 619: double, initial_value,
1.2 timbl 620: long int, length)
621: {
1.14 timbl 622: int which_list;
623: HTList* conversion[2];
624:
1.65 frystyk 625: if (STREAM_TRACE) {
1.75 ! frystyk 626: TTYPrint(TDEST, "StackValue.. Evaluating stream stack for %s worth %.3f to %s\n",
1.65 frystyk 627: HTAtom_name(rep_in), initial_value,
628: HTAtom_name(rep_out));
629: }
1.2 timbl 630: if (rep_out == WWW_SOURCE ||
1.10 timbl 631: rep_out == rep_in) return 0.0;
1.2 timbl 632:
1.14 timbl 633: conversion[0] = theseConversions;
634: conversion[1] = HTConversions;
635:
636: for(which_list = 0; which_list<2; which_list++)
637: if (conversion[which_list]) {
1.15 luotonen 638: HTList * cur = conversion[which_list];
1.2 timbl 639: HTPresentation * pres;
1.15 luotonen 640: while ((pres = (HTPresentation*)HTList_nextObject(cur))) {
641: if (pres->rep == rep_in &&
1.17 luotonen 642: (pres->rep_out == rep_out || wild_match(pres->rep_out, rep_out))) {
1.59 frystyk 643: double value = initial_value * pres->quality;
1.2 timbl 644: if (HTMaxSecs != 0.0)
1.15 luotonen 645: value = value - (length*pres->secs_per_byte + pres->secs)
1.2 timbl 646: /HTMaxSecs;
647: return value;
648: }
649: }
650: }
1.63 frystyk 651: return NO_VALUE_FOUND; /* Really bad */
1.1 timbl 652: }
1.2 timbl 653:
1.10 timbl 654:
Webmaster