Annotation of libwww/Library/src/HTWWWStr.c, revision 2.36
2.1 frystyk 1: /* HTWWWStr.c
2: ** WWW RELATED STRING UTILITIES
3: **
4: ** (c) COPYRIGHT MIT 1995.
5: ** Please first read the full copyright statement in the file COPYRIGH.
2.36 ! kahan 6: ** @(#) $Id: HTWWWStr.c,v 2.35 1999/03/24 16:26:57 frystyk Exp $
2.1 frystyk 7: **
8: ** Now 13 95 Spwaned from HTString.c
9: */
10:
11: /* Library include files */
2.24 frystyk 12: #include "wwwsys.h"
2.10 frystyk 13: #include "WWWUtil.h"
2.1 frystyk 14: #include "HTParse.h"
2.17 frystyk 15: #include "HTInet.h"
2.11 frystyk 16: #include "HTUser.h"
2.1 frystyk 17: #include "HTWWWStr.h" /* Implemented here */
18:
19: PRIVATE char * months[12] = {
20: "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
21: };
22:
2.8 frystyk 23: #ifndef HAVE_STRFTIME
2.1 frystyk 24: PRIVATE char * wkdays[7] = {
2.28 frystyk 25: "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
2.1 frystyk 26: };
27: #endif
28:
29: /* ------------------------------------------------------------------------- */
30:
31: /* Find next Field
32: ** ---------------
33: ** Finds the next RFC822 token in a string
34: ** On entry,
35: ** *pstr points to a string containing a word separated
36: ** by white white space "," ";" or "=". The word
37: ** can optionally be quoted using <"> or "<" ">"
38: ** Comments surrrounded by '(' ')' are filtered out
39: **
40: ** On exit,
41: ** *pstr has been moved to the first delimiter past the
42: ** field
43: ** THE STRING HAS BEEN MUTILATED by a 0 terminator
44: **
45: ** Returns a pointer to the first word or NULL on error
46: */
47: PUBLIC char * HTNextField (char ** pstr)
48: {
49: char * p = *pstr;
50: char * start = NULL;
2.14 frystyk 51: if (!pstr || !*pstr) return NULL;
2.1 frystyk 52: while (1) {
53: /* Strip white space and other delimiters */
2.21 frystyk 54: while (*p && (isspace((int) *p) || *p==',' || *p==';' || *p=='=')) p++;
2.1 frystyk 55: if (!*p) {
56: *pstr = p;
57: return NULL; /* No field */
58: }
59:
60: if (*p == '"') { /* quoted field */
61: start = ++p;
62: for(;*p && *p!='"'; p++)
63: if (*p == '\\' && *(p+1)) p++; /* Skip escaped chars */
64: break; /* kr95-10-9: needs to stop here */
65: } else if (*p == '<') { /* quoted field */
66: start = ++p;
67: for(;*p && *p!='>'; p++)
68: if (*p == '\\' && *(p+1)) p++; /* Skip escaped chars */
69: break; /* kr95-10-9: needs to stop here */
70: } else if (*p == '(') { /* Comment */
71: for(;*p && *p!=')'; p++)
72: if (*p == '\\' && *(p+1)) p++; /* Skip escaped chars */
73: p++;
74: } else { /* Spool field */
75: start = p;
2.21 frystyk 76: while(*p && !isspace((int) *p) && *p!=',' && *p!=';' && *p!='=')
2.1 frystyk 77: p++;
78: break; /* Got it */
79: }
80: }
2.22 frystyk 81: if (*p) *p++ = '\0';
82: *pstr = p;
83: return start;
84: }
85:
86: /* Find next LWS delimited token
87: ** -----------------------------
88: ** On entry,
89: ** *pstr points to a string containing a word separated
90: ** by white white space "," ";" or "=". The word
91: ** can optionally be quoted using <"> or "<" ">"
92: ** Comments surrrounded by '(' ')' are filtered out
93: **
94: ** On exit,
95: ** *pstr has been moved to the first delimiter past the
96: ** field
97: ** THE STRING HAS BEEN MUTILATED by a 0 terminator
98: **
99: ** Returns a pointer to the first word or NULL on error
100: */
101: PUBLIC char * HTNextLWSToken (char ** pstr)
102: {
103: char * p = *pstr;
104: char * start = NULL;
105: if (!pstr || !*pstr) return NULL;
106:
107: /* Strip initial white space */
108: while (*p && (isspace((int) *p))) p++;
109: if (!*p) {
110: *pstr = p;
111: return NULL; /* No field */
112: }
113:
114: /* Now search for the next white space */
115: start = p;
116: while(*p && !isspace((int) *p)) p++;
117:
2.1 frystyk 118: if (*p) *p++ = '\0';
119: *pstr = p;
120: return start;
121: }
122:
2.16 frystyk 123: /* Find next Name-value pair
124: ** -------------------------
125: ** This is the same as HTNextField but it does not look for '=' as a
126: ** separator so if there is a name-value pair then both parts are
127: ** returned.
128: ** Returns a pointer to the first word or NULL on error
129: */
130: PUBLIC char * HTNextPair (char ** pstr)
131: {
132: char * p = *pstr;
133: char * start = NULL;
134: if (!pstr || !*pstr) return NULL;
135: while (1) {
136: /* Strip white space and other delimiters */
137: while (*p && (*p==',' || *p==';')) p++;
138: if (!*p) {
139: *pstr = p;
140: return NULL; /* No field */
141: }
142:
143: if (*p == '"') { /* quoted field */
144: start = ++p;
145: for(;*p && *p!='"'; p++)
146: if (*p == '\\' && *(p+1)) p++; /* Skip escaped chars */
147: break; /* kr95-10-9: needs to stop here */
148: } else if (*p == '<') { /* quoted field */
149: start = ++p;
150: for(;*p && *p!='>'; p++)
151: if (*p == '\\' && *(p+1)) p++; /* Skip escaped chars */
152: break; /* kr95-10-9: needs to stop here */
153: } else if (*p == '(') { /* Comment */
154: for(;*p && *p!=')'; p++)
155: if (*p == '\\' && *(p+1)) p++; /* Skip escaped chars */
156: p++;
157: } else { /* Spool field */
158: start = p;
159: while(*p && *p!=',' && *p!=';')
2.35 frystyk 160: p++;
161: break; /* Got it */
162: }
163: }
164: if (*p) *p++ = '\0';
165: *pstr = p;
166: return start;
167: }
168:
169: /* Find next Name-value param
170: ** --------------------------
171: ** This is the same as HTNextPair but doesn't look for ','
172: ** Returns a pointer to the first word or NULL on error
173: */
174: PUBLIC char * HTNextParam (char ** pstr)
175: {
176: char * p = *pstr;
177: char * start = NULL;
178: if (!pstr || !*pstr) return NULL;
179: while (1) {
180: /* Strip white space and other delimiters */
181: while (*p && *p==';') p++;
182: if (!*p) {
183: *pstr = p;
184: return NULL; /* No field */
185: }
186:
187: if (*p == '"') { /* quoted field */
188: start = ++p;
189: for(;*p && *p!='"'; p++)
190: if (*p == '\\' && *(p+1)) p++; /* Skip escaped chars */
191: break; /* kr95-10-9: needs to stop here */
192: } else if (*p == '<') { /* quoted field */
193: start = ++p;
194: for(;*p && *p!='>'; p++)
195: if (*p == '\\' && *(p+1)) p++; /* Skip escaped chars */
196: break; /* kr95-10-9: needs to stop here */
197: } else if (*p == '(') { /* Comment */
198: for(;*p && *p!=')'; p++)
199: if (*p == '\\' && *(p+1)) p++; /* Skip escaped chars */
200: p++;
201: } else { /* Spool field */
202: start = p;
203: while (*p && *p!=';')
2.20 frystyk 204: p++;
2.33 frystyk 205: break; /* Got it */
206: }
207: }
208: if (*p) *p++ = '\0';
209: *pstr = p;
210: return start;
211: }
212:
213: /* Find next element in a comma separated string
214: ** ---------------------------------------------
215: ** This is the same as HTNextPair but it does not look for anything
216: ** else than ',' as separator
217: ** Returns a pointer to the first word or NULL on error
218: */
219: PUBLIC char * HTNextElement (char ** pstr)
220: {
221: char * p = *pstr;
222: char * start = NULL;
223: if (!pstr || !*pstr) return NULL;
224:
225: /* Strip white space and other delimiters */
226: while (*p && ((isspace((int) *p)) || *p==',')) p++;
227: if (!*p) {
228: *pstr = p;
229: return NULL; /* No field */
230: }
231: start = p;
232: while (1) {
233: if (*p == '"') { /* quoted field */
234: for(;*p && *p!='"'; p++)
235: if (*p == '\\' && *(p+1)) p++; /* Skip escaped chars */
236: p++;
237: } else if (*p == '<') { /* quoted field */
238: for(;*p && *p!='>'; p++)
239: if (*p == '\\' && *(p+1)) p++; /* Skip escaped chars */
240: p++;
241: } else if (*p == '(') { /* Comment */
242: for(;*p && *p!=')'; p++)
243: if (*p == '\\' && *(p+1)) p++; /* Skip escaped chars */
244: p++;
245: } else { /* Spool field */
246: while (*p && *p!=',') p++;
2.20 frystyk 247: break; /* Got it */
248: }
249: }
250: if (*p) *p++ = '\0';
251: *pstr = p;
252: return start;
253: }
254:
255: /* Find next "/" delimied segment
256: ** ------------------------------
257: ** This is the same as HTNextField but it includes "/" as a delimiter.
258: ** Returns a pointer to the first segment or NULL on error
259: */
260: PUBLIC char * HTNextSegment (char ** pstr)
261: {
262: char * p = *pstr;
263: char * start = NULL;
264: if (!pstr || !*pstr) return NULL;
265: while (1) {
266: /* Strip white space and other delimiters */
2.21 frystyk 267: while (*p && (isspace((int) *p) || *p==',' || *p==';' || *p=='=' || *p=='/')) p++;
2.20 frystyk 268: if (!*p) {
269: *pstr = p;
270: return NULL; /* No field */
271: }
272:
273: if (*p == '"') { /* quoted field */
274: start = ++p;
275: for(;*p && *p!='"'; p++)
276: if (*p == '\\' && *(p+1)) p++; /* Skip escaped chars */
277: break; /* kr95-10-9: needs to stop here */
278: } else if (*p == '<') { /* quoted field */
279: start = ++p;
280: for(;*p && *p!='>'; p++)
281: if (*p == '\\' && *(p+1)) p++; /* Skip escaped chars */
282: break; /* kr95-10-9: needs to stop here */
283: } else if (*p == '(') { /* Comment */
284: for(;*p && *p!=')'; p++)
285: if (*p == '\\' && *(p+1)) p++; /* Skip escaped chars */
286: p++;
287: } else { /* Spool field */
288: start = p;
2.21 frystyk 289: while(*p && !isspace((int) *p) && *p!=',' && *p!=';' && *p!='=' && *p!='/')
2.16 frystyk 290: p++;
291: break; /* Got it */
292: }
293: }
294: if (*p) *p++ = '\0';
295: *pstr = p;
296: return start;
297: }
298:
2.1 frystyk 299: /*
2.14 frystyk 300: ** Find the next s-expression token from a string of characters.
301: ** We return the name of this expression and the param points to the
302: ** parameters.
303: **
304: ** NOTE: The string has been mutilated by '/0's
305: */
306: PUBLIC char * HTNextSExp (char ** exp, char ** param)
307: {
308: char * p = *exp;
309: char * name = NULL;
310: if (!exp || !*exp) return NULL;
2.21 frystyk 311: while (*p && isspace((int) *p)) p++; /* Strip leading white space */
2.14 frystyk 312: if (!*p) {
313: *exp = p;
314: return NULL; /* No field */
315: }
316: if (*p == '{') { /* Open bracket */
317: int cnt = 1;
318: /*
319: ** Look for name of this expression. If we find a token then search
320: ** for the rest of the expression and remove the end '}'
321: */
322: p++;
323: if ((name = HTNextField(&p)) == NULL) return NULL;
2.21 frystyk 324: while (*p && isspace((int) *p)) p++;
2.14 frystyk 325: *param = p;
326: while (*p) {
327: if (*p == '{') cnt++;
328: if (*p == '}') cnt--;
329: if (!cnt) {
330: *p = '\0';
331: break;
332: }
333: p++;
334: }
335: }
336: return name;
337: }
338:
339: /*
2.1 frystyk 340: ** Returns a Message-ID string including the open '<' and the closing '>'.
341: ** The format of the string is:
342: **
343: ** "<" time-in-sec "Z" process-id "@" FQDN ">"
344: **
345: ** or on systems that doesn't have process-id:
346: **
347: ** "<" time-in-sec "Z" user "@" FQDN ">"
348: **
349: ** Returns a pointer to the MessageID
350: */
2.11 frystyk 351: PUBLIC const char * HTMessageIdStr (HTUserProfile * up)
2.1 frystyk 352: {
353: static char buf[80];
354: time_t sectime = time(NULL);
2.8 frystyk 355: #ifdef HAVE_GETPID
2.11 frystyk 356: const char * address = HTUserProfile_fqdn(up);
2.1 frystyk 357: #else
2.11 frystyk 358: const char * address = HTUserProfile_email(up);
2.8 frystyk 359: #endif /* HAVE_GETPID */
2.1 frystyk 360: if (!address) address = tmpnam(NULL);
361: if ((!address || !*address) && sectime < 0) {
2.34 frystyk 362: HTTRACE(CORE_TRACE, "MessageID... Can't make a unique MessageID\n");
2.1 frystyk 363: return "";
364: }
2.8 frystyk 365: #ifdef HAVE_GETPID
2.23 frystyk 366: sprintf(buf, "<%ldZ%ld@%s>", (long)sectime, (long)getpid(), address ? address : "@@@");
2.1 frystyk 367: #else
368: sprintf(buf, "<%ldZ%s>", sectime, address ? address : "@@@");
2.8 frystyk 369: #endif /* HAVE_GETPID */
2.1 frystyk 370:
371: *(buf+79) = '\0';
372: return buf;
373: }
374:
375: /* Date and Time Parser
376: ** --------------------
377: ** These functions are taken from the server written by Ari Luotonen
378: */
2.14 frystyk 379: #if 0
2.8 frystyk 380: PRIVATE int make_num (const char * s)
2.1 frystyk 381: {
382: if (*s >= '0' && *s <= '9')
383: return 10 * (*s - '0') + *(s+1) - '0';
384: else
385: return *(s+1) - '0';
386: }
2.14 frystyk 387: #endif
388: PRIVATE int make_month (char * s, char ** ends)
2.1 frystyk 389: {
2.14 frystyk 390: char * ptr = s;
2.21 frystyk 391: while (!isalpha((int) *ptr)) ptr++;
2.14 frystyk 392: if (*ptr) {
393: int i;
394: *ends = ptr+3;
395: for (i=0; i<12; i++)
396: if (!strncasecomp(months[i], ptr, 3)) return i;
397: }
2.1 frystyk 398: return 0;
399: }
400:
401: /*
402: ** Parse a str in GMT format to a local time time_t representation
403: ** Four formats are accepted:
404: **
405: ** Wkd, 00 Mon 0000 00:00:00 GMT (rfc1123)
406: ** Weekday, 00-Mon-00 00:00:00 GMT (rfc850)
407: ** Wkd Mon 00 00:00:00 0000 GMT (ctime)
408: ** 1*DIGIT (delta-seconds)
409: */
2.16 frystyk 410: PUBLIC time_t HTParseTime (const char * str, HTUserProfile * up, BOOL expand)
2.1 frystyk 411: {
2.14 frystyk 412: char * s;
2.1 frystyk 413: struct tm tm;
414: time_t t;
415:
416: if (!str) return 0;
417:
418: if ((s = strchr(str, ','))) { /* Thursday, 10-Jun-93 01:29:59 GMT */
419: s++; /* or: Thu, 10 Jan 1993 01:29:59 GMT */
420: while (*s && *s==' ') s++;
421: if (strchr(s,'-')) { /* First format */
2.34 frystyk 422: HTTRACE(CORE_TRACE, "Format...... Weekday, 00-Mon-00 00:00:00 GMT\n");
2.1 frystyk 423: if ((int)strlen(s) < 18) {
2.34 frystyk 424: HTTRACE(CORE_TRACE, "ERROR....... Not a valid time format \"%s\"\n" _ s);
2.1 frystyk 425: return 0;
426: }
2.14 frystyk 427: tm.tm_mday = strtol(s, &s, 10);
428: tm.tm_mon = make_month(s, &s);
2.16 frystyk 429: tm.tm_year = strtol(++s, &s, 10);
2.14 frystyk 430: tm.tm_hour = strtol(s, &s, 10);
431: tm.tm_min = strtol(++s, &s, 10);
432: tm.tm_sec = strtol(++s, &s, 10);
433:
2.1 frystyk 434: } else { /* Second format */
2.34 frystyk 435: HTTRACE(CORE_TRACE, "Format...... Wkd, 00 Mon 0000 00:00:00 GMT\n");
2.1 frystyk 436: if ((int)strlen(s) < 20) {
2.34 frystyk 437: HTTRACE(CORE_TRACE, "ERROR....... Not a valid time format \"%s\"\n" _ s);
2.1 frystyk 438: return 0;
439: }
2.14 frystyk 440: tm.tm_mday = strtol(s, &s, 10);
441: tm.tm_mon = make_month(s, &s);
442: tm.tm_year = strtol(s, &s, 10) - 1900;
443: tm.tm_hour = strtol(s, &s, 10);
444: tm.tm_min = strtol(++s, &s, 10);
445: tm.tm_sec = strtol(++s, &s, 10);
2.1 frystyk 446: }
2.21 frystyk 447: } else if (isdigit((int) *str)) {
2.19 frystyk 448:
449: if (strchr(str, 'T')) { /* ISO (limited format) date string */
2.34 frystyk 450: HTTRACE(CORE_TRACE, "Format...... YYYY.MM.DDThh:mmStzWkd\n");
2.19 frystyk 451: s = (char *) str;
452: while (*s && *s==' ') s++;
453: if ((int)strlen(s) < 21) {
2.34 frystyk 454: HTTRACE(CORE_TRACE, "ERROR....... Not a valid time format `%s\'\n" _ s);
2.19 frystyk 455: return 0;
456: }
457: tm.tm_year = strtol(s, &s, 10) - 1900;
458: tm.tm_mon = strtol(++s, &s, 10);
459: tm.tm_mday = strtol(++s, &s, 10);
460: tm.tm_hour = strtol(++s, &s, 10);
461: tm.tm_min = strtol(++s, &s, 10);
462: tm.tm_sec = strtol(++s, &s, 10);
463:
464: } else { /* delta seconds */
465: t = expand ? time(NULL) + atol(str) : atol(str);
2.34 frystyk 466:
467: #ifdef HTDEBUG
2.19 frystyk 468: if (CORE_TRACE) {
469: if (expand) {
2.36 ! kahan 470: #if defined (HAVE_CTIME_R_2)
! 471: char buffer[CTIME_MAX];
! 472: HTTRACE(CORE_TRACE, "Time string. Delta-time %s parsed to %ld seconds, or in local time: %s" _
! 473: str _ (long) t _ (char *) ctime_r(&t, buffer));
! 474: #elif defined(HAVE_CTIME_R_3)
2.19 frystyk 475: char buffer[CTIME_MAX];
2.34 frystyk 476: HTTRACE(CORE_TRACE, "Time string. Delta-time %s parsed to %ld seconds, or in local time: %s" _
477: str _ (long) t _ (char *) ctime_r(&t, buffer, CTIME_MAX));
2.1 frystyk 478: #else
2.34 frystyk 479: HTTRACE(CORE_TRACE, "Time string. Delta-time %s parsed to %ld seconds, or in local time: %s" _
480: str _ (long) t _ ctime(&t));
481: #endif /* HT_REENTRANT */
2.19 frystyk 482: } else {
2.34 frystyk 483: HTTRACE(CORE_TRACE, "Time string. Delta-time %s parsed to %ld seconds\n" _ str _ (long) t);
2.19 frystyk 484: }
2.16 frystyk 485: }
2.34 frystyk 486: #endif /* HT_DEBUG */
2.19 frystyk 487: return t;
2.1 frystyk 488: }
489:
490: } else { /* Try the other format: Wed Jun 9 01:29:59 1993 GMT */
2.34 frystyk 491: HTTRACE(CORE_TRACE, "Format...... Wkd Mon 00 00:00:00 0000 GMT\n");
2.14 frystyk 492: s = (char *) str;
2.1 frystyk 493: while (*s && *s==' ') s++;
2.34 frystyk 494: HTTRACE(CORE_TRACE, "Trying...... The Wrong time format: %s\n" _ s);
2.1 frystyk 495: if ((int)strlen(s) < 24) {
2.34 frystyk 496: HTTRACE(CORE_TRACE, "ERROR....... Not a valid time format \"%s\"\n" _ s);
2.1 frystyk 497: return 0;
498: }
2.14 frystyk 499: tm.tm_mon = make_month(s, &s);
500: tm.tm_mday = strtol(s, &s, 10);
501: tm.tm_hour = strtol(s, &s, 10);
502: tm.tm_min = strtol(++s, &s, 10);
503: tm.tm_sec = strtol(++s, &s, 10);
504: tm.tm_year = strtol(s, &s, 10) - 1900;
2.1 frystyk 505: }
506: if (tm.tm_sec < 0 || tm.tm_sec > 59 ||
507: tm.tm_min < 0 || tm.tm_min > 59 ||
508: tm.tm_hour < 0 || tm.tm_hour > 23 ||
509: tm.tm_mday < 1 || tm.tm_mday > 31 ||
510: tm.tm_mon < 0 || tm.tm_mon > 11 ||
511: tm.tm_year <70 || tm.tm_year >120) {
2.34 frystyk 512: HTTRACE(CORE_TRACE, "ERROR....... Parsed illegal time: %02d.%02d.%02d %02d:%02d:%02d\n" _
513: tm.tm_mday _ tm.tm_mon+1 _ tm.tm_year _
514: tm.tm_hour _ tm.tm_min _ tm.tm_sec);
2.1 frystyk 515: return 0;
516: }
517:
2.30 frystyk 518: #if 0
2.8 frystyk 519: #if defined(HAVE_TIMEZONE) && defined(HAVE_ALTZONE)
2.1 frystyk 520: tm.tm_isdst = daylight; /* Already taken into account */
2.34 frystyk 521: HTTRACE(CORE_TRACE, "Time string. Daylight is %s\n" _
2.30 frystyk 522: daylight>0 ? "on" : daylight==0 ? "off" : "unknown");
523: #endif
2.1 frystyk 524: #else
2.30 frystyk 525: /* Let mktime decide whether we have DST or not */
2.1 frystyk 526: tm.tm_isdst = -1;
527: #endif
528:
2.8 frystyk 529: #ifdef HAVE_MKTIME
2.1 frystyk 530: t = mktime(&tm);
2.17 frystyk 531: t += (up ? HTUserProfile_timezone(up) : HTGetTimeZoneOffset());
2.1 frystyk 532: #else
2.8 frystyk 533: #ifdef HAVE_TIMEGM
2.1 frystyk 534: t = timegm(&tm);
535: #else
2.8 frystyk 536: #error "Neither mktime nor timegm defined"
537: #endif /* HAVE_TIMEGM */
538: #endif /* HAVE_MKTIME */
2.1 frystyk 539:
2.34 frystyk 540: HTTRACE(CORE_TRACE, "Time string. %s parsed to %ld calendar time or `%s' in local time\n" _
541: str _ (long) t _ ctime(&t));
2.1 frystyk 542: return t;
543: }
544:
545: /*
546: ** Returns a string pointer to a static area of the current calendar
547: ** time in RFC 1123 format, for example
548: **
549: ** Sun, 06 Nov 1994 08:49:37 GMT
550: **
551: ** The result can be given in both local and GMT dependent on the flag
552: */
2.8 frystyk 553: PUBLIC const char *HTDateTimeStr (time_t * calendar, BOOL local)
2.1 frystyk 554: {
555: static char buf[40];
556:
2.8 frystyk 557: #ifdef HAVE_STRFTIME
2.1 frystyk 558: if (local) {
559: /*
560: ** Solaris 2.3 has a bug so we _must_ use reentrant version
561: ** Thomas Maslen <tmaslen@verity.com>
562: */
563: #if defined(HT_REENTRANT) || defined(SOLARIS)
564: struct tm loctime;
565: localtime_r(calendar, &loctime);
566: strftime(buf, 40, "%a, %d %b %Y %H:%M:%S", &loctime);
567: #else
568: struct tm *loctime = localtime(calendar);
569: strftime(buf, 40, "%a, %d %b %Y %H:%M:%S", loctime);
570: #endif /* SOLARIS || HT_REENTRANT */
571: } else {
572: #if defined(HT_REENTRANT) || defined(SOLARIS)
573: struct tm gmt;
574: gmtime_r(calendar, &gmt);
575: strftime(buf, 40, "%a, %d %b %Y %H:%M:%S GMT", &gmt);
576: #else
577: struct tm *gmt = gmtime(calendar);
578: strftime(buf, 40, "%a, %d %b %Y %H:%M:%S GMT", gmt);
579: #endif /* SOLARIS || HT_REENTRANT */
580: }
581: #else
582: if (local) {
583: #if defined(HT_REENTRANT)
584: struct tm loctime;
585: localtime_r(calendar, &loctime);
586: #else
587: struct tm *loctime = localtime(calendar);
588: #endif /* HT_REENTRANT */
2.24 frystyk 589: sprintf(buf,"%s, %02d %s %04d %02d:%02d:%02d",
2.12 frystyk 590: wkdays[loctime->tm_wday],
591: loctime->tm_mday,
592: months[loctime->tm_mon],
2.24 frystyk 593: loctime->tm_year + 1900,
2.12 frystyk 594: loctime->tm_hour,
595: loctime->tm_min,
596: loctime->tm_sec);
2.1 frystyk 597: } else {
598: #if defined(HT_REENTRANT) || defined(SOLARIS)
599: struct tm gmt;
600: gmtime_r(calendar, &gmt);
601: #else
602: struct tm *gmt = gmtime(calendar);
603: #endif
2.24 frystyk 604: sprintf(buf,"%s, %02d %s %04d %02d:%02d:%02d GMT",
2.1 frystyk 605: wkdays[gmt->tm_wday],
606: gmt->tm_mday,
607: months[gmt->tm_mon],
2.24 frystyk 608: gmt->tm_year + 1900,
2.1 frystyk 609: gmt->tm_hour,
610: gmt->tm_min,
611: gmt->tm_sec);
612: }
613: #endif
614: return buf;
615: }
616:
617: /* HTDateDirStr
618: ** ------------
619: ** Generates a date string used in directory listings
620: */
621: PUBLIC BOOL HTDateDirStr (time_t * time, char * str, int len)
622: {
2.8 frystyk 623: #ifdef HAVE_STRFTIME
2.1 frystyk 624: #if defined(HT_REENTRANT) || defined(SOLARIS)
625: struct tm loctime;
626: localtime_r(time, &loctime);
2.29 frystyk 627: strftime(str, len, "%d-%b-%Y %H:%M", &loctime);
2.1 frystyk 628: return YES;
629: #else
2.29 frystyk 630: strftime(str, len, "%d-%b-%Y %H:%M", localtime(time));
2.1 frystyk 631: return YES;
632: #endif /* HT_REENTRANT || SOLARIS */
633: #else
634: if (len >= 16) {
635: struct tm *loctime = localtime(time);
2.12 frystyk 636: sprintf(str,"%02d-%s-%02d %02d:%02d",
2.1 frystyk 637: loctime->tm_mday,
638: months[loctime->tm_mon],
639: loctime->tm_year % 100,
640: loctime->tm_hour,
641: loctime->tm_min);
642: return YES;
643: }
644: return NO;
2.8 frystyk 645: #endif /* HAVE_STRFTIME */
2.1 frystyk 646: }
647:
648: /* HTNumToStr
649: ** Converts a long (byte count) to a string
650: ** ----------------------------------------
651: ** This function was a PAIN! In computer-world 1K is 1024 bytes
652: ** and 1M is 1024K -- however, sprintf() still formats in base-10.
653: ** Therefore I output only until 999, and then start using the
654: ** next unit. This doesn't work wrong, it's just a feature.
655: ** The "str" must be large enough to contain the result.
656: */
657: PUBLIC void HTNumToStr (unsigned long n, char * str, int len)
658: {
659: double size = n/1024.0;
660: if (len < 6) {
661: *str = '\0';
662: return;
663: }
664: if (n < 1000)
665: sprintf(str, "%dK", n>0 ? 1 : 0);
666: else if (size + 0.999 < 1000)
667: sprintf(str, "%dK", (int)(size + 0.5));
668: else if ((size /= 1024) < 9.9)
669: sprintf(str, "%.1fM", (size + 0.05));
670: else if (size < 1000)
671: sprintf(str, "%dM", (int)(size + 0.5));
672: else if ((size /= 1024) < 9.9)
673: sprintf(str, "%.1fG", (size + 0.05));
674: else
675: sprintf(str, "%dG", (int)(size + 0.5));
2.12 frystyk 676: }
677:
678: /*
679: ** Matches MIME constructions for content-types and others like
680: ** them, for example "text/html", "text/plain". It can also match
681: ** wild cards like "text/<star>" and "<star>/<star>. We use <star>
682: ** instead of * in order note to make C like comments :-)
683: */
684: PUBLIC BOOL HTMIMEMatch (HTAtom * tmplate, HTAtom * actual)
685: {
686: const char *t, *a;
687: char *st, *sa;
688: BOOL match = NO;
689:
690: if (tmplate && actual && (t = HTAtom_name(tmplate))) {
691: if (!strcmp(t, "*"))
692: return YES;
693:
694: if (strchr(t, '*') &&
695: (a = HTAtom_name(actual)) &&
696: (st = strchr(t, '/')) && (sa = strchr(a,'/'))) {
697:
698: *sa = 0;
699: *st = 0;
700:
701: if ((*(st-1)=='*' &&
702: (*(st+1)=='*' || !strcasecomp(st+1, sa+1))) ||
703: (*(st+1)=='*' && !strcasecomp(t,a)))
704: match = YES;
705:
706: *sa = '/';
707: *st = '/';
708: }
709: }
710: return match;
2.1 frystyk 711: }
712:
713: /* Convert file URLs into a local representation
714: ** ---------------------------------------------
715: ** The URL has already been translated through the rules in get_physical
716: ** in HTAccess.c and all we need to do now is to map the path to a local
717: ** representation, for example if must translate '/' to the ones that
718: ** turn the wrong way ;-)
719: ** Returns:
720: ** OK: local file (that must be freed by caller)
721: ** Error: NULL
722: */
2.11 frystyk 723: PUBLIC char * HTWWWToLocal (const char * url, const char * base,
724: HTUserProfile * up)
2.1 frystyk 725: {
726: if (url) {
727: char * access = HTParse(url, base, PARSE_ACCESS);
728: char * host = HTParse(url, base, PARSE_HOST);
729: char * path = HTParse(url, base, PARSE_PATH+PARSE_PUNCTUATION);
2.11 frystyk 730: const char * myhost = HTUserProfile_fqdn(up);
2.1 frystyk 731:
732: /* Find out if this is a reference to the local file system */
2.16 frystyk 733: if ((*access && strcmp(access, "file") && strcmp(access, "cache")) ||
734: (*host && strcasecomp(host, "localhost") &&
735: myhost && strcmp(host, myhost))) {
2.34 frystyk 736: HTTRACE(CORE_TRACE, "LocalName... Not on local file system\n");
2.5 frystyk 737: HT_FREE(access);
738: HT_FREE(host);
739: HT_FREE(path);
2.1 frystyk 740: return NULL;
741: } else {
742: char *ptr;
743: if ((ptr = strchr(path, ';')) || (ptr = strchr(path, '?')))
744: *ptr = '\0';
745:
746: /*
747: ** Do whatever translation is required here in order to fit your
748: ** platform _before_ the path is unescaped.
749: */
750: #ifdef VMS
751: HTVMS_checkDecnet(path);
752: #endif
2.3 frystyk 753: #ifdef WWW_MSWINDOWS
2.27 frystyk 754: /* An absolute pathname with logical drive */
2.25 frystyk 755: if (*path == '/' && path[2] == ':') {
2.27 frystyk 756: char *orig=path, *dest=path+1;
2.25 frystyk 757: while((*orig++ = *dest++));
758:
759: /* A network host */
760: } else if (*host && strcasecomp(host, "localhost")) {
761: char * newpath = NULL;
762: StrAllocMCopy(&newpath, "//", host, path, NULL);
763: HT_FREE(path);
764: path = newpath;
2.27 frystyk 765: }
766:
767: /* Convert '/' to '\' */
768: {
769: char *p = path;
770: while (*p) {
771: if (*p=='/') *p='\\';
772: p++;
773: }
2.25 frystyk 774: }
2.1 frystyk 775: #endif
776:
777: HTUnEscape(path); /* Take out the escaped characters */
2.34 frystyk 778: HTTRACE(CORE_TRACE, "Node........ `%s' means path `%s'\n" _ url _ path);
2.5 frystyk 779: HT_FREE(access);
780: HT_FREE(host);
2.1 frystyk 781: return path;
782: }
783: }
784: return NULL;
785: }
786:
787: /* Convert a local file name into a URL
788: ** ------------------------------------
789: ** Generates a WWW URL name from a local file name or NULL if error.
790: ** Returns:
791: ** OK: local file (that must be freed by caller)
792: ** Error: NULL
793: */
2.32 frystyk 794: PUBLIC char * HTLocalToWWW (const char * local, const char * access)
2.1 frystyk 795: {
2.25 frystyk 796: char * escaped = NULL;
2.32 frystyk 797: const char * scheme = (access && *access) ? access : "file:";
2.1 frystyk 798: if (local && *local) {
2.26 frystyk 799: #ifdef VMS
2.25 frystyk 800: char * unescaped = NULL;
801: if ((unescaped = (char *) HT_MALLOC(strlen(local) + 10)) == NULL)
802: HT_OUTOFMEM("HTLocalToWWW");
2.32 frystyk 803: strcpy(unescaped, scheme); /* We get an absolute file name */
2.26 frystyk 804:
2.1 frystyk 805: /* convert directory name to Unix-style syntax */
806: {
807: char * disk = strchr (local, ':');
808: char * dir = strchr (local, '[');
809: if (disk) {
810: *disk = '\0';
2.25 frystyk 811: strcat(unescaped, "/");
812: strcat(unescaped, local);
2.1 frystyk 813: }
814: if (dir) {
815: char *p;
816: *dir = '/'; /* Convert leading '[' */
817: for (p = dir ; *p != ']'; ++p)
818: if (*p == '.') *p = '/';
819: *p = '\0'; /* Cut on final ']' */
2.25 frystyk 820: strcat(unescaped, dir);
2.1 frystyk 821: }
822: }
2.26 frystyk 823: escaped = HTEscape(unescaped, URL_DOSFILE);
2.25 frystyk 824: HT_FREE(unescaped);
825:
2.1 frystyk 826: #else /* not VMS */
827: #ifdef WIN32
2.26 frystyk 828: char * unescaped = NULL;
829: if ((unescaped = (char *) HT_MALLOC(strlen(local) + 10)) == NULL)
830: HT_OUTOFMEM("HTLocalToWWW");
2.32 frystyk 831: strcpy(unescaped, scheme); /* We get an absolute file name */
2.25 frystyk 832: if (strchr(local, ':')) strcat(unescaped, "/");
833: {
834: const char *p = local;
835: char *q = unescaped+strlen(unescaped);
836: while (*p) {
837: if (*p=='\\') {
838: *q++='/';
839: } else
840: *q++=*p;
841: p++;
842: }
843: *q = '\0';
844: }
845: escaped = HTEscape(unescaped, URL_DOSFILE);
846: HT_FREE(unescaped);
847:
848: #else /* Unix */
2.26 frystyk 849: char * escaped_path = HTEscape(local, URL_PATH);
2.32 frystyk 850: escaped = StrAllocMCopy(&escaped, scheme, escaped_path, NULL);
2.26 frystyk 851: HT_FREE(escaped_path);
2.25 frystyk 852:
2.1 frystyk 853: #endif /* not WIN32 */
854: #endif /* not VMS */
855: }
2.25 frystyk 856: return escaped;
2.1 frystyk 857: }
Webmaster