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