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