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