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