~ubuntu-branches/ubuntu/warty/lynx/warty-security

« back to all changes in this revision

Viewing changes to WWW/Library/Implementation/HTAAUtil.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2004-09-16 12:14:10 UTC
  • Revision ID: james.westby@ubuntu.com-20040916121410-cz1gu92c4nqfeyrg
Tags: upstream-2.8.5
ImportĀ upstreamĀ versionĀ 2.8.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* MODULE                                                       HTAAUtil.c
 
2
**              COMMON PARTS OF ACCESS AUTHORIZATION MODULE
 
3
**                      FOR BOTH SERVER AND BROWSER
 
4
**
 
5
** IMPORTANT:
 
6
**      Routines in this module use dynamic allocation, but free
 
7
**      automatically all the memory reserved by them.
 
8
**
 
9
**      Therefore the caller never has to (and never should)
 
10
**      free() any object returned by these functions.
 
11
**
 
12
**      Therefore also all the strings returned by this package
 
13
**      are only valid until the next call to the same function
 
14
**      is made.  This approach is selected, because of the nature
 
15
**      of access authorization: no string returned by the package
 
16
**      needs to be valid longer than until the next call.
 
17
**
 
18
**      This also makes it easy to plug the AA package in:
 
19
**      you don't have to ponder whether to free() something
 
20
**      here or is it done somewhere else (because it is always
 
21
**      done somewhere else).
 
22
**
 
23
**      The strings that the package needs to store are copied
 
24
**      so the original strings given as parameters to AA
 
25
**      functions may be freed or modified with no side effects.
 
26
**
 
27
**      The AA package does not free() anything else than what
 
28
**      it has itself allocated.
 
29
**
 
30
**      AA (Access Authorization) package means modules which
 
31
**      names start with HTAA.
 
32
**
 
33
** AUTHORS:
 
34
**      AL      Ari Luotonen    luotonen@dxcern.cern.ch
 
35
**      MD      Mark Donszelmann    duns@vxdeop.cern.ch
 
36
**
 
37
** HISTORY:
 
38
**       8 Nov 93  MD   (VMS only) Added case insensitive comparison in HTAA_templateCaseMatch
 
39
**
 
40
**
 
41
** BUGS:
 
42
**
 
43
**
 
44
*/
 
45
 
 
46
#include <HTUtils.h>
 
47
 
 
48
#include <HTAAUtil.h>   /* Implemented here     */
 
49
#include <HTAssoc.h>    /* Assoc list           */
 
50
#include <HTTCP.h>
 
51
#include <HTTP.h>
 
52
 
 
53
#include <LYStrings.h>
 
54
#include <LYLeaks.h>
 
55
 
 
56
/* PUBLIC                                               HTAAScheme_enum()
 
57
**              TRANSLATE SCHEME NAME INTO
 
58
**              A SCHEME ENUMERATION
 
59
**
 
60
** ON ENTRY:
 
61
**      name            is a string representing the scheme name.
 
62
**
 
63
** ON EXIT:
 
64
**      returns         the enumerated constant for that scheme.
 
65
*/
 
66
PUBLIC HTAAScheme HTAAScheme_enum ARGS1(CONST char*, name)
 
67
{
 
68
    char *upcased = NULL;
 
69
 
 
70
    if (!name)
 
71
        return HTAA_UNKNOWN;
 
72
 
 
73
    StrAllocCopy(upcased, name);
 
74
    LYUpperCase(upcased);
 
75
 
 
76
    if (!strncmp(upcased, "NONE", 4)) {
 
77
        FREE(upcased);
 
78
        return HTAA_NONE;
 
79
    } else if (!strncmp(upcased, "BASIC", 5)) {
 
80
        FREE(upcased);
 
81
        return HTAA_BASIC;
 
82
    } else if (!strncmp(upcased, "PUBKEY", 6)) {
 
83
        FREE(upcased);
 
84
        return HTAA_PUBKEY;
 
85
    } else if (!strncmp(upcased, "KERBEROSV4", 10)) {
 
86
        FREE(upcased);
 
87
        return HTAA_KERBEROS_V4;
 
88
    } else if (!strncmp(upcased, "KERBEROSV5", 10)) {
 
89
        FREE(upcased);
 
90
        return HTAA_KERBEROS_V5;
 
91
    } else {
 
92
        FREE(upcased);
 
93
        return HTAA_UNKNOWN;
 
94
    }
 
95
}
 
96
 
 
97
 
 
98
/* PUBLIC                                               HTAAScheme_name()
 
99
**                      GET THE NAME OF A GIVEN SCHEME
 
100
** ON ENTRY:
 
101
**      scheme          is one of the scheme enum values:
 
102
**                      HTAA_NONE, HTAA_BASIC, HTAA_PUBKEY, ...
 
103
**
 
104
** ON EXIT:
 
105
**      returns         the name of the scheme, i.e.
 
106
**                      "None", "Basic", "Pubkey", ...
 
107
*/
 
108
PUBLIC char *HTAAScheme_name ARGS1(HTAAScheme, scheme)
 
109
{
 
110
    switch (scheme) {
 
111
        case HTAA_NONE:
 
112
            return "None";
 
113
        case HTAA_BASIC:
 
114
            return "Basic";
 
115
        case HTAA_PUBKEY:
 
116
            return "Pubkey";
 
117
        case HTAA_KERBEROS_V4:
 
118
            return "KerberosV4";
 
119
        case HTAA_KERBEROS_V5:
 
120
            return "KerberosV5";
 
121
        case HTAA_UNKNOWN:
 
122
            return "UNKNOWN";
 
123
        default:
 
124
            return "THIS-IS-A-BUG";
 
125
    }
 
126
}
 
127
 
 
128
 
 
129
/* PUBLIC                                                   HTAAMethod_enum()
 
130
**              TRANSLATE METHOD NAME INTO AN ENUMERATED VALUE
 
131
** ON ENTRY:
 
132
**      name            is the method name to translate.
 
133
**
 
134
** ON EXIT:
 
135
**      returns         HTAAMethod enumerated value corresponding
 
136
**                      to the given name.
 
137
*/
 
138
PUBLIC HTAAMethod HTAAMethod_enum ARGS1(CONST char *, name)
 
139
{
 
140
    if (!name)
 
141
        return METHOD_UNKNOWN;
 
142
 
 
143
    if (0==strcasecomp(name, "GET"))
 
144
        return METHOD_GET;
 
145
    else if (0==strcasecomp(name, "PUT"))
 
146
        return METHOD_PUT;
 
147
    else
 
148
        return METHOD_UNKNOWN;
 
149
}
 
150
 
 
151
 
 
152
/* PUBLIC                                               HTAAMethod_name()
 
153
**                      GET THE NAME OF A GIVEN METHOD
 
154
** ON ENTRY:
 
155
**      method          is one of the method enum values:
 
156
**                      METHOD_GET, METHOD_PUT, ...
 
157
**
 
158
** ON EXIT:
 
159
**      returns         the name of the scheme, i.e.
 
160
**                      "GET", "PUT", ...
 
161
*/
 
162
PUBLIC char *HTAAMethod_name ARGS1(HTAAMethod, method)
 
163
{
 
164
    switch (method) {
 
165
      case METHOD_GET:
 
166
          return "GET";
 
167
      case METHOD_PUT:
 
168
          return "PUT";
 
169
      case METHOD_UNKNOWN:
 
170
          return "UNKNOWN";
 
171
      default:
 
172
          return "THIS-IS-A-BUG";
 
173
    }
 
174
}
 
175
 
 
176
 
 
177
/* PUBLIC                                               HTAAMethod_inList()
 
178
**              IS A METHOD IN A LIST OF METHOD NAMES
 
179
** ON ENTRY:
 
180
**      method          is the method to look for.
 
181
**      list            is a list of method names.
 
182
**
 
183
** ON EXIT:
 
184
**      returns         YES, if method was found.
 
185
**                      NO, if not found.
 
186
*/
 
187
PUBLIC BOOL HTAAMethod_inList ARGS2(HTAAMethod, method,
 
188
                                    HTList *,   list)
 
189
{
 
190
    HTList *cur = list;
 
191
    char *item;
 
192
 
 
193
    while (NULL != (item = (char*)HTList_nextObject(cur))) {
 
194
        CTRACE((tfp, " %s", item));
 
195
        if (method == HTAAMethod_enum(item))
 
196
            return YES;
 
197
    }
 
198
 
 
199
    return NO;  /* Not found */
 
200
}
 
201
 
 
202
 
 
203
/* PUBLIC                                               HTAA_templateMatch()
 
204
**              STRING COMPARISON FUNCTION FOR FILE NAMES
 
205
**                 WITH ONE WILDCARD * IN THE TEMPLATE
 
206
** NOTE:
 
207
**      This is essentially the same code as in HTRules.c, but it
 
208
**      cannot be used because it is embedded in between other code.
 
209
**      (In fact, HTRules.c should use this routine, but then this
 
210
**       routine would have to be more sophisticated... why is life
 
211
**       sometimes so hard...)
 
212
**
 
213
** ON ENTRY:
 
214
**      template        is a template string to match the file name
 
215
**                      against, may contain a single wildcard
 
216
**                      character * which matches zero or more
 
217
**                      arbitrary characters.
 
218
**      filename        is the filename (or pathname) to be matched
 
219
**                      against the template.
 
220
**
 
221
** ON EXIT:
 
222
**      returns         YES, if filename matches the template.
 
223
**                      NO, otherwise.
 
224
*/
 
225
PUBLIC BOOL HTAA_templateMatch ARGS2(CONST char *, template,
 
226
                                     CONST char *, filename)
 
227
{
 
228
    CONST char *p = template;
 
229
    CONST char *q = filename;
 
230
    int m;
 
231
 
 
232
    for (; *p  &&  *q  &&  *p == *q; p++, q++)  /* Find first mismatch */
 
233
        ; /* do nothing else */
 
234
 
 
235
    if (!*p && !*q)
 
236
        return YES;                     /* Equally long equal strings */
 
237
    else if ('*' == *p) {               /* Wildcard */
 
238
        p++;                            /* Skip wildcard character */
 
239
        m = strlen(q) - strlen(p);      /* Amount to match to wildcard */
 
240
        if (m < 0)
 
241
            return NO;                  /* No match, filename too short */
 
242
        else {                  /* Skip the matched characters and compare */
 
243
            if (strcmp(p, q+m))
 
244
                return NO;      /* Tail mismatch */
 
245
            else
 
246
                return YES;     /* Tail match */
 
247
        }
 
248
    }   /* if wildcard */
 
249
    else
 
250
        return NO;              /* Length or character mismatch */
 
251
}
 
252
 
 
253
 
 
254
/* PUBLIC                                               HTAA_templateCaseMatch()
 
255
**              STRING COMPARISON FUNCTION FOR FILE NAMES
 
256
**                 WITH ONE WILDCARD * IN THE TEMPLATE (Case Insensitive)
 
257
** NOTE:
 
258
**      This is essentially the same code as in HTAA_templateMatch, but
 
259
**      it compares case insensitive (for VMS). Reason for this routine
 
260
**      is that HTAA_templateMatch gets called from several places, also
 
261
**      there where a case sensitive match is needed, so one cannot just
 
262
**      change the HTAA_templateMatch routine for VMS.
 
263
**
 
264
** ON ENTRY:
 
265
**      template        is a template string to match the file name
 
266
**                      against, may contain a single wildcard
 
267
**                      character * which matches zero or more
 
268
**                      arbitrary characters.
 
269
**      filename        is the filename (or pathname) to be matched
 
270
**                      against the template.
 
271
**
 
272
** ON EXIT:
 
273
**      returns         YES, if filename matches the template.
 
274
**                      NO, otherwise.
 
275
*/
 
276
PUBLIC BOOL HTAA_templateCaseMatch ARGS2(CONST char *, template,
 
277
                                         CONST char *, filename)
 
278
{
 
279
    CONST char *p = template;
 
280
    CONST char *q = filename;
 
281
    int m;
 
282
 
 
283
    /* Find first mismatch */
 
284
    for (; *p  &&  *q  &&  TOUPPER(*p) == TOUPPER(*q); p++, q++)
 
285
        ; /* do nothing else */
 
286
 
 
287
    if (!*p && !*q)
 
288
        return YES;                     /* Equally long equal strings */
 
289
    else if ('*' == *p) {               /* Wildcard */
 
290
        p++;                            /* Skip wildcard character */
 
291
        m = strlen(q) - strlen(p);      /* Amount to match to wildcard */
 
292
        if (m < 0)
 
293
            return NO;                  /* No match, filename too short */
 
294
        else {                  /* Skip the matched characters and compare */
 
295
            if (strcasecomp(p, q+m))
 
296
                return NO;      /* Tail mismatch */
 
297
            else
 
298
                return YES;     /* Tail match */
 
299
        }
 
300
    }   /* if wildcard */
 
301
    else
 
302
        return NO;              /* Length or character mismatch */
 
303
}
 
304
 
 
305
 
 
306
/* PUBLIC                                       HTAA_makeProtectionTemplate()
 
307
**              CREATE A PROTECTION TEMPLATE FOR THE FILES
 
308
**              IN THE SAME DIRECTORY AS THE GIVEN FILE
 
309
**              (Used by server if there is no fancier way for
 
310
**              it to tell the client, and by browser if server
 
311
**              didn't send WWW-ProtectionTemplate: field)
 
312
** ON ENTRY:
 
313
**      docname is the document pathname (from URL).
 
314
**
 
315
** ON EXIT:
 
316
**      returns a template matching docname, and other files
 
317
**              files in that directory.
 
318
**
 
319
**              E.g.  /foo/bar/x.html  =>  /foo/bar/ *
 
320
**                                                  ^
 
321
**                              Space only to prevent it from
 
322
**                              being a comment marker here,
 
323
**                              there really isn't any space.
 
324
*/
 
325
PUBLIC char *HTAA_makeProtectionTemplate ARGS1(CONST char *, docname)
 
326
{
 
327
    char *template = NULL;
 
328
    char *slash = NULL;
 
329
 
 
330
    if (docname) {
 
331
        StrAllocCopy(template, docname);
 
332
        slash = strrchr(template, '/');
 
333
        if (slash)
 
334
            slash++;
 
335
        else
 
336
            slash = template;
 
337
        *slash = '\0';
 
338
        StrAllocCat(template, "*");
 
339
    }
 
340
    else
 
341
        StrAllocCopy(template, "*");
 
342
 
 
343
    CTRACE((tfp, "make_template: made template `%s' for file `%s'\n",
 
344
                template, docname));
 
345
 
 
346
    return template;
 
347
}
 
348
 
 
349
 
 
350
/*
 
351
** Skip leading whitespace from *s forward
 
352
*/
 
353
#define SKIPWS(s) while (*s==' ' || *s=='\t') s++;
 
354
 
 
355
/*
 
356
** Kill trailing whitespace starting from *(s-1) backwards
 
357
*/
 
358
#define KILLWS(s) {char *c=s-1; while (*c==' ' || *c=='\t') *(c--)='\0';}
 
359
 
 
360
 
 
361
/* PUBLIC                                               HTAA_parseArgList()
 
362
**              PARSE AN ARGUMENT LIST GIVEN IN A HEADER FIELD
 
363
** ON ENTRY:
 
364
**      str     is a comma-separated list:
 
365
**
 
366
**                      item, item, item
 
367
**              where
 
368
**                      item ::= value
 
369
**                             | name=value
 
370
**                             | name="value"
 
371
**
 
372
**              Leading and trailing whitespace is ignored
 
373
**              everywhere except inside quotes, so the following
 
374
**              examples are equal:
 
375
**
 
376
**                      name=value,foo=bar
 
377
**                       name="value",foo="bar"
 
378
**                        name = value ,  foo = bar
 
379
**                         name = "value" ,  foo = "bar"
 
380
**
 
381
** ON EXIT:
 
382
**      returns a list of name-value pairs (actually HTAssocList*).
 
383
**              For items with no name, just value, the name is
 
384
**              the number of order number of that item. E.g.
 
385
**              "1" for the first, etc.
 
386
*/
 
387
PUBLIC HTAssocList *HTAA_parseArgList ARGS1(char *, str)
 
388
{
 
389
    HTAssocList *assoc_list = HTAssocList_new();
 
390
    char *cur = NULL;
 
391
    char *name = NULL;
 
392
    int n = 0;
 
393
 
 
394
    if (!str)
 
395
        return assoc_list;
 
396
 
 
397
    while (*str) {
 
398
        SKIPWS(str);                            /* Skip leading whitespace */
 
399
        cur = str;
 
400
        n++;
 
401
 
 
402
        while (*cur  &&  *cur != '='  &&  *cur != ',')
 
403
            cur++;      /* Find end of name (or lonely value without a name) */
 
404
        KILLWS(cur);    /* Kill trailing whitespace */
 
405
 
 
406
        if (*cur == '=') {                      /* Name followed by a value */
 
407
            *(cur++) = '\0';                    /* Terminate name */
 
408
            StrAllocCopy(name, str);
 
409
            SKIPWS(cur);                        /* Skip WS leading the value */
 
410
            str = cur;
 
411
            if (*str == '"') {                  /* Quoted value */
 
412
                str++;
 
413
                cur = str;
 
414
                while (*cur  &&  *cur != '"')
 
415
                    cur++;
 
416
                if (*cur == '"')
 
417
                    *(cur++) = '\0';            /* Terminate value */
 
418
                /* else it is lacking terminating quote */
 
419
                SKIPWS(cur);                    /* Skip WS leading comma */
 
420
                if (*cur == ',')
 
421
                    cur++;                      /* Skip separating colon */
 
422
            }
 
423
            else {                              /* Unquoted value */
 
424
                while (*cur  &&  *cur != ',')
 
425
                    cur++;
 
426
                KILLWS(cur);                    /* Kill trailing whitespace */
 
427
                if (*cur == ',')
 
428
                    *(cur++) = '\0';
 
429
                /* else *cur already NULL */
 
430
            }
 
431
        }
 
432
        else {  /* No name, just a value */
 
433
            if (*cur == ',')
 
434
                *(cur++) = '\0';                /* Terminate value */
 
435
            /* else last value on line (already terminated by NULL) */
 
436
            HTSprintf0(&name, "%d", n);         /* Item order number for name */
 
437
        }
 
438
        HTAssocList_add(assoc_list, name, str);
 
439
        str = cur;
 
440
    } /* while *str */
 
441
 
 
442
    FREE(name);
 
443
    return assoc_list;
 
444
}
 
445
 
 
446
 
 
447
/************** HEADER LINE READER -- DOES UNFOLDING *************************/
 
448
 
 
449
#define BUFFER_SIZE     1024
 
450
 
 
451
PRIVATE size_t buffer_length;
 
452
PRIVATE char *buffer = 0;
 
453
PRIVATE char *start_pointer;
 
454
PRIVATE char *end_pointer;
 
455
PRIVATE int in_soc = -1;
 
456
 
 
457
#ifdef LY_FIND_LEAKS
 
458
PRIVATE void FreeHTAAUtil NOARGS
 
459
{
 
460
    FREE(buffer);
 
461
}
 
462
#endif /* LY_FIND_LEAKS */
 
463
 
 
464
/* PUBLIC                                               HTAA_setupReader()
 
465
**              SET UP HEADER LINE READER, i.e., give
 
466
**              the already-read-but-not-yet-processed
 
467
**              buffer of text to be read before more
 
468
**              is read from the socket.
 
469
** ON ENTRY:
 
470
**      start_of_headers is a pointer to a buffer containing
 
471
**                      the beginning of the header lines
 
472
**                      (rest will be read from a socket).
 
473
**      length          is the number of valid characters in
 
474
**                      'start_of_headers' buffer.
 
475
**      soc             is the socket to use when start_of_headers
 
476
**                      buffer is used up.
 
477
** ON EXIT:
 
478
**      returns         nothing.
 
479
**                      Subsequent calls to HTAA_getUnfoldedLine()
 
480
**                      will use this buffer first and then
 
481
**                      proceed to read from socket.
 
482
*/
 
483
PUBLIC void HTAA_setupReader ARGS3(char *,      start_of_headers,
 
484
                                   int,         length,
 
485
                                   int,         soc)
 
486
{
 
487
    if (!start_of_headers)
 
488
        length = 0;            /* initialize length (is this reached at all?) */
 
489
    if (buffer == NULL) {                                      /* first call? */
 
490
        buffer_length = length;
 
491
        if (buffer_length < BUFFER_SIZE)     /* would fall below BUFFER_SIZE? */
 
492
            buffer_length = BUFFER_SIZE;
 
493
        buffer = (char*)malloc((size_t)(sizeof(char)*(buffer_length + 1)));
 
494
    }
 
495
    else if (length > (int)buffer_length) {               /* need more space? */
 
496
        buffer_length = length;
 
497
        buffer = (char*)realloc((char*)buffer,
 
498
                                (size_t)(sizeof(char)*(buffer_length + 1)));
 
499
    }
 
500
    if (buffer == NULL) outofmem(__FILE__, "HTAA_setupReader");
 
501
#ifdef LY_FIND_LEAKS
 
502
    atexit(FreeHTAAUtil);
 
503
#endif
 
504
    start_pointer = buffer;
 
505
    if (start_of_headers) {
 
506
        strncpy(buffer, start_of_headers, length);
 
507
        buffer[length] = '\0';
 
508
        end_pointer = buffer + length;
 
509
    }
 
510
    else {
 
511
        *start_pointer = '\0';
 
512
        end_pointer = start_pointer;
 
513
    }
 
514
    in_soc = soc;
 
515
}
 
516
 
 
517
 
 
518
/* PUBLIC                                               HTAA_getUnfoldedLine()
 
519
**              READ AN UNFOLDED HEADER LINE FROM SOCKET
 
520
** ON ENTRY:
 
521
**      HTAA_setupReader must absolutely be called before
 
522
**      this function to set up internal buffer.
 
523
**
 
524
** ON EXIT:
 
525
**      returns a newly-allocated character string representing
 
526
**              the read line.  The line is unfolded, i.e.
 
527
**              lines that begin with whitespace are appended
 
528
**              to current line.  E.g.
 
529
**
 
530
**                      Field-Name: Blaa-Blaa
 
531
**                       This-Is-A-Continuation-Line
 
532
**                       Here-Is_Another
 
533
**
 
534
**              is seen by the caller as:
 
535
**
 
536
**      Field-Name: Blaa-Blaa This-Is-A-Continuation-Line Here-Is_Another
 
537
**
 
538
*/
 
539
PUBLIC char *HTAA_getUnfoldedLine NOARGS
 
540
{
 
541
    char *line = NULL;
 
542
    char *cur;
 
543
    int count;
 
544
    BOOL peek_for_folding = NO;
 
545
 
 
546
    if (in_soc < 0) {
 
547
        CTRACE((tfp, "%s %s\n",
 
548
                    "HTAA_getUnfoldedLine: buffer not initialized",
 
549
                    "with function HTAA_setupReader()"));
 
550
        return NULL;
 
551
    }
 
552
 
 
553
    for(;;) {
 
554
 
 
555
        /* Reading from socket */
 
556
 
 
557
        if (start_pointer >= end_pointer) {/*Read the next block and continue*/
 
558
#ifdef USE_SSL
 
559
            if (SSL_handle)
 
560
                count = SSL_read(SSL_handle, buffer, BUFFER_SIZE);
 
561
            else
 
562
                count = NETREAD(in_soc, buffer, BUFFER_SIZE);
 
563
#else
 
564
            count = NETREAD(in_soc, buffer, BUFFER_SIZE);
 
565
#endif /* USE_SSL */
 
566
            if (count <= 0) {
 
567
                in_soc = -1;
 
568
                return line;
 
569
            }
 
570
            start_pointer = buffer;
 
571
            end_pointer = buffer + count;
 
572
            *end_pointer = '\0';
 
573
#ifdef NOT_ASCII
 
574
            cur = start_pointer;
 
575
            while (cur < end_pointer) {
 
576
                *cur = TOASCII(*cur);
 
577
                cur++;
 
578
            }
 
579
#endif /*NOT_ASCII*/
 
580
        }
 
581
        cur = start_pointer;
 
582
 
 
583
 
 
584
        /* Unfolding */
 
585
 
 
586
        if (peek_for_folding) {
 
587
            if (*cur != ' '  &&  *cur != '\t')
 
588
                return line;    /* Ok, no continuation line */
 
589
            else                /* So this is a continuation line, continue */
 
590
                peek_for_folding = NO;
 
591
        }
 
592
 
 
593
 
 
594
        /* Finding end-of-line */
 
595
 
 
596
        while (cur < end_pointer && *cur != '\n') /* Find the end-of-line */
 
597
            cur++;                                /* (or end-of-buffer).  */
 
598
 
 
599
 
 
600
        /* Terminating line */
 
601
 
 
602
        if (cur < end_pointer) {        /* So *cur==LF, terminate line */
 
603
            *cur = '\0';                /* Overwrite LF */
 
604
            if (*(cur-1) == '\r')
 
605
                *(cur-1) = '\0';        /* Overwrite CR */
 
606
            peek_for_folding = YES;     /* Check for a continuation line */
 
607
        }
 
608
 
 
609
 
 
610
        /* Copying the result */
 
611
 
 
612
        if (line)
 
613
            StrAllocCat(line, start_pointer);   /* Append */
 
614
        else
 
615
            StrAllocCopy(line, start_pointer);  /* A new line */
 
616
 
 
617
        start_pointer = cur+1;  /* Skip the read line */
 
618
 
 
619
    } /* forever */
 
620
}