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