Annotation of libwww/Library/src/HTFormat.c, revision 1.14
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:
67: /* Presentation methods
68: ** --------------------
69: */
70:
1.14 ! timbl 71: PUBLIC HTList * HTConversions = NULL;
1.2 timbl 72:
73:
74: /* Define a presentation system command for a content-type
75: ** -------------------------------------------------------
76: */
1.12 timbl 77: PUBLIC void HTSetPresentation ARGS6(
78: HTList *, conversions,
79: CONST char *, representation,
80: CONST char *, command,
81: float, quality,
82: float, secs,
83: float, secs_per_byte
1.2 timbl 84: ){
85:
86: HTPresentation * pres = (HTPresentation *)malloc(sizeof(HTPresentation));
87: if (pres == NULL) outofmem(__FILE__, "HTSetPresentation");
88:
89: pres->rep = HTAtom_for(representation);
90: pres->rep_out = WWW_PRESENT; /* Fixed for now ... :-) */
91: pres->converter = HTSaveAndExecute; /* Fixed for now ... */
92: pres->quality = quality;
93: pres->secs = secs;
94: pres->secs_per_byte = secs_per_byte;
95: pres->rep = HTAtom_for(representation);
96: pres->command = 0;
97: StrAllocCopy(pres->command, command);
98:
1.12 timbl 99: /* if (!HTPresentations) HTPresentations = HTList_new(); */
1.2 timbl 100:
1.12 timbl 101: #ifdef OLD_CODE if (strcmp(representation, "*")==0) {
1.2 timbl 102: if (default_presentation) free(default_presentation);
103: default_presentation = pres;
1.12 timbl 104: } else
105: #endif
106: HTList_addObject(conversions, pres);
1.2 timbl 107: }
108:
109:
110: /* Define a built-in function for a content-type
111: ** ---------------------------------------------
112: */
1.12 timbl 113: PUBLIC void HTSetConversion ARGS7(
114: HTList *, conversions,
115: CONST char *, representation_in,
116: CONST char *, representation_out,
1.6 timbl 117: HTConverter*, converter,
1.12 timbl 118: float, quality,
119: float, secs,
120: float, secs_per_byte
1.2 timbl 121: ){
1.1 timbl 122:
1.2 timbl 123: HTPresentation * pres = (HTPresentation *)malloc(sizeof(HTPresentation));
124: if (pres == NULL) outofmem(__FILE__, "HTSetPresentation");
125:
126: pres->rep = HTAtom_for(representation_in);
127: pres->rep_out = HTAtom_for(representation_out);
128: pres->converter = converter;
129: pres->command = NULL; /* Fixed */
130: pres->quality = quality;
131: pres->secs = secs;
132: pres->secs_per_byte = secs_per_byte;
133: pres->command = 0;
134:
1.12 timbl 135: /* if (!HTPresentations) HTPresentations = HTList_new(); */
1.2 timbl 136:
1.12 timbl 137: #ifdef OLD_CODE
1.2 timbl 138: if (strcmp(representation_in, "*")==0) {
139: if (default_presentation) free(default_presentation);
140: default_presentation = pres;
1.12 timbl 141: } else
142: #endif
143: HTList_addObject(conversions, pres);
1.2 timbl 144: }
1.1 timbl 145:
146:
147:
1.13 timbl 148: /* Socket Input Buffering
149: ** ----------------------
1.1 timbl 150: **
1.13 timbl 151: ** This code is used because one cannot in general open a
152: ** file descriptor for a socket.
153: **
1.1 timbl 154: ** The input file is read using the macro which can read from
1.13 timbl 155: ** a socket or a file, but this should not be used for files
156: ** as fopen() etc is more portable of course.
157: **
1.1 timbl 158: ** The input buffer size, if large will give greater efficiency and
159: ** release the server faster, and if small will save space on PCs etc.
160: */
161:
162:
163: /* Set up the buffering
164: **
165: ** These routines are public because they are in fact needed by
166: ** many parsers, and on PCs and Macs we should not duplicate
167: ** the static buffer area.
168: */
1.13 timbl 169: PUBLIC HTInputSocket * HTInputSocket_new ARGS1 (int,file_number)
1.1 timbl 170: {
1.13 timbl 171: HTInputSocket *isoc = (HTInputSocket *)malloc(sizeof(*isoc));
172: if (!isoc) outofmem(__FILE__, "HTInputSocket_new");
173: isoc->input_file_number = file_number;
174: isoc->input_pointer = isoc->input_limit = isoc->input_buffer;
175: return isoc;
1.1 timbl 176: }
177:
178:
1.13 timbl 179: PUBLIC char HTInputSocket_getCharacter ARGS1(HTInputSocket*, isoc)
1.1 timbl 180: {
181: char ch;
182: do {
1.13 timbl 183: if (isoc-> input_pointer >= isoc->input_limit) {
1.1 timbl 184: int status = NETREAD(
1.13 timbl 185: isoc->input_file_number,
186: isoc->input_buffer, INPUT_BUFFER_SIZE);
1.1 timbl 187: if (status <= 0) {
188: if (status == 0) return (char)EOF;
189: if (TRACE) fprintf(stderr,
190: "HTFormat: File read error %d\n", status);
191: return (char)EOF; /* -1 is returned by UCX at end of HTTP link */
192: }
1.13 timbl 193: isoc-> input_pointer = isoc->input_buffer;
194: isoc->input_limit = isoc->input_buffer + status;
1.1 timbl 195: }
1.13 timbl 196: ch = *isoc-> input_pointer++;
1.1 timbl 197: } while (ch == (char) 13); /* Ignore ASCII carriage return */
198:
199: return FROMASCII(ch);
200: }
201:
1.13 timbl 202: PUBLIC void HTInputSocket_free(HTInputSocket * me)
203: {
204: if (me) free(me);
205: }
206:
207:
1.1 timbl 208: /* Stream the data to an ouput file as binary
209: */
1.13 timbl 210: PUBLIC int HTOutputBinary ARGS3( HTInputSocket *, isoc,
211: int, input,
212: FILE *, output)
1.1 timbl 213: {
214: do {
215: int status = NETREAD(
1.13 timbl 216: input, isoc->input_buffer, INPUT_BUFFER_SIZE);
1.1 timbl 217: if (status <= 0) {
218: if (status == 0) return 0;
219: if (TRACE) fprintf(stderr,
220: "HTFormat: File read error %d\n", status);
221: return 2; /* Error */
222: }
1.13 timbl 223: fwrite(isoc->input_buffer, sizeof(char), status, output);
1.1 timbl 224: } while (YES);
225: }
226:
227:
1.2 timbl 228: /* Create a filter stack
229: ** ---------------------
230: **
1.7 secret 231: ** If a wildcard match is made, a temporary HTPresentation
1.2 timbl 232: ** structure is made to hold the destination format while the
233: ** new stack is generated. This is just to pass the out format to
234: ** MIME so far. Storing the format of a stream in the stream might
235: ** be a lot neater.
1.10 timbl 236: **
237: ** The www/source format is special, in that if you can take
238: ** that you can take anything. However, we
1.2 timbl 239: */
1.12 timbl 240: PUBLIC HTStream * HTStreamStack ARGS2(
1.10 timbl 241: HTFormat, rep_in,
1.12 timbl 242: HTRequest *, request)
1.2 timbl 243: {
1.12 timbl 244: HTFormat rep_out = request->output_format; /* Could be a param */
1.14 ! timbl 245: HTList * conversion[2];
1.2 timbl 246: HTAtom * wildcard = HTAtom_for("*");
1.10 timbl 247: HTFormat source = WWW_SOURCE;
1.14 ! timbl 248: int which_list;
! 249: HTPresentation * pres, *match, *wildcard_match=0,
! 250: *source_match=0, *source_wildcard_match=0;
! 251:
1.2 timbl 252: if (TRACE) fprintf(stderr,
253: "HTFormat: Constructing stream stack for %s to %s\n",
1.10 timbl 254: HTAtom_name(rep_in),
1.2 timbl 255: HTAtom_name(rep_out));
256:
1.14 ! timbl 257:
1.2 timbl 258: if (rep_out == WWW_SOURCE ||
1.12 timbl 259: rep_out == rep_in) return request->output_stream;
1.2 timbl 260:
1.14 ! timbl 261: conversion[0] = request->conversions;
! 262: conversion[1] = HTConversions;
1.2 timbl 263:
1.14 ! timbl 264: for(which_list = 0; which_list<2; which_list++)
! 265: if (conversion[which_list]) {
! 266: HTList * conversions = conversion[which_list];
1.12 timbl 267: int n = HTList_count(conversions);
1.2 timbl 268: int i;
269: for(i=0; i<n; i++) {
1.12 timbl 270: pres = HTList_objectAt(conversions, i);
1.10 timbl 271: if (pres->rep == rep_in) {
1.2 timbl 272: if (pres->rep_out == rep_out)
1.12 timbl 273: return (*pres->converter)(request, pres->command,
274: rep_in, pres->rep_out, request->output_stream);
1.2 timbl 275: if (pres->rep_out == wildcard) {
1.10 timbl 276: wildcard_match = pres;
277: }
278: }
279: if (pres->rep == source) {
280: if (pres->rep_out == rep_out)
281: source_match = pres;
282: if (pres->rep_out == wildcard) {
283: source_wildcard_match = pres;
1.2 timbl 284: }
285: }
286: }
287: }
1.14 ! timbl 288: match = wildcard_match ? wildcard_match :
! 289: source_match ? source_match :
! 290: source_wildcard_match;
! 291:
! 292: if (match) return (*match->converter)(
! 293: request, match->command, rep_in, rep_out,
! 294: request->output_stream);
1.10 timbl 295:
1.2 timbl 296: return NULL;
297: }
298:
299:
300: /* Find the cost of a filter stack
301: ** -------------------------------
302: **
303: ** Must return the cost of the same stack which StreamStack would set up.
304: **
305: ** On entry,
306: ** length The size of the data to be converted
307: */
1.12 timbl 308: PUBLIC float HTStackValue ARGS5(
1.14 ! timbl 309: HTList *, theseConversions,
1.10 timbl 310: HTFormat, rep_in,
1.2 timbl 311: HTFormat, rep_out,
312: float, initial_value,
313: long int, length)
314: {
315: HTAtom * wildcard = HTAtom_for("*");
1.14 ! timbl 316: int which_list;
! 317: HTList* conversion[2];
! 318:
1.2 timbl 319: if (TRACE) fprintf(stderr,
320: "HTFormat: Evaluating stream stack for %s worth %.3f to %s\n",
1.10 timbl 321: HTAtom_name(rep_in), initial_value,
1.2 timbl 322: HTAtom_name(rep_out));
323:
324: if (rep_out == WWW_SOURCE ||
1.10 timbl 325: rep_out == rep_in) return 0.0;
1.2 timbl 326:
1.12 timbl 327: /* if (!HTPresentations) HTFormatInit(); set up the list */
1.2 timbl 328:
1.14 ! timbl 329: conversion[0] = theseConversions;
! 330: conversion[1] = HTConversions;
! 331:
! 332: for(which_list = 0; which_list<2; which_list++)
! 333: if (conversion[which_list]) {
! 334: HTList * conversions = conversion[which_list];
1.12 timbl 335: int n = HTList_count(conversions);
1.2 timbl 336: int i;
337: HTPresentation * pres;
338: for(i=0; i<n; i++) {
1.12 timbl 339: pres = HTList_objectAt(conversions, i);
1.10 timbl 340: if (pres->rep == rep_in && (
1.2 timbl 341: pres->rep_out == rep_out ||
342: pres->rep_out == wildcard)) {
343: float value = initial_value * pres->quality;
344: if (HTMaxSecs != 0.0)
345: value = value - (length*pres->secs_per_byte + pres->secs)
346: /HTMaxSecs;
347: return value;
348: }
349: }
350: }
351:
352: return -1e30; /* Really bad */
353:
354: }
355:
1.1 timbl 356:
1.2 timbl 357: /* Push data from a socket down a stream
358: ** -------------------------------------
1.1 timbl 359: **
1.2 timbl 360: ** This routine is responsible for creating and PRESENTING any
1.1 timbl 361: ** graphic (or other) objects described by the file.
1.2 timbl 362: **
363: ** The file number given is assumed to be a TELNET stream ie containing
364: ** CRLF at the end of lines which need to be stripped to LF for unix
365: ** when the format is textual.
366: **
1.1 timbl 367: */
1.2 timbl 368: PUBLIC void HTCopy ARGS2(
369: int, file_number,
370: HTStream*, sink)
1.1 timbl 371: {
1.2 timbl 372: HTStreamClass targetClass;
1.13 timbl 373: HTInputSocket * isoc;
1.2 timbl 374:
1.5 timbl 375: /* Push the data down the stream
1.2 timbl 376: **
377: */
378: targetClass = *(sink->isa); /* Copy pointers to procedures */
1.13 timbl 379: isoc = HTInputSocket_new(file_number);
1.2 timbl 380:
381: /* Push binary from socket down sink
1.10 timbl 382: **
383: ** This operation could be put into a main event loop
1.2 timbl 384: */
385: for(;;) {
386: int status = NETREAD(
1.13 timbl 387: file_number, isoc->input_buffer, INPUT_BUFFER_SIZE);
1.2 timbl 388: if (status <= 0) {
389: if (status == 0) break;
390: if (TRACE) fprintf(stderr,
391: "HTFormat: Read error, read returns %d\n", status);
392: break;
393: }
1.8 timbl 394:
395: #ifdef NOT_ASCII
396: {
397: char * p;
1.13 timbl 398: for(p = isoc->input_buffer; p < isoc->input_buffer+status; p++) {
1.8 timbl 399: *p = FROMASCII(*p);
400: }
401: }
402: #endif
403:
1.13 timbl 404: (*targetClass.put_block)(sink, isoc->input_buffer, status);
1.2 timbl 405: } /* next bufferload */
1.13 timbl 406: HTInputSocket_free(isoc);
1.2 timbl 407: }
408:
1.1 timbl 409:
1.7 secret 410:
411: /* Push data from a file pointer down a stream
412: ** -------------------------------------
413: **
414: ** This routine is responsible for creating and PRESENTING any
415: ** graphic (or other) objects described by the file.
416: **
417: **
418: */
419: PUBLIC void HTFileCopy ARGS2(
420: FILE *, fp,
421: HTStream*, sink)
422: {
423: HTStreamClass targetClass;
1.13 timbl 424: char input_buffer[INPUT_BUFFER_SIZE];
1.7 secret 425:
426: /* Push the data down the stream
427: **
428: */
429: targetClass = *(sink->isa); /* Copy pointers to procedures */
430:
431: /* Push binary from socket down sink
432: */
433: for(;;) {
434: int status = fread(
435: input_buffer, 1, INPUT_BUFFER_SIZE, fp);
436: if (status == 0) { /* EOF or error */
437: if (ferror(fp) == 0) break;
438: if (TRACE) fprintf(stderr,
439: "HTFormat: Read error, read returns %d\n", ferror(fp));
440: break;
441: }
442: (*targetClass.put_block)(sink, input_buffer, status);
1.13 timbl 443: } /* next bufferload */
1.7 secret 444: }
445:
446:
447:
448:
1.2 timbl 449: /* Push data from a socket down a stream STRIPPING CR
450: ** --------------------------------------------------
451: **
452: ** This routine is responsible for creating and PRESENTING any
1.8 timbl 453: ** graphic (or other) objects described by the socket.
1.2 timbl 454: **
455: ** The file number given is assumed to be a TELNET stream ie containing
456: ** CRLF at the end of lines which need to be stripped to LF for unix
457: ** when the format is textual.
458: **
1.1 timbl 459: */
1.2 timbl 460: PUBLIC void HTCopyNoCR ARGS2(
461: int, file_number,
462: HTStream*, sink)
463: {
1.13 timbl 464: HTStreamClass targetClass;
465: HTInputSocket * isoc;
1.1 timbl 466:
1.2 timbl 467: /* Push the data, ignoring CRLF, down the stream
468: **
469: */
470: targetClass = *(sink->isa); /* Copy pointers to procedures */
471:
472: /* Push text from telnet socket down sink
473: **
474: ** @@@@@ To push strings could be faster? (especially is we
475: ** cheat and don't ignore CR! :-}
476: */
1.13 timbl 477: isoc = HTInputSocket_new(file_number);
1.2 timbl 478: for(;;) {
479: char character;
1.13 timbl 480: character = HTInputSocket_getCharacter(isoc);
1.2 timbl 481: if (character == (char)EOF) break;
482: (*targetClass.put_character)(sink, character);
483: }
1.13 timbl 484: HTInputSocket_free(isoc);
1.2 timbl 485: }
1.1 timbl 486:
1.2 timbl 487:
1.7 secret 488:
1.2 timbl 489: /* Parse a socket given format and file number
490: **
491: ** This routine is responsible for creating and PRESENTING any
492: ** graphic (or other) objects described by the file.
493: **
494: ** The file number given is assumed to be a TELNET stream ie containing
495: ** CRLF at the end of lines which need to be stripped to LF for unix
496: ** when the format is textual.
497: **
498: */
1.14 ! timbl 499:
1.12 timbl 500: PUBLIC int HTParseSocket ARGS3(
1.10 timbl 501: HTFormat, rep_in,
1.2 timbl 502: int, file_number,
1.12 timbl 503: HTRequest *, request)
1.2 timbl 504: {
505: HTStream * stream;
506: HTStreamClass targetClass;
1.1 timbl 507:
1.12 timbl 508: stream = HTStreamStack(rep_in, request);
1.2 timbl 509:
510: if (!stream) {
511: char buffer[1024]; /* @@@@@@@@ */
512: sprintf(buffer, "Sorry, can't convert from %s to %s.",
1.12 timbl 513: HTAtom_name(rep_in), HTAtom_name(request->output_format));
1.3 timbl 514: if (TRACE) fprintf(stderr, "HTFormat: %s\n", buffer);
1.12 timbl 515: return HTLoadError(request->output_stream, 501, buffer);
1.2 timbl 516: }
1.1 timbl 517:
1.3 timbl 518: /* Push the data, ignoring CRLF if necessary, down the stream
519: **
1.2 timbl 520: **
1.3 timbl 521: ** @@ Bug: This decision ought to be made based on "encoding"
1.9 timbl 522: ** rather than on format. @@@ When we handle encoding.
1.3 timbl 523: ** The current method smells anyway.
1.2 timbl 524: */
525: targetClass = *(stream->isa); /* Copy pointers to procedures */
1.10 timbl 526: if (rep_in == WWW_BINARY || HTOutputSource
527: || strstr(HTAtom_name(rep_in), "image/")
528: || strstr(HTAtom_name(rep_in), "video/")) { /* @@@@@@ */
1.2 timbl 529: HTCopy(file_number, stream);
530: } else { /* ascii text with CRLFs :-( */
531: HTCopyNoCR(file_number, stream);
532: }
1.7 secret 533: (*targetClass.free)(stream);
534:
535: return HT_LOADED;
536: }
537:
538:
539:
540: /* Parse a file given format and file pointer
541: **
542: ** This routine is responsible for creating and PRESENTING any
543: ** graphic (or other) objects described by the file.
544: **
545: ** The file number given is assumed to be a TELNET stream ie containing
1.10 timbl 546: ** CRLF at the end of lines which need to be stripped to \n for unix
1.7 secret 547: ** when the format is textual.
548: **
549: */
1.12 timbl 550: PUBLIC int HTParseFile ARGS3(
1.10 timbl 551: HTFormat, rep_in,
1.7 secret 552: FILE *, fp,
1.12 timbl 553: HTRequest *, request)
1.7 secret 554: {
555: HTStream * stream;
556: HTStreamClass targetClass;
557:
1.12 timbl 558: stream = HTStreamStack(rep_in, request);
1.7 secret 559:
560: if (!stream) {
561: char buffer[1024]; /* @@@@@@@@ */
562: sprintf(buffer, "Sorry, can't convert from %s to %s.",
1.12 timbl 563: HTAtom_name(rep_in), HTAtom_name(request->output_format));
1.7 secret 564: if (TRACE) fprintf(stderr, "HTFormat(in HTParseFile): %s\n", buffer);
1.12 timbl 565: return HTLoadError(request->output_stream, 501, buffer);
1.7 secret 566: }
567:
1.9 timbl 568: /* Push the data down the stream
1.7 secret 569: **
570: **
571: ** @@ Bug: This decision ought to be made based on "encoding"
1.10 timbl 572: ** rather than on content-type. @@@ When we handle encoding.
1.7 secret 573: ** The current method smells anyway.
574: */
575: targetClass = *(stream->isa); /* Copy pointers to procedures */
576: HTFileCopy(fp, stream);
1.2 timbl 577: (*targetClass.free)(stream);
1.1 timbl 578:
1.2 timbl 579: return HT_LOADED;
1.1 timbl 580: }
1.2 timbl 581:
1.10 timbl 582:
583: /* Converter stream: Network Telnet to internal character text
584: ** -----------------------------------------------------------
585: **
586: ** The input is assumed to be in ASCII, with lines delimited
587: ** by (13,10) pairs, These pairs are converted into (CR,LF)
588: ** pairs in the local representation. The (CR,LF) sequence
589: ** when found is changed to a '\n' character, the internal
590: ** C representation of a new line.
591: */
592:
593:
1.11 timbl 594: PRIVATE void NetToText_put_character ARGS2(HTStream *, me, char, net_char)
1.10 timbl 595: {
596: char c = FROMASCII(net_char);
597: if (me->had_cr) {
598: if (c==LF) {
599: me->sink->isa->put_character(me->sink, '\n'); /* Newline */
600: me->had_cr = NO;
601: return;
602: } else {
603: me->sink->isa->put_character(me->sink, CR); /* leftover */
604: }
605: }
606: me->had_cr = (c==CR);
607: if (!me->had_cr)
608: me->sink->isa->put_character(me->sink, c); /* normal */
609: }
610:
1.11 timbl 611: PRIVATE void NetToText_put_string ARGS2(HTStream *, me, CONST char *, s)
1.10 timbl 612: {
613: CONST char * p;
614: for(p=s; *p; p++) NetToText_put_character(me, *p);
615: }
616:
1.11 timbl 617: PRIVATE void NetToText_put_block ARGS3(HTStream *, me, CONST char*, s, int, l)
1.10 timbl 618: {
619: CONST char * p;
620: for(p=s; p<(s+l); p++) NetToText_put_character(me, *p);
621: }
622:
623: PRIVATE void NetToText_free ARGS1(HTStream *, me)
624: {
625: me->sink->isa->free(me->sink); /* Close rest of pipe */
626: free(me);
627: }
628:
629: PRIVATE void NetToText_abort ARGS2(HTStream *, me, HTError, e)
630: {
631: me->sink->isa->abort(me->sink,e); /* Abort rest of pipe */
632: free(me);
633: }
634:
635: /* The class structure
636: */
637: PRIVATE HTStreamClass NetToTextClass = {
638: "NetToText",
639: NetToText_free,
640: NetToText_abort,
641: NetToText_put_character,
642: NetToText_put_string,
643: NetToText_put_block
644: };
645:
646: /* The creation method
647: */
648: PUBLIC HTStream * HTNetToText ARGS1(HTStream *, sink)
649: {
650: HTStream* me = (HTStream*)malloc(sizeof(*me));
651: if (me == NULL) outofmem(__FILE__, "NetToText");
652: me->isa = &NetToTextClass;
653:
654: me->had_cr = NO;
655: me->sink = sink;
656: return me;
657: }
1.2 timbl 658:
659:
Webmaster