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