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