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