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