Annotation of libwww/Library/src/HTFormat.c, revision 1.59.2.1
1.55 frystyk 1: /* HTFormat.c
2: ** MANAGE DIFFERENT FILE FORMATS
3: **
4: ** (c) COPYRIGHT CERN 1994.
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"
24: #include "HTThread.h"
25: #include "HTError.h"
26: #include "HTFormat.h" /* Implemented here */
1.2 timbl 27:
1.52 frystyk 28: /* Public variables */
1.59 frystyk 29: PUBLIC double HTMaxSecs = 1e10; /* No effective limit */
30: PUBLIC double HTMaxLength = 1e10; /* No effective limit */
1.52 frystyk 31: PUBLIC HTList * HTConversions = NULL;
1.1 timbl 32:
1.52 frystyk 33: /* Accept-Encoding and Accept-Language */
1.17 luotonen 34: typedef struct _HTAcceptNode {
35: HTAtom * atom;
1.59 frystyk 36: double quality;
1.17 luotonen 37: } HTAcceptNode;
38:
1.59.2.1! frystyk 39: struct _HTStream {
! 40: CONST HTStreamClass * isa;
! 41: };
! 42:
1.52 frystyk 43: /* ------------------------------------------------------------------------- */
1.17 luotonen 44:
1.52 frystyk 45: /*
46: ** This function replaces the code in HTRequest_delete() in order to keep
47: ** the data structure hidden (it is NOT a joke!)
48: ** Henrik 14/03-94
1.2 timbl 49: */
1.52 frystyk 50: PUBLIC void HTFormatDelete ARGS1(HTRequest *, request)
1.31 frystyk 51: {
1.56 frystyk 52: if (request && request->conversions) {
53: HTList *cur = request->conversions;
54: HTPresentation *pres;
55: while ((pres = (HTPresentation*) HTList_nextObject(cur))) {
56: FREE(pres->command); /* Leak fixed AL 6 Feb 1994 */
57: free(pres);
58: }
59: HTList_delete(request->conversions);
60: request->conversions = NULL;
1.31 frystyk 61: }
62: }
63:
1.2 timbl 64:
65: /* Define a presentation system command for a content-type
66: ** -------------------------------------------------------
1.52 frystyk 67: ** INPUT:
68: ** conversions: The list of conveters and presenters
69: ** representation: the MIME-style format name
70: ** command: the MAILCAP-style command template
71: ** quality: A degradation faction [0..1]
72: ** maxbytes: A limit on the length acceptable as input (0 infinite)
73: ** maxsecs: A limit on the time user will wait (0 for infinity)
1.2 timbl 74: */
1.49 howcome 75: PUBLIC void HTSetPresentation ARGS7(
1.12 timbl 76: HTList *, conversions,
77: CONST char *, representation,
78: CONST char *, command,
1.52 frystyk 79: CONST char *, test_command, /* HWL 27/9/94: mailcap functionality */
1.59 frystyk 80: double, quality,
81: double, secs,
82: double, secs_per_byte)
1.52 frystyk 83: {
1.2 timbl 84: HTPresentation * pres = (HTPresentation *)malloc(sizeof(HTPresentation));
85: if (pres == NULL) outofmem(__FILE__, "HTSetPresentation");
86:
87: pres->rep = HTAtom_for(representation);
88: pres->rep_out = WWW_PRESENT; /* Fixed for now ... :-) */
89: pres->converter = HTSaveAndExecute; /* Fixed for now ... */
90: pres->quality = quality;
91: pres->secs = secs;
92: pres->secs_per_byte = secs_per_byte;
93: pres->rep = HTAtom_for(representation);
1.49 howcome 94: pres->command = NULL;
1.2 timbl 95: StrAllocCopy(pres->command, command);
1.49 howcome 96: pres->test_command = NULL;
97: StrAllocCopy(pres->test_command, test_command);
1.12 timbl 98: HTList_addObject(conversions, pres);
1.2 timbl 99: }
100:
101:
102: /* Define a built-in function for a content-type
103: ** ---------------------------------------------
104: */
1.12 timbl 105: PUBLIC void HTSetConversion ARGS7(
106: HTList *, conversions,
107: CONST char *, representation_in,
108: CONST char *, representation_out,
1.6 timbl 109: HTConverter*, converter,
1.59 frystyk 110: double, quality,
111: double, secs,
112: double, secs_per_byte)
1.52 frystyk 113: {
1.2 timbl 114: HTPresentation * pres = (HTPresentation *)malloc(sizeof(HTPresentation));
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:
128:
129: /*
130: ** Cleanup memory after the GLOBAL list of converters. Note that there
131: ** is also a LOCAL conversion list associated with each HTRequest
132: ** structure. Written by Eric Sink, eric@spyglass.com
133: */
134: PUBLIC void HTDisposeConversions NOARGS
135: {
136: if (HTConversions) {
1.59.2.1! frystyk 137: HTList *cur = HTConversions;
! 138: HTPresentation *pres;
! 139: while ((pres = (HTPresentation*) HTList_nextObject(cur))) {
! 140: FREE(pres->command);
! 141: free(pres);
! 142: }
1.56 frystyk 143: HTList_delete(HTConversions);
144: HTConversions = NULL;
145: }
1.2 timbl 146: }
1.1 timbl 147:
148:
1.17 luotonen 149: PUBLIC void HTAcceptEncoding ARGS3(HTList *, list,
150: char *, enc,
1.59 frystyk 151: double, quality)
1.17 luotonen 152: {
153: HTAcceptNode * node;
154: char * cur;
155:
156: if (!list || !enc || !*enc) return;
157:
158: for(cur=enc; *cur; cur++) *cur=TOLOWER(*cur);
159:
160: node = (HTAcceptNode*)calloc(1, sizeof(HTAcceptNode));
161: if (!node) outofmem(__FILE__, "HTAcceptEncoding");
162: HTList_addObject(list, (void*)node);
163:
164: node->atom = HTAtom_for(enc);
165: node->quality = quality;
166: }
167:
168:
169: PUBLIC void HTAcceptLanguage ARGS3(HTList *, list,
170: char *, lang,
1.59 frystyk 171: double, quality)
1.17 luotonen 172: {
173: HTAcceptNode * node;
174:
175: if (!list || !lang || !*lang) return;
176:
177: node = (HTAcceptNode*)calloc(1, sizeof(HTAcceptNode));
178: if (!node) outofmem(__FILE__, "HTAcceptLanguage");
179:
180: HTList_addObject(list, (void*)node);
181: node->atom = HTAtom_for(lang);
182: node->quality = quality;
183: }
184:
185:
1.48 frystyk 186: PRIVATE BOOL wild_match ARGS2(HTAtom *, tmplate,
1.17 luotonen 187: HTAtom *, actual)
188: {
189: char *t, *a, *st, *sa;
190: BOOL match = NO;
191:
1.48 frystyk 192: if (tmplate && actual && (t = HTAtom_name(tmplate))) {
1.22 luotonen 193: if (!strcmp(t, "*"))
194: return YES;
1.17 luotonen 195:
1.22 luotonen 196: if (strchr(t, '*') &&
197: (a = HTAtom_name(actual)) &&
198: (st = strchr(t, '/')) && (sa = strchr(a,'/'))) {
1.17 luotonen 199:
1.22 luotonen 200: *sa = 0;
201: *st = 0;
202:
203: if ((*(st-1)=='*' &&
204: (*(st+1)=='*' || !strcasecomp(st+1, sa+1))) ||
205: (*(st+1)=='*' && !strcasecomp(t,a)))
206: match = YES;
207:
208: *sa = '/';
209: *st = '/';
210: }
211: }
1.23 luotonen 212: return match;
1.17 luotonen 213: }
214:
1.36 luotonen 215: /*
216: * Added by takada@seraph.ntt.jp (94/04/08)
217: */
1.48 frystyk 218: PRIVATE BOOL lang_match ARGS2(HTAtom *, tmplate,
1.36 luotonen 219: HTAtom *, actual)
220: {
221: char *t, *a, *st, *sa;
222: BOOL match = NO;
223:
1.48 frystyk 224: if (tmplate && actual &&
225: (t = HTAtom_name(tmplate)) && (a = HTAtom_name(actual))) {
1.36 luotonen 226: st = strchr(t, '_');
227: sa = strchr(a, '_');
228: if ((st != NULL) && (sa != NULL)) {
229: if (!strcasecomp(t, a))
230: match = YES;
231: else
232: match = NO;
233: }
234: else {
235: if (st != NULL) *st = 0;
236: if (sa != NULL) *sa = 0;
237: if (!strcasecomp(t, a))
238: match = YES;
239: else
240: match = NO;
241: if (st != NULL) *st = '_';
242: if (sa != NULL) *sa = '_';
243: }
244: }
245: return match;
246: }
247: /* end of addition */
248:
249:
1.17 luotonen 250:
1.59 frystyk 251: PRIVATE double type_value ARGS2(HTAtom *, content_type,
1.17 luotonen 252: HTList *, accepted)
253: {
254: HTList * cur = accepted;
255: HTPresentation * pres;
256: HTPresentation * wild = NULL;
257:
258: if (!content_type || !accepted) return -1;
259:
260: while ((pres = (HTPresentation*)HTList_nextObject(cur))) {
261: if (pres->rep == content_type)
262: return pres->quality;
263: else if (wild_match(pres->rep, content_type))
264: wild = pres;
265: }
266: if (wild) return wild->quality;
267: else return -1;
268: }
269:
270:
1.59 frystyk 271: PRIVATE double lang_value ARGS2(HTAtom *, language,
1.17 luotonen 272: HTList *, accepted)
273: {
274: HTList * cur = accepted;
275: HTAcceptNode * node;
276: HTAcceptNode * wild = NULL;
277:
278: if (!language || !accepted || HTList_isEmpty(accepted)) {
279: return 0.1;
280: }
281:
282: while ((node = (HTAcceptNode*)HTList_nextObject(cur))) {
283: if (node->atom == language) {
284: return node->quality;
285: }
1.36 luotonen 286: /*
287: * patch by takada@seraph.ntt.jp (94/04/08)
288: * the original line was
289: * else if (wild_match(node->atom, language)) {
290: * and the new line is
291: */
292: else if (lang_match(node->atom, language)) {
1.17 luotonen 293: wild = node;
294: }
295: }
296:
297: if (wild) {
298: return wild->quality;
299: }
300: else {
301: return 0.1;
302: }
303: }
304:
305:
1.59 frystyk 306: PRIVATE double encoding_value ARGS2(HTAtom *, encoding,
1.17 luotonen 307: HTList *, accepted)
308: {
309: HTList * cur = accepted;
310: HTAcceptNode * node;
311: HTAcceptNode * wild = NULL;
312: char * e;
313:
314: if (!encoding || !accepted || HTList_isEmpty(accepted))
315: return 1;
316:
317: e = HTAtom_name(encoding);
318: if (!strcmp(e, "7bit") || !strcmp(e, "8bit") || !strcmp(e, "binary"))
319: return 1;
320:
321: while ((node = (HTAcceptNode*)HTList_nextObject(cur))) {
322: if (node->atom == encoding)
323: return node->quality;
324: else if (wild_match(node->atom, encoding))
325: wild = node;
326: }
327: if (wild) return wild->quality;
328: else return 1;
329: }
330:
331:
332: PUBLIC BOOL HTRank ARGS4(HTList *, possibilities,
333: HTList *, accepted_content_types,
334: HTList *, accepted_languages,
335: HTList *, accepted_encodings)
336: {
337: int accepted_cnt = 0;
338: HTList * accepted;
339: HTList * sorted;
340: HTList * cur;
341: HTContentDescription * d;
342:
343: if (!possibilities) return NO;
344:
345: accepted = HTList_new();
346: cur = possibilities;
347: while ((d = (HTContentDescription*)HTList_nextObject(cur))) {
1.59 frystyk 348: double tv = type_value(d->content_type, accepted_content_types);
349: double lv = lang_value(d->content_language, accepted_languages);
350: double ev = encoding_value(d->content_encoding, accepted_encodings);
1.17 luotonen 351:
352: if (tv > 0) {
353: d->quality *= tv * lv * ev;
354: HTList_addObject(accepted, d);
355: accepted_cnt++;
356: }
1.18 luotonen 357: else {
358: if (d->filename) free(d->filename);
359: free(d);
360: }
1.17 luotonen 361: }
362:
1.58 frystyk 363: if (PROT_TRACE) fprintf(TDEST, "Ranking.....\n");
364: if (PROT_TRACE) fprintf(TDEST,
1.18 luotonen 365: "\nRANK QUALITY CONTENT-TYPE LANGUAGE ENCODING FILE\n");
1.17 luotonen 366:
367: sorted = HTList_new();
368: while (accepted_cnt-- > 0) {
369: HTContentDescription * worst = NULL;
370: cur = accepted;
371: while ((d = (HTContentDescription*)HTList_nextObject(cur))) {
372: if (!worst || d->quality < worst->quality)
373: worst = d;
374: }
375: if (worst) {
1.58 frystyk 376: if (PROT_TRACE)
377: fprintf(TDEST, "%d. %.4f %-20.20s %-8.8s %-10.10s %s\n",
378: accepted_cnt+1,
379: worst->quality,
380: (worst->content_type
1.17 luotonen 381: ? HTAtom_name(worst->content_type) : "-"),
1.58 frystyk 382: (worst->content_language
1.17 luotonen 383: ? HTAtom_name(worst->content_language) :"-"),
1.58 frystyk 384: (worst->content_encoding
1.17 luotonen 385: ? HTAtom_name(worst->content_encoding) :"-"),
1.58 frystyk 386: (worst->filename
1.17 luotonen 387: ? worst->filename :"-"));
388: HTList_removeObject(accepted, (void*)worst);
389: HTList_addObject(sorted, (void*)worst);
390: }
391: }
1.58 frystyk 392: if (PROT_TRACE) fprintf(TDEST, "\n");
1.17 luotonen 393: HTList_delete(accepted);
394: HTList_delete(possibilities->next);
395: possibilities->next = sorted->next;
396: sorted->next = NULL;
397: HTList_delete(sorted);
398:
399: if (!HTList_isEmpty(possibilities)) return YES;
400: else return NO;
401: }
402:
403:
404:
405:
406:
1.13 timbl 407: /* Socket Input Buffering
408: ** ----------------------
1.1 timbl 409: **
1.13 timbl 410: ** This code is used because one cannot in general open a
411: ** file descriptor for a socket.
412: **
1.1 timbl 413: ** The input file is read using the macro which can read from
1.13 timbl 414: ** a socket or a file, but this should not be used for files
415: ** as fopen() etc is more portable of course.
416: **
1.1 timbl 417: ** The input buffer size, if large will give greater efficiency and
418: ** release the server faster, and if small will save space on PCs etc.
419: */
420:
421:
422: /* Set up the buffering
423: **
424: ** These routines are public because they are in fact needed by
425: ** many parsers, and on PCs and Macs we should not duplicate
426: ** the static buffer area.
427: */
1.58 frystyk 428: PUBLIC HTInputSocket * HTInputSocket_new ARGS1 (SOCKFD, file_number)
1.1 timbl 429: {
1.28 frystyk 430: HTInputSocket *isoc = (HTInputSocket *)calloc(1, sizeof(*isoc));
1.13 timbl 431: if (!isoc) outofmem(__FILE__, "HTInputSocket_new");
432: isoc->input_file_number = file_number;
433: isoc->input_pointer = isoc->input_limit = isoc->input_buffer;
434: return isoc;
1.1 timbl 435: }
436:
1.35 frystyk 437: /* This should return HT_INTERRUPTED if interrupted BUT the connection
438: MUST not be closed */
439: PUBLIC int HTInputSocket_getCharacter ARGS1(HTInputSocket*, isoc)
1.1 timbl 440: {
1.35 frystyk 441: int ch;
1.1 timbl 442: do {
1.52 frystyk 443: if (isoc->input_pointer >= isoc->input_limit) {
444: int status = NETREAD(isoc->input_file_number,
445: isoc->input_buffer, INPUT_BUFFER_SIZE);
1.1 timbl 446: if (status <= 0) {
1.39 frystyk 447: if (status == 0)
448: return EOF;
449: if (status == HT_INTERRUPTED) {
450: if (TRACE)
1.58 frystyk 451: fprintf(TDEST, "Get Char.... Interrupted in HTInputSocket_getCharacter\n");
1.39 frystyk 452: return HT_INTERRUPTED;
453: }
1.58 frystyk 454: if (PROT_TRACE)
455: fprintf(TDEST, "Read Socket. READ ERROR %d\n", socerrno);
1.39 frystyk 456: return EOF; /* -1 is returned by UCX at end of HTTP link */
1.1 timbl 457: }
1.35 frystyk 458: isoc->input_pointer = isoc->input_buffer;
1.13 timbl 459: isoc->input_limit = isoc->input_buffer + status;
1.1 timbl 460: }
1.39 frystyk 461: ch = (unsigned char) *isoc->input_pointer++;
462: } while (ch == 13); /* Ignore ASCII carriage return */
1.1 timbl 463:
464: return FROMASCII(ch);
465: }
466:
1.17 luotonen 467: PUBLIC void HTInputSocket_free ARGS1(HTInputSocket *, me)
1.13 timbl 468: {
469: if (me) free(me);
470: }
471:
472:
1.16 luotonen 473: PUBLIC char * HTInputSocket_getBlock ARGS2(HTInputSocket*, isoc,
474: int *, len)
475: {
476: if (isoc->input_pointer >= isoc->input_limit) {
477: int status = NETREAD(isoc->input_file_number,
478: isoc->input_buffer,
479: ((*len < INPUT_BUFFER_SIZE) ?
480: *len : INPUT_BUFFER_SIZE));
481: if (status <= 0) {
482: isoc->input_limit = isoc->input_buffer;
1.58 frystyk 483: if (status < 0) {
484: if (PROT_TRACE)
485: fprintf(TDEST, "Read Socket. READ ERROR %d\n", socerrno);
486: }
1.16 luotonen 487: *len = 0;
488: return NULL;
489: }
490: else {
491: *len = status;
492: return isoc->input_buffer;
493: }
494: }
495: else {
496: char * ret = isoc->input_pointer;
497: *len = isoc->input_limit - isoc->input_pointer;
498: isoc->input_pointer = isoc->input_limit;
499: return ret;
500: }
501: }
502:
503:
1.15 luotonen 504: PRIVATE int fill_in_buffer ARGS1(HTInputSocket *, isoc)
505: {
506: if (isoc) {
507: int status;
508:
509: isoc->input_pointer = isoc->input_buffer;
510: status = NETREAD(isoc->input_file_number,
511: isoc->input_buffer,
512: INPUT_BUFFER_SIZE);
513: if (status <= 0) {
514: isoc->input_limit = isoc->input_buffer;
1.58 frystyk 515: if (status < 0) {
516: if (PROT_TRACE)
517: fprintf(TDEST, "Read Socket. READ ERROR %d\n", socerrno);
518: }
1.15 luotonen 519: }
520: else
521: isoc->input_limit = isoc->input_buffer + status;
522: return status;
523: }
524: return -1;
525: }
526:
527:
528: PRIVATE void ascii_cat ARGS3(char **, linep,
529: char *, start,
530: char *, end)
531: {
532: if (linep && start && end && start <= end) {
533: char *ptr;
534:
535: if (*linep) {
536: int len = strlen(*linep);
537: *linep = (char*)realloc(*linep, len + end-start + 1);
538: ptr = *linep + len;
539: }
540: else {
541: ptr = *linep = (char*)malloc(end-start + 1);
542: }
543:
544: while (start < end) {
545: *ptr = FROMASCII(*start);
546: ptr++;
547: start++;
548: }
549: *ptr = 0;
550: }
551: }
552:
553:
554: PRIVATE char * get_some_line ARGS2(HTInputSocket *, isoc,
555: BOOL, unfold)
556: {
557: if (!isoc)
558: return NULL;
559: else {
560: BOOL check_unfold = NO;
561: int prev_cr = 0;
562: char *start = isoc->input_pointer;
563: char *cur = isoc->input_pointer;
564: char * line = NULL;
565:
566: for(;;) {
567: /*
568: ** Get more if needed to complete line
569: */
570: if (cur >= isoc->input_limit) { /* Need more data */
571: ascii_cat(&line, start, cur);
572: if (fill_in_buffer(isoc) <= 0)
573: return line;
574: start = cur = isoc->input_pointer;
575: } /* if need more data */
576:
577: /*
578: ** Find a line feed if there is one
579: */
580: for(; cur < isoc->input_limit; cur++) {
581: char c = FROMASCII(*cur);
582: if (!c) {
1.18 luotonen 583: if (line) free(line); /* Leak fixed AL 6 Feb 94 */
1.15 luotonen 584: return NULL; /* Panic! read a 0! */
585: }
586: if (check_unfold && c != ' ' && c != '\t') {
587: return line; /* Note: didn't update isoc->input_pointer */
588: }
589: else {
590: check_unfold = NO;
591: }
592:
593: if (c=='\r') {
594: prev_cr = 1;
595: }
596: else {
597: if (c=='\n') { /* Found a line feed */
598: ascii_cat(&line, start, cur-prev_cr);
599: start = isoc->input_pointer = cur+1;
600:
1.44 frystyk 601: if (line && (int) strlen(line) > 0 && unfold) {
1.15 luotonen 602: check_unfold = YES;
603: }
604: else {
605: return line;
606: }
607: } /* if NL */
608: /* else just a regular character */
609: prev_cr = 0;
610: } /* if not CR */
611: } /* while characters in buffer remain */
612: } /* until line read or end-of-file */
613: } /* valid parameters to function */
614: }
615:
1.43 frystyk 616: /* The returned string must be freed by the caller */
1.15 luotonen 617: PUBLIC char * HTInputSocket_getLine ARGS1(HTInputSocket *, isoc)
618: {
619: return get_some_line(isoc, NO);
620: }
621:
1.43 frystyk 622: /* The returned string must be freed by the caller */
1.15 luotonen 623: PUBLIC char * HTInputSocket_getUnfoldedLine ARGS1(HTInputSocket *, isoc)
624: {
625: return get_some_line(isoc, YES);
626: }
627:
628:
1.33 luotonen 629: PRIVATE BOOL better_match ARGS2(HTFormat, f,
630: HTFormat, g)
631: {
632: CONST char *p, *q;
633:
634: if (f && g && (p = HTAtom_name(f)) && (q = HTAtom_name(g))) {
635: int i,j;
636: for(i=0 ; *p; p++) if (*p == '*') i++;
637: for(j=0 ; *q; q++) if (*q == '*') j++;
638: if (i < j) return YES;
639: }
640: return NO;
641: }
642:
1.17 luotonen 643:
1.2 timbl 644: /* Create a filter stack
645: ** ---------------------
646: **
1.7 secret 647: ** If a wildcard match is made, a temporary HTPresentation
1.2 timbl 648: ** structure is made to hold the destination format while the
649: ** new stack is generated. This is just to pass the out format to
650: ** MIME so far. Storing the format of a stream in the stream might
651: ** be a lot neater.
1.10 timbl 652: **
1.29 frystyk 653: ** The star/star format is special, in that if you can take
1.40 frystyk 654: ** that you can take anything.
655: **
656: ** On succes, request->error_block is set to YES so no more error
657: ** messages to the stream as the stream might be of any format.
1.2 timbl 658: */
1.52 frystyk 659: PUBLIC HTStream * HTStreamStack ARGS5(HTFormat, rep_in,
660: HTFormat, rep_out,
661: HTStream *, output_stream,
1.34 luotonen 662: HTRequest *, request,
663: BOOL, guess)
1.2 timbl 664: {
1.14 timbl 665: HTList * conversion[2];
666: int which_list;
1.59 frystyk 667: double best_quality = -1e30; /* Pretty bad! */
1.29 frystyk 668: HTPresentation *pres, *match, *best_match=0;
1.14 timbl 669:
1.47 frystyk 670: request->error_block = YES; /* No more error output to stream */
1.58 frystyk 671: if (TRACE) fprintf(TDEST,
1.39 frystyk 672: "StreamStack. Constructing stream stack for %s to %s\n",
1.10 timbl 673: HTAtom_name(rep_in),
1.2 timbl 674: HTAtom_name(rep_out));
1.34 luotonen 675:
676: if (guess && rep_in == WWW_UNKNOWN) {
1.58 frystyk 677: if (PROT_TRACE) fprintf(TDEST, "Returning... guessing stream\n");
1.52 frystyk 678: return HTGuess_new(request, NULL, rep_in, rep_out, output_stream);
1.34 luotonen 679: }
680:
1.47 frystyk 681: if (rep_out == WWW_SOURCE || rep_out == rep_in) {
1.52 frystyk 682: return output_stream;
1.47 frystyk 683: }
1.2 timbl 684:
1.14 timbl 685: conversion[0] = request->conversions;
686: conversion[1] = HTConversions;
1.17 luotonen 687:
1.15 luotonen 688: for(which_list = 0; which_list<2; which_list++) {
689: HTList * cur = conversion[which_list];
690:
691: while ((pres = (HTPresentation*)HTList_nextObject(cur))) {
1.25 frystyk 692: if ((pres->rep == rep_in || wild_match(pres->rep, rep_in)) &&
1.33 luotonen 693: (pres->rep_out == rep_out || wild_match(pres->rep_out, rep_out))) {
694: if (!best_match ||
695: better_match(pres->rep, best_match->rep) ||
696: (!better_match(best_match->rep, pres->rep) &&
697: pres->quality > best_quality)) {
1.58 frystyk 698: #ifdef GOT_SYSTEM
699: if (!pres->test_command||(system(pres->test_command)==0)){
1.49 howcome 700: if (TRACE && pres->test_command)
1.58 frystyk 701: fprintf(TDEST, "HTStreamStack testing %s %d\n",pres->test_command,system(pres->test_command));
1.49 howcome 702: best_match = pres;
703: best_quality = pres->quality;
704: }
1.58 frystyk 705: #endif /* GOT_SYSTEM */
1.10 timbl 706: }
707: }
1.2 timbl 708: }
709: }
1.33 luotonen 710:
1.29 frystyk 711: match = best_match ? best_match : NULL;
712: if (match) {
713: if (match->rep == WWW_SOURCE) {
1.58 frystyk 714: if (TRACE) fprintf(TDEST, "StreamStack. Don't know how to handle this, so put out %s to %s\n",
1.29 frystyk 715: HTAtom_name(match->rep),
716: HTAtom_name(rep_out));
717: }
1.52 frystyk 718: return (*match->converter)(request, match->command, rep_in, rep_out,
719: output_stream);
1.29 frystyk 720: }
1.42 frystyk 721: {
722: char *msg = NULL;
723: StrAllocCopy(msg, "Can't convert from ");
724: StrAllocCat(msg, HTAtom_name(rep_in));
725: StrAllocCat(msg, " to ");
726: StrAllocCat(msg, HTAtom_name(rep_out));
727: HTErrorAdd(request, ERR_FATAL, NO, HTERR_NOT_IMPLEMENTED,
728: (void *) msg, (int) strlen(msg), "HTStreamStack");
729: free(msg);
730: }
1.47 frystyk 731: request->error_block = NO; /* We didn't put up a stream anyway */
1.2 timbl 732: return NULL;
733: }
734:
735:
736: /* Find the cost of a filter stack
737: ** -------------------------------
738: **
739: ** Must return the cost of the same stack which StreamStack would set up.
740: **
741: ** On entry,
742: ** length The size of the data to be converted
743: */
1.59 frystyk 744: PUBLIC double HTStackValue ARGS5(
1.14 timbl 745: HTList *, theseConversions,
1.10 timbl 746: HTFormat, rep_in,
1.2 timbl 747: HTFormat, rep_out,
1.59 frystyk 748: double, initial_value,
1.2 timbl 749: long int, length)
750: {
1.14 timbl 751: int which_list;
752: HTList* conversion[2];
753:
1.58 frystyk 754: if (TRACE) fprintf(TDEST,
1.39 frystyk 755: "StackValue.. Evaluating stream stack for %s worth %.3f to %s\n",
1.10 timbl 756: HTAtom_name(rep_in), initial_value,
1.2 timbl 757: HTAtom_name(rep_out));
758:
759: if (rep_out == WWW_SOURCE ||
1.10 timbl 760: rep_out == rep_in) return 0.0;
1.2 timbl 761:
1.14 timbl 762: conversion[0] = theseConversions;
763: conversion[1] = HTConversions;
764:
765: for(which_list = 0; which_list<2; which_list++)
766: if (conversion[which_list]) {
1.15 luotonen 767: HTList * cur = conversion[which_list];
1.2 timbl 768: HTPresentation * pres;
1.15 luotonen 769: while ((pres = (HTPresentation*)HTList_nextObject(cur))) {
770: if (pres->rep == rep_in &&
1.17 luotonen 771: (pres->rep_out == rep_out || wild_match(pres->rep_out, rep_out))) {
1.59 frystyk 772: double value = initial_value * pres->quality;
1.2 timbl 773: if (HTMaxSecs != 0.0)
1.15 luotonen 774: value = value - (length*pres->secs_per_byte + pres->secs)
1.2 timbl 775: /HTMaxSecs;
776: return value;
777: }
778: }
779: }
780:
781: return -1e30; /* Really bad */
1.17 luotonen 782: }
783:
784:
1.2 timbl 785:
1.1 timbl 786:
1.2 timbl 787: /* Push data from a socket down a stream
788: ** -------------------------------------
1.1 timbl 789: **
1.2 timbl 790: ** This routine is responsible for creating and PRESENTING any
1.1 timbl 791: ** graphic (or other) objects described by the file.
1.2 timbl 792: **
793: ** The file number given is assumed to be a TELNET stream ie containing
794: ** CRLF at the end of lines which need to be stripped to LF for unix
795: ** when the format is textual.
796: **
1.26 luotonen 797: ** RETURNS the number of bytes transferred.
798: **
1.1 timbl 799: */
1.26 luotonen 800: PUBLIC int HTCopy ARGS2(
1.58 frystyk 801: SOCKFD, file_number,
1.2 timbl 802: HTStream*, sink)
1.1 timbl 803: {
1.2 timbl 804: HTStreamClass targetClass;
1.13 timbl 805: HTInputSocket * isoc;
1.26 luotonen 806: int cnt = 0;
807:
1.5 timbl 808: /* Push the data down the stream
1.2 timbl 809: **
810: */
811: targetClass = *(sink->isa); /* Copy pointers to procedures */
1.13 timbl 812: isoc = HTInputSocket_new(file_number);
1.2 timbl 813:
814: /* Push binary from socket down sink
1.10 timbl 815: **
816: ** This operation could be put into a main event loop
1.2 timbl 817: */
818: for(;;) {
819: int status = NETREAD(
1.13 timbl 820: file_number, isoc->input_buffer, INPUT_BUFFER_SIZE);
1.2 timbl 821: if (status <= 0) {
822: if (status == 0) break;
1.58 frystyk 823: if (TRACE) fprintf(TDEST,
1.39 frystyk 824: "Socket Copy. Read error, read returns %d with errno=%d\n",
1.58 frystyk 825: status, socerrno);
1.2 timbl 826: break;
827: }
1.26 luotonen 828:
1.8 timbl 829: #ifdef NOT_ASCII
830: {
831: char * p;
1.13 timbl 832: for(p = isoc->input_buffer; p < isoc->input_buffer+status; p++) {
1.8 timbl 833: *p = FROMASCII(*p);
834: }
835: }
836: #endif
837:
1.13 timbl 838: (*targetClass.put_block)(sink, isoc->input_buffer, status);
1.26 luotonen 839: cnt += status;
1.2 timbl 840: } /* next bufferload */
1.26 luotonen 841:
1.13 timbl 842: HTInputSocket_free(isoc);
1.26 luotonen 843:
844: return cnt;
1.2 timbl 845: }
846:
1.1 timbl 847:
1.7 secret 848:
849: /* Push data from a file pointer down a stream
850: ** -------------------------------------
851: **
852: ** This routine is responsible for creating and PRESENTING any
853: ** graphic (or other) objects described by the file.
854: **
855: **
856: */
857: PUBLIC void HTFileCopy ARGS2(
858: FILE *, fp,
859: HTStream*, sink)
860: {
861: HTStreamClass targetClass;
1.13 timbl 862: char input_buffer[INPUT_BUFFER_SIZE];
1.7 secret 863:
864: /* Push the data down the stream
865: **
866: */
867: targetClass = *(sink->isa); /* Copy pointers to procedures */
868:
869: /* Push binary from socket down sink
870: */
871: for(;;) {
872: int status = fread(
873: input_buffer, 1, INPUT_BUFFER_SIZE, fp);
874: if (status == 0) { /* EOF or error */
875: if (ferror(fp) == 0) break;
1.58 frystyk 876: if (TRACE) fprintf(TDEST,
1.39 frystyk 877: "File Copy... Read error, read returns %d\n", ferror(fp));
1.7 secret 878: break;
879: }
880: (*targetClass.put_block)(sink, input_buffer, status);
1.13 timbl 881: } /* next bufferload */
1.7 secret 882: }
883:
884:
885:
886:
1.2 timbl 887: /* Push data from a socket down a stream STRIPPING CR
888: ** --------------------------------------------------
889: **
890: ** This routine is responsible for creating and PRESENTING any
1.8 timbl 891: ** graphic (or other) objects described by the socket.
1.2 timbl 892: **
893: ** The file number given is assumed to be a TELNET stream ie containing
894: ** CRLF at the end of lines which need to be stripped to LF for unix
895: ** when the format is textual.
1.37 frystyk 896: **
897: ** Character handling is now of type int, Henrik, May 09-94
1.1 timbl 898: */
1.2 timbl 899: PUBLIC void HTCopyNoCR ARGS2(
1.58 frystyk 900: SOCKFD, file_number,
1.2 timbl 901: HTStream*, sink)
902: {
1.13 timbl 903: HTStreamClass targetClass;
904: HTInputSocket * isoc;
1.37 frystyk 905: int ch;
1.1 timbl 906:
1.2 timbl 907: /* Push the data, ignoring CRLF, down the stream
908: **
909: */
910: targetClass = *(sink->isa); /* Copy pointers to procedures */
911:
912: /* Push text from telnet socket down sink
913: **
914: ** @@@@@ To push strings could be faster? (especially is we
915: ** cheat and don't ignore CR! :-}
916: */
1.13 timbl 917: isoc = HTInputSocket_new(file_number);
1.37 frystyk 918: while ((ch = HTInputSocket_getCharacter(isoc)) >= 0)
919: (*targetClass.put_character)(sink, ch);
1.13 timbl 920: HTInputSocket_free(isoc);
1.2 timbl 921: }
1.1 timbl 922:
1.2 timbl 923:
924: /* Parse a socket given format and file number
925: **
926: ** This routine is responsible for creating and PRESENTING any
927: ** graphic (or other) objects described by the file.
928: **
929: ** The file number given is assumed to be a TELNET stream ie containing
930: ** CRLF at the end of lines which need to be stripped to LF for unix
931: ** when the format is textual.
932: **
1.42 frystyk 933: ** Returns <0 on error, HT_LOADED on success.
1.2 timbl 934: */
1.14 timbl 935:
1.46 frystyk 936: /* The parameter to this function and HTParsefile should be HTRequest */
937:
1.12 timbl 938: PUBLIC int HTParseSocket ARGS3(
1.10 timbl 939: HTFormat, rep_in,
1.58 frystyk 940: SOCKFD, file_number,
1.12 timbl 941: HTRequest *, request)
1.2 timbl 942: {
943: HTStream * stream;
944: HTStreamClass targetClass;
1.1 timbl 945:
1.40 frystyk 946: if (request->error_stack) {
1.58 frystyk 947: if (TRACE) fprintf(TDEST, "ParseSocket. Called whith non-empty error stack, so I return right away!\n");
1.40 frystyk 948: return -1;
949: }
950:
1.42 frystyk 951: /* Set up stream stack */
1.52 frystyk 952: if ((stream = HTStreamStack(rep_in, request->output_format,
953: request->output_stream,
954: request, YES)) == NULL)
1.42 frystyk 955: return -1;
1.1 timbl 956:
1.3 timbl 957: /* Push the data, ignoring CRLF if necessary, down the stream
958: **
1.2 timbl 959: **
1.3 timbl 960: ** @@ Bug: This decision ought to be made based on "encoding"
1.9 timbl 961: ** rather than on format. @@@ When we handle encoding.
1.3 timbl 962: ** The current method smells anyway.
1.2 timbl 963: */
964: targetClass = *(stream->isa); /* Copy pointers to procedures */
1.52 frystyk 965: if (rep_in == WWW_BINARY || rep_in == WWW_UNKNOWN
1.26 luotonen 966: || (request->content_encoding &&
967: request->content_encoding != HTAtom_for("8bit") &&
968: request->content_encoding != HTAtom_for("7bit"))
1.10 timbl 969: || strstr(HTAtom_name(rep_in), "image/")
970: || strstr(HTAtom_name(rep_in), "video/")) { /* @@@@@@ */
1.29 frystyk 971: HTCopy(file_number, stream);
1.52 frystyk 972: } else
1.2 timbl 973: HTCopyNoCR(file_number, stream);
1.45 duns 974: (*targetClass._free)(stream);
1.7 secret 975:
976: return HT_LOADED;
977: }
978:
979:
980:
981: /* Parse a file given format and file pointer
982: **
983: ** This routine is responsible for creating and PRESENTING any
984: ** graphic (or other) objects described by the file.
985: **
986: ** The file number given is assumed to be a TELNET stream ie containing
1.10 timbl 987: ** CRLF at the end of lines which need to be stripped to \n for unix
1.7 secret 988: ** when the format is textual.
989: **
990: */
1.12 timbl 991: PUBLIC int HTParseFile ARGS3(
1.10 timbl 992: HTFormat, rep_in,
1.7 secret 993: FILE *, fp,
1.12 timbl 994: HTRequest *, request)
1.7 secret 995: {
996: HTStream * stream;
997: HTStreamClass targetClass;
1.40 frystyk 998:
999: if (request->error_stack) {
1.58 frystyk 1000: if (TRACE) fprintf(TDEST, "ParseFile... Called whith non-empty error stack, so I return right away!\n");
1.40 frystyk 1001: return -1;
1002: }
1.7 secret 1003:
1.42 frystyk 1004: /* Set up stream stack */
1.52 frystyk 1005: if ((stream = HTStreamStack(rep_in, request->output_format,
1006: request->output_stream, request, YES)) == NULL)
1.42 frystyk 1007: return -1;
1.7 secret 1008:
1.9 timbl 1009: /* Push the data down the stream
1.7 secret 1010: **
1011: **
1012: ** @@ Bug: This decision ought to be made based on "encoding"
1.10 timbl 1013: ** rather than on content-type. @@@ When we handle encoding.
1.7 secret 1014: ** The current method smells anyway.
1015: */
1016: targetClass = *(stream->isa); /* Copy pointers to procedures */
1017: HTFileCopy(fp, stream);
1.45 duns 1018: (*targetClass._free)(stream);
1.1 timbl 1019:
1.2 timbl 1020: return HT_LOADED;
1.1 timbl 1021: }
1.2 timbl 1022:
1.10 timbl 1023:
1.52 frystyk 1024: /* ------------------------------------------------------------------------- */
1025: /* MULTI THREADED IMPLEMENTATIONS */
1026: /* ------------------------------------------------------------------------- */
1027:
1028: /* Push data from a socket down a stream
1029: ** -------------------------------------
1030: **
1031: ** This routine is responsible for creating and PRESENTING any
1.59.2.1! frystyk 1032: ** graphic (or other) objects described by the file. As this function
! 1033: ** max reads a chunk of data on size INPUT_BUFFER_SIZE, it can be used
! 1034: ** with both blocking or non-blocking sockets. It will always return to
! 1035: ** the event loop, however if we are using blocking I/O then we get a full
! 1036: ** buffer read, otherwise we get what's available.
1.52 frystyk 1037: **
1038: ** Returns HT_LOADED if finished reading
1.59.2.1! frystyk 1039: ** HT_ERROR if error,
! 1040: ** HT_INTERRUPTED if interrupted
! 1041: ** HT_WOULD_BLOCK if read would block
1.52 frystyk 1042: */
1.59.2.1! frystyk 1043: PUBLIC int HTSocketRead ARGS2(HTRequest *, request, HTStream *, target)
1.52 frystyk 1044: {
1.59.2.1! frystyk 1045: HTInputSocket *isoc = request->net_info->isoc;
! 1046: int b_read = isoc->input_limit-isoc->input_buffer;
! 1047: int status;
1.58 frystyk 1048: if (!isoc || isoc->input_file_number==INVSOC) {
1049: if (PROT_TRACE) fprintf(TDEST, "Read Socket. Bad argument\n");
1.59.2.1! frystyk 1050: return HT_ERROR;
1.52 frystyk 1051: }
1052:
1.59.2.1! frystyk 1053: if (HTThreadIntr(isoc->input_file_number)) /* Interrupted */
! 1054: return HT_INTERRUPTED;
! 1055: #if 0
! 1056: while(1) {
! 1057: #endif
! 1058: /* Read from socket if we got rid of all the data previously read */
! 1059: if (isoc->input_pointer >= isoc->input_limit) {
! 1060: if ((b_read = NETREAD(isoc->input_file_number, isoc->input_buffer,
! 1061: INPUT_BUFFER_SIZE)) < 0) {
1.52 frystyk 1062: #ifdef EAGAIN
1.59.2.1! frystyk 1063: if (socerrno==EAGAIN || socerrno==EWOULDBLOCK) /* POSIX, SVR4*/
1.52 frystyk 1064: #else
1.59.2.1! frystyk 1065: if (socerrno==EWOULDBLOCK) /* BSD */
1.52 frystyk 1066: #endif
1.59.2.1! frystyk 1067: {
! 1068: if (PROT_TRACE)
! 1069: fprintf(TDEST, "Read Socket. WOULD BLOCK soc %d\n",
! 1070: isoc->input_file_number);
! 1071: HTThreadState(isoc->input_file_number, THD_SET_READ);
! 1072: return HT_WOULD_BLOCK;
! 1073: } else { /* We have a real error */
! 1074: if (PROT_TRACE)
! 1075: fprintf(TDEST, "Read Socket. READ ERROR %d\n", socerrno);
! 1076: return HT_ERROR;
! 1077: }
! 1078: } else if (!b_read) {
! 1079: HTThreadState(isoc->input_file_number, THD_CLR_READ);
! 1080: return HT_LOADED;
! 1081: }
! 1082:
! 1083: /* Remember how much we have read from the input socket */
! 1084: isoc->input_pointer = isoc->input_buffer;
! 1085: isoc->input_limit = isoc->input_buffer + b_read;
! 1086:
! 1087: #ifdef NOT_ASCII
1.52 frystyk 1088: {
1.59.2.1! frystyk 1089: char *p = isoc->input_buffer;
! 1090: while (p < isoc->input_limit) {
! 1091: *p = FROMASCII(*p);
! 1092: p++;
! 1093: }
! 1094: }
! 1095: #endif
! 1096: if (PROT_TRACE)
! 1097: fprintf(TDEST, "Read Socket. %d bytes read from socket %d\n",
! 1098: b_read, isoc->input_file_number);
! 1099: }
! 1100:
! 1101: /* Now push the data down the stream */
! 1102: if ((status = (*target->isa->put_block)(target, isoc->input_buffer,
! 1103: b_read)) != HT_OK) {
! 1104: if (status==HT_WOULD_BLOCK) {
! 1105: if (PROT_TRACE)
! 1106: fprintf(TDEST, "Read Socket. Stream WOULD BLOCK\n");
! 1107: HTThreadState(isoc->input_file_number, THD_CLR_READ);
1.52 frystyk 1108: return HT_WOULD_BLOCK;
1109: } else { /* We have a real error */
1110: if (PROT_TRACE)
1.59.2.1! frystyk 1111: fprintf(TDEST, "Read Socket. Stream ERROR\n");
! 1112: return status;
1.52 frystyk 1113: }
1114: }
1.59.2.1! frystyk 1115: isoc->input_pointer = isoc->input_buffer + b_read;
! 1116: #if 0
1.52 frystyk 1117: }
1.59.2.1! frystyk 1118: #else
! 1119: return HT_WOULD_BLOCK;
! 1120: #endif
1.52 frystyk 1121: }
Webmaster