summary refs log tree commit diff stats
path: root/tests/stdlib/tunicode.nim
blob: 33405cf38535218eb0c5c571c1e14471cc057b03 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
import std/unicode


proc asRune(s: static[string]): Rune =
  ## Compile-time conversion proc for converting string literals to a Rune
  ## value. Returns the first Rune of the specified string.
  ##
  ## Shortcuts code like ``"å".runeAt(0)`` to ``"å".asRune`` and returns a
  ## compile-time constant.
  if s.len == 0: Rune(0)
  else: s.runeAt(0)

let
  someString = "öÑ"
  someRunes = toRunes(someString)
  compared = (someString == $someRunes)
doAssert compared == true

proc testReplacements(word: string): string =
  case word
  of "two":
    return "2"
  of "foo":
    return "BAR"
  of "βeta":
    return "beta"
  of "alpha":
    return "αlpha"
  else:
    return "12345"

doAssert translate("two not alpha foo βeta", testReplacements) == "2 12345 αlpha BAR beta"
doAssert translate("  two not foo βeta  ", testReplacements) == "  2 12345 BAR beta  "

doAssert title("foo bar") == "Foo Bar"
doAssert title("αlpha βeta γamma") == "Αlpha Βeta Γamma"
doAssert title("") == ""

doAssert capitalize("βeta") == "Βeta"
doAssert capitalize("foo") == "Foo"
doAssert capitalize("") == ""

doAssert swapCase("FooBar") == "fOObAR"
doAssert swapCase(" ") == " "
doAssert swapCase("Αlpha Βeta Γamma") == "αLPHA βETA γAMMA"
doAssert swapCase("a✓B") == "A✓b"
doAssert swapCase("Јамогујестистаклоитоминештети") == "јАМОГУЈЕСТИСТАКЛОИТОМИНЕШТЕТИ"
doAssert swapCase("ὕαλονϕαγεῖνδύναμαιτοῦτοοὔμεβλάπτει") == "ὝΑΛΟΝΦΑΓΕῖΝΔΎΝΑΜΑΙΤΟῦΤΟΟὔΜΕΒΛΆΠΤΕΙ"
doAssert swapCase("Կրնամապակիուտեևինծիանհանգիստչըներ") == "կՐՆԱՄԱՊԱԿԻՈՒՏԵևԻՆԾԻԱՆՀԱՆԳԻՍՏՉԸՆԵՐ"
doAssert swapCase("") == ""

doAssert isAlpha("r")
doAssert isAlpha("α")
doAssert isAlpha("ϙ")
doAssert isAlpha("ஶ")
doAssert(not isAlpha("$"))
doAssert(not isAlpha(""))

doAssert isAlpha("Βeta")
doAssert isAlpha("Args")
doAssert isAlpha("𐌼𐌰𐌲𐌲𐌻𐌴𐍃𐍄𐌰𐌽")
doAssert isAlpha("ὕαλονϕαγεῖνδύναμαιτοῦτοοὔμεβλάπτει")
doAssert isAlpha("Јамогујестистаклоитоминештети")
doAssert isAlpha("Կրնամապակիուտեևինծիանհանգիստչըներ")
doAssert(not isAlpha("$Foo✓"))
doAssert(not isAlpha("⠙⠕⠑⠎⠝⠞"))

doAssert isSpace("\t")
doAssert isSpace("\l")
doAssert(not isSpace("Β"))
doAssert(not isSpace("Βeta"))

doAssert isSpace("\t\l \v\r\f")
doAssert isSpace("       ")
doAssert(not isSpace(""))
doAssert(not isSpace("ΑΓc   \td"))

doAssert(not isLower(' '.Rune))

doAssert(not isUpper(' '.Rune))

doAssert toUpper("Γ") == "Γ"
doAssert toUpper("b") == "B"
doAssert toUpper("α") == "Α"
doAssert toUpper("✓") == "✓"
doAssert toUpper("ϙ") == "Ϙ"
doAssert toUpper("") == ""

doAssert toUpper("ΑΒΓ") == "ΑΒΓ"
doAssert toUpper("AAccβ") == "AACCΒ"
doAssert toUpper("A✓") == "A✓"

doAssert toLower("a") == "a"
doAssert toLower("γ") == "γ"
doAssert toLower("Γ") == "γ"
doAssert toLower("4") == "4"
doAssert toLower("Ϙ") == "ϙ"
doAssert toLower("") == ""

doAssert toLower("abcdγ") == "abcdγ"
doAssert toLower("abCDΓ") == "abcdγ"
doAssert toLower("33aaΓ") == "33aaγ"

doAssert reversed("Reverse this!") == "!siht esreveR"
doAssert reversed("先秦兩漢") == "漢兩秦先"
doAssert reversed("as⃝df̅") == "f̅ds⃝a"
doAssert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞"
doAssert reversed("ὕαλονϕαγεῖνδύναμαιτοῦτοοὔμεβλάπτει") == "ιετπάλβεμὔοοτῦοτιαμανύδνῖεγαϕνολαὕ"
doAssert reversed("Јамогујестистаклоитоминештети") == "итетшенимотиолкатситсејугомаЈ"
doAssert reversed("Կրնամապակիուտեևինծիանհանգիստչըներ") == "րենըչտսիգնահնաիծնիևետւոիկապամանրԿ"
doAssert len(toRunes("as⃝df̅")) == runeLen("as⃝df̅")
const test = "as⃝"
doAssert lastRune(test, test.len-1)[1] == 3
doAssert graphemeLen("è", 0) == 2

# test for rune positioning and runeSubStr()
let s = "Hänsel  ««: 10,00€"

var t = ""
for c in s.utf8:
  t.add c

doAssert(s == t)

doAssert(runeReverseOffset(s, 1) == (20, 18))
doAssert(runeReverseOffset(s, 19) == (-1, 18))

doAssert(runeStrAtPos(s, 0) == "H")
doAssert(runeSubStr(s, 0, 1) == "H")
doAssert(runeStrAtPos(s, 10) == ":")
doAssert(runeSubStr(s, 10, 1) == ":")
doAssert(runeStrAtPos(s, 9) == "«")
doAssert(runeSubStr(s, 9, 1) == "«")
doAssert(runeStrAtPos(s, 17) == "€")
doAssert(runeSubStr(s, 17, 1) == "€")
# echo runeStrAtPos(s, 18) # index error

doAssert(runeSubStr(s, 0) == "Hänsel  ««: 10,00€")
doAssert(runeSubStr(s, -18) == "Hänsel  ««: 10,00€")
doAssert(runeSubStr(s, 10) == ": 10,00€")
doAssert(runeSubStr(s, 18) == "")
doAssert(runeSubStr(s, 0, 10) == "Hänsel  ««")

doAssert(runeSubStr(s, 12) == "10,00€")
doAssert(runeSubStr(s, -6) == "10,00€")

doAssert(runeSubStr(s, 12, 5) == "10,00")
doAssert(runeSubStr(s, 12, -1) == "10,00")
doAssert(runeSubStr(s, -6, 5) == "10,00")
doAssert(runeSubStr(s, -6, -1) == "10,00")

doAssert(runeSubStr(s, 0, 100) == "Hänsel  ««: 10,00€")
doAssert(runeSubStr(s, -100, 100) == "Hänsel  ««: 10,00€")
doAssert(runeSubStr(s, 0, -100) == "")
doAssert(runeSubStr(s, 100, -100) == "")

block splitTests:
  let s = " this is an example  "
  let s2 = ":this;is;an:example;;"
  let s3 = ":this×is×an:example××"
  doAssert s.split() == @["", "this", "is", "an", "example", "", ""]
  doAssert s2.split(seps = [':'.Rune, ';'.Rune]) == @["", "this", "is", "an",
      "example", "", ""]
  doAssert s3.split(seps = [':'.Rune, "×".asRune]) == @["", "this", "is",
      "an", "example", "", ""]
  doAssert s.split(maxsplit = 4) == @["", "this", "is", "an", "example  "]
  doAssert s.split(' '.Rune, maxsplit = 1) == @["", "this is an example  "]
  doAssert s3.split("×".runeAt(0)) == @[":this", "is", "an:example", "", ""]

block stripTests:
  doAssert(strip("") == "")
  doAssert(strip(" ") == "")
  doAssert(strip("y") == "y")
  doAssert(strip("  foofoofoo  ") == "foofoofoo")
  doAssert(strip("sfoofoofoos", runes = ['s'.Rune]) == "foofoofoo")

  block:
    let stripTestRunes = ['b'.Rune, 'a'.Rune, 'r'.Rune]
    doAssert(strip("barfoofoofoobar", runes = stripTestRunes) == "foofoofoo")
  doAssert(strip("sfoofoofoos", leading = false, runes = ['s'.Rune]) == "sfoofoofoo")
  doAssert(strip("sfoofoofoos", trailing = false, runes = ['s'.Rune]) == "foofoofoos")

  block:
    let stripTestRunes = ["«".asRune, "»".asRune]
    doAssert(strip("«TEXT»", runes = stripTestRunes) == "TEXT")
  doAssert(strip("copyright©", leading = false, runes = ["©".asRune]) == "copyright")
  doAssert(strip("¿Question?", trailing = false, runes = ["¿".asRune]) == "Question?")
  doAssert(strip("×text×", leading = false, runes = ["×".asRune]) == "×text")
  doAssert(strip("×text×", trailing = false, runes = ["×".asRune]) == "text×")

block repeatTests:
  doAssert repeat('c'.Rune, 5) == "ccccc"
  doAssert repeat("×".asRune, 5) == "×××××"

block alignTests:
  doAssert align("abc", 4) == " abc"
  doAssert align("a", 0) == "a"
  doAssert align("1232", 6) == "  1232"
  doAssert align("1232", 6, '#'.Rune) == "##1232"
  doAssert align("1232", 6, "×".asRune) == "××1232"
  doAssert alignLeft("abc", 4) == "abc "
  doAssert alignLeft("a", 0) == "a"
  doAssert alignLeft("1232", 6) == "1232  "
  doAssert alignLeft("1232", 6, '#'.Rune) == "1232##"
  doAssert alignLeft("1232", 6, "×".asRune) == "1232××"

block differentSizes:
  # upper and lower variants have different number of bytes
  doAssert toLower("AẞC") == "aßc"
  doAssert toLower("ȺẞCD") == "ⱥßcd"
  doAssert toUpper("ⱥbc") == "ȺBC"
  doAssert toUpper("rsⱦuv") == "RSȾUV"
  doAssert swapCase("ⱥbCd") == "ȺBcD"
  doAssert swapCase("XyꟆaB") == "xYᶎAb"
  doAssert swapCase("aᵹcᲈd") == "AꝽCꙊD"

block: # bug #17768
  let s1 = "abcdef"
  let s2 = "abcdéf"

  doAssert s1.runeSubstr(0, -1) == "abcde"
  doAssert s2.runeSubstr(0, -1) == "abcdé"
an>!was_units || was_units != u) sprintf(s + strlen(s), " %s", u); return u; } #endif /* EXP_READPROGRESS */ /* Issue a read-progress message. HTReadProgress() ** ------------------------------ */ PUBLIC void HTReadProgress ARGS2( long, bytes, long, total) { #ifdef WIN_EX /* 1998/07/08 (Wed) 16:09:47 */ #include <sys/timeb.h> #define kb_units 1024L static double now, first, last; static long bytes_last; double transfer_rate; char line[MAX_LINE]; struct timeb tb; char *units = "bytes"; ftime(&tb); now = tb.time + (double)tb.millitm / 1000; if (bytes == 0) { first = last = now; bytes_last = bytes; } else if ((bytes > 0) && (now > first)) { transfer_rate = (double)bytes / (now - first); /* bytes/sec */ if (now != last) { last = now; bytes_last = bytes; } if (LYshow_kb_rate && (total >= kb_units || bytes >= kb_units)) { if (total > 0) total /= 1024; bytes /= 1024; units = "KB"; } if (total > 0) sprintf (line, "Read %3d%%, %ld of %ld %s.", (int) (bytes * 100 / total), bytes, total, units); else sprintf (line, "Read %ld %s of data.", bytes, units); if (transfer_rate > 0.0) { int n; n = strlen(line); if (LYshow_kb_rate) { sprintf (line + n, " %6.2lf KB/sec.", transfer_rate / 1024.0); } else { int t_rate; t_rate = (int)transfer_rate; if (t_rate < 1000) sprintf (line + n, " %6d bytes/sec.", t_rate); else sprintf (line + n, " %6d,%03d bytes/sec.", t_rate / 1000, t_rate % 1000); } } if (total < 0) { if (total < -1) strcat(line, " (Press 'z' to abort)"); } statusline(line); } #else /* !WIN_EX */ #ifdef EXP_READPROGRESS static long bytes_last, total_last; static long transfer_rate = 0; char line[300], bytesp[80], totalp[80], transferp[80]; int renew = 0; char *was_units; #if HAVE_GETTIMEOFDAY struct timeval tv; int dummy = gettimeofday(&tv, (struct timezone *)0); double now = tv.tv_sec + tv.tv_usec/1000000. ; static double first, last, last_active; #else time_t now = time((time_t *)0); /* once per second */ static time_t first, last, last_active; #endif if (bytes == 0) { first = last = last_active = now; bytes_last = bytes; line[0] = 0; } else if (bytes < 0) { /* stalled */ bytes = bytes_last; total = total_last; } if ((bytes > 0) && (now != first)) /* 1 sec delay for transfer_rate calculation without g-t-o-d */ { if (transfer_rate <= 0) /* the very first time */ transfer_rate = (bytes) / (now - first); /* bytes/sec */ total_last = total; /* * Optimal refresh time: every 0.2 sec, use interpolation. Transfer * rate is not constant when we have partial content in a proxy, so * interpolation lies - will check every second at least for sure. */ #if HAVE_GETTIMEOFDAY if (now >= last + 0.2) renew = 1; #else if (((bytes - bytes_last) > (transfer_rate / 5)) || (now != last)) { renew = 1; bytes_last += (transfer_rate / 5); /* until we got next second */ } #endif if (renew) { if (now != last) { last = now; if (bytes_last != bytes) last_active = now; bytes_last = bytes; transfer_rate = bytes / (now - first); /* more accurate here */ } if (total > 0) was_units = sprint_bytes(totalp, total, 0); else was_units = 0; sprint_bytes(bytesp, bytes, was_units); sprint_bytes(transferp, transfer_rate, 0); if (total > 0) sprintf (line, gettext("Read %s of %s of data"), bytesp, totalp); else sprintf (line, gettext("Read %s of data"), bytesp); if (transfer_rate > 0) sprintf (line + strlen(line), gettext(", %s/sec"), transferp); if (now - last_active >= 5) sprintf (line + strlen(line), gettext(" (stalled for %ld sec)"), (long)(now - last_active)); if (total > 0 && transfer_rate) sprintf (line + strlen(line), gettext(", ETA %ld sec"), (long)((total - bytes)/transfer_rate)); sprintf (line + strlen(line), "."); if (total < -1) strcat(line, gettext(" (Press 'z' to abort)")); /* do not store the message for history page. */ statusline(line); CTRACE((tfp, "%s\n", line)); } } #else /* !EXP_READPROGRESS */ static long kb_units = 1024; static time_t first, last; static long bytes_last; static long transfer_rate = 0; long divisor; char line[80]; time_t now = time((time_t *)0); /* once per second */ static char *units = "bytes"; if (bytes == 0) { first = last = now; bytes_last = bytes; } else if ((bytes > 0) && (now != first)) /* 1 sec delay for transfer_rate calculation :-( */ { if (transfer_rate <= 0) /* the very first time */ transfer_rate = (bytes) / (now - first); /* bytes/sec */ /* * Optimal refresh time: every 0.2 sec, use interpolation. Transfer * rate is not constant when we have partial content in a proxy, so * interpolation lies - will check every second at least for sure. */ if (((bytes - bytes_last) > (transfer_rate / 5)) || (now != last)) { bytes_last += (transfer_rate / 5); /* until we got next second */ if (now != last) { last = now; bytes_last = bytes; transfer_rate = (bytes_last) / (last - first); /* more accurate here */ } units = gettext("bytes"); divisor = 1; if (LYshow_kb_rate && (total >= kb_units || bytes >= kb_units)) { units = gettext("KB"); divisor = kb_units; bytes /= divisor; if (total > 0) total /= divisor; } if (total > 0) sprintf (line, gettext("Read %ld of %ld %s of data"), bytes, total, units); else sprintf (line, gettext("Read %ld %s of data"), bytes, units); if ((transfer_rate > 0) && (!LYshow_kb_rate || (bytes * divisor >= kb_units))) sprintf (line + strlen(line), gettext(", %ld %s/sec."), transfer_rate / divisor, units); else sprintf (line + strlen(line), "."); if (total < 0) { if (total < -1) strcat(line, gettext(" (Press 'z' to abort)")); } /* do not store the message for history page. */ statusline(line); CTRACE((tfp, "%s\n", line)); } } #endif /* EXP_READPROGRESS */ #endif /* WIN_EX */ } PRIVATE BOOL conf_cancelled = NO; /* used by HTConfirm only - kw */ PUBLIC BOOL HTLastConfirmCancelled NOARGS { if (conf_cancelled) { conf_cancelled = NO; /* reset */ return(YES); } else { return(NO); } } #define DFT_CONFIRM ~(YES|NO) /* Seek confirmation with default answer. HTConfirmDefault() ** -------------------------------------- */ PUBLIC int HTConfirmDefault ARGS2(CONST char *, Msg, int, Dft) { /* Meta-note: don't move the following note from its place right in front of the first gettext(). As it is now, it should automatically appear in generated lynx.pot files. - kw */ /* NOTE TO TRANSLATORS: If you provide a translation for "yes", lynx * will take the first byte of the translation as a positive response * to Yes/No questions. If you provide a translation for "no", lynx * will take the first byte of the translation as a negative response * to Yes/No questions. For both, lynx will also try to show the * first byte in the prompt as a character, instead of (y) or (n), * respectively. This will not work right for multibyte charsets! * Don't translate "yes" and "no" for CJK character sets (or translate * them to "yes" and "no"). For a translation using UTF-8, don't * translate if the translation would begin with anything but a 7-bit * (US_ASCII) character. That also means do not translate if the * translation would begin with anything but a 7-bit character, if * you use a single-byte character encoding (a charset like ISO-8859-n) * but anticipate that the message catalog may be used re-encoded in * UTF-8 form. * For translations using other character sets, you may also wish to * leave "yes" and "no" untranslated, if using (y) and (n) is the * preferred behavior. * Lynx will also accept y Y n N as responses unless there is a conflict * with the first letter of the "yes" or "no" translation. */ char *msg_yes = gettext("yes"); char *msg_no = gettext("no"); int result = -1; /* If they're not really distinct in the first letter, revert to English */ if (TOUPPER(*msg_yes) == TOUPPER(*msg_no)) { msg_yes = "yes"; msg_no = "no"; } conf_cancelled = NO; if (dump_output_immediately) { /* Non-interactive, can't respond */ if (Dft == DFT_CONFIRM) { CTRACE((tfp, "Confirm: %s (%c/%c) ", Msg, *msg_yes, *msg_no)); } else { CTRACE((tfp, "Confirm: %s (%c) ", Msg, (Dft == YES) ? *msg_yes : *msg_no)); } CTRACE((tfp, "- NO, not interactive.\n")); result = NO; } else { char *msg = NULL; char fallback_y = 'y'; /* English letter response as fallback */ char fallback_n = 'n'; /* English letter response as fallback */ if (fallback_y == *msg_yes || fallback_y == *msg_no) fallback_y = '\0'; /* conflict or duplication, don't use */ if (fallback_n == *msg_yes || fallback_n == *msg_no) fallback_n = '\0'; /* conflict or duplication, don't use */ if (Dft == DFT_CONFIRM) HTSprintf0(&msg, "%s (%c/%c) ", Msg, *msg_yes, *msg_no); else HTSprintf0(&msg, "%s (%c) ", Msg, (Dft == YES) ? *msg_yes : *msg_no); if (LYTraceLogFP) { CTRACE((tfp, "Confirm: %s", msg)); } _statusline(msg); FREE(msg); while (result < 0) { int c = LYgetch_for(FOR_SINGLEKEY); #ifdef VMS if (HadVMSInterrupt) { HadVMSInterrupt = FALSE; c = *msg_no; } #endif /* VMS */ if (c == 7 || c == 3) { /* remember we had ^G or ^C */ conf_cancelled = YES; result = NO; } else if (TOUPPER(c) == TOUPPER(*msg_yes)) { result = YES; } else if (TOUPPER(c) == TOUPPER(*msg_no)) { result = NO; } else if (fallback_y && TOLOWER(c) == fallback_y) { result = YES; } else if (fallback_n && TOLOWER(c) == fallback_n) { result = NO; } else if (Dft != DFT_CONFIRM) { result = Dft; break; } } CTRACE((tfp, "- %s%s.\n", (result != NO) ? "YES" : "NO", conf_cancelled ? ", cancelled" : "")); } return (result); } /* Seek confirmation. HTConfirm() ** ------------------ */ PUBLIC BOOL HTConfirm ARGS1(CONST char *, Msg) { return (BOOL) HTConfirmDefault(Msg, DFT_CONFIRM); } /* * Ask a post resubmission prompt with some indication of what would * be resubmitted, useful especially for going backward in history. * Try to use parts of the address or, if given, the title, depending * on how much fits on the statusline. * if_imgmap and if_file indicate how to handle an address that is * a "LYNXIMGMAP:", or a "file:" URL (presumably the List Page file), * respectively: 0: auto-deny, 1: auto-confirm, 2: prompt. * - kw */ PUBLIC BOOL confirm_post_resub ARGS4( CONST char*, address, CONST char*, title, int, if_imgmap, int, if_file) { size_t len1; CONST char *msg = CONFIRM_POST_RESUBMISSION_TO; char buf[240]; char *temp = NULL; BOOL res; size_t maxlen = LYcols - 6; if (!address) { return(NO); } else if (!strncmp(address, "LYNXIMGMAP:", 11)) { if (if_imgmap <= 0) return(NO); else if (if_imgmap == 1) return(YES); else msg = CONFIRM_POST_LIST_RELOAD; } else if (!strncmp(address, "file:", 5)) { if (if_file <= 0) return(NO); else if (if_file == 1) return(YES); else msg = CONFIRM_POST_LIST_RELOAD; } else if (dump_output_immediately) { return(NO); } if (maxlen >= sizeof(buf)) maxlen = sizeof(buf) - 1; if ((len1 = strlen(msg)) + strlen(address) <= maxlen) { sprintf(buf, msg, address); return HTConfirm(buf); } if (len1 + strlen(temp = HTParse(address, "", PARSE_ACCESS+PARSE_HOST+PARSE_PATH +PARSE_PUNCTUATION)) <= maxlen) { sprintf(buf, msg, temp); res = HTConfirm(buf); FREE(temp); return(res); } FREE(temp); if (title && (len1 + strlen(title) <= maxlen)) { sprintf(buf, msg, title); return HTConfirm(buf); } if (len1 + strlen(temp = HTParse(address, "", PARSE_ACCESS+PARSE_HOST +PARSE_PUNCTUATION)) <= maxlen) { sprintf(buf, msg, temp); res = HTConfirm(buf); FREE(temp); return(res); } FREE(temp); if ((temp = HTParse(address, "", PARSE_HOST)) && *temp && len1 + strlen(temp) <= maxlen) { sprintf(buf, msg, temp); res = HTConfirm(buf); FREE(temp); return(res); } FREE(temp); return HTConfirm(CONFIRM_POST_RESUBMISSION); } /* Prompt for answer and get text back. HTPrompt() ** ------------------------------------ */ PUBLIC char * HTPrompt ARGS2( CONST char *, Msg, CONST char *, deflt) { char * rep = NULL; char Tmp[200]; Tmp[0] = '\0'; Tmp[sizeof(Tmp)-1] = '\0'; _statusline(Msg); if (deflt) strncpy(Tmp, deflt, sizeof(Tmp)-1); if (!dump_output_immediately) LYgetstr(Tmp, VISIBLE, sizeof(Tmp), NORECALL); StrAllocCopy(rep, Tmp); return rep; } /* ** Prompt for password without echoing the reply. HTPromptPassword() ** ---------------------------------------------- */ PUBLIC char * HTPromptPassword ARGS1( CONST char *, Msg) { char *result = NULL; char pw[120]; pw[0] = '\0'; if (!dump_output_immediately) { _statusline(Msg ? Msg : PASSWORD_PROMPT); LYgetstr(pw, HIDDEN, sizeof(pw), NORECALL); /* hidden */ StrAllocCopy(result, pw); } else { printf("\n%s\n", PASSWORD_REQUIRED); StrAllocCopy(result, ""); } return result; } /* Prompt both username and password. HTPromptUsernameAndPassword() ** ---------------------------------- ** ** On entry, ** Msg is the prompting message. ** *username and ** *password are char pointers which contain default ** or zero-length strings; they are changed ** to point to result strings. ** IsProxy should be TRUE if this is for ** proxy authentication. ** ** If *username is not NULL, it is taken ** to point to a default value. ** Initial value of *password is ** completely discarded. ** ** On exit, ** *username and *password point to newly allocated ** strings -- original strings pointed to by them ** are NOT freed. ** */ PUBLIC void HTPromptUsernameAndPassword ARGS4( CONST char *, Msg, char **, username, char **, password, BOOL, IsProxy) { if ((IsProxy == FALSE && authentication_info[0] && authentication_info[1]) || (IsProxy == TRUE && proxyauth_info[0] && proxyauth_info[1])) { /* ** The -auth or -pauth parameter gave us both the username ** and password to use for the first realm or proxy server, ** respectively, so just use them without any prompting. - FM */ StrAllocCopy(*username, (IsProxy ? proxyauth_info[0] : authentication_info[0])); if (IsProxy) { FREE(proxyauth_info[0]); } else { FREE(authentication_info[0]); } StrAllocCopy(*password, (IsProxy ? proxyauth_info[1] : authentication_info[1])); if (IsProxy) { FREE(proxyauth_info[1]); } else { FREE(authentication_info[1]); } } else if (dump_output_immediately) { /* * We are not interactive and don't have both the * username and password from the command line, * but might have one or the other. - FM */ if ((IsProxy == FALSE && authentication_info[0]) || (IsProxy == TRUE && proxyauth_info[0])) { /* ** Use the command line username. - FM */ StrAllocCopy(*username, (IsProxy ? proxyauth_info[0] : authentication_info[0])); if (IsProxy) { FREE(proxyauth_info[0]); } else { FREE(authentication_info[0]); } } else { /* ** Default to "WWWuser". - FM */ StrAllocCopy(*username, "WWWuser"); } if ((IsProxy == FALSE && authentication_info[1]) || (IsProxy == TRUE && proxyauth_info[1])) { /* ** Use the command line password. - FM */ StrAllocCopy(*password, (IsProxy ? proxyauth_info[1] : authentication_info[1])); if (IsProxy) { FREE(proxyauth_info[1]); } else { FREE(authentication_info[1]); } } else { /* ** Default to a zero-length string. - FM */ StrAllocCopy(*password, ""); } printf("\n%s\n", USERNAME_PASSWORD_REQUIRED); } else { /* * We are interactive and don't have both the * username and password from the command line, * but might have one or the other. - FM */ if ((IsProxy == FALSE && authentication_info[0]) || (IsProxy == TRUE && proxyauth_info[0])) { /* ** Offer the command line username in the ** prompt for the first realm. - FM */ StrAllocCopy(*username, (IsProxy ? proxyauth_info[0] : authentication_info[0])); if (IsProxy) { FREE(proxyauth_info[0]); } else { FREE(authentication_info[0]); } } /* * Prompt for confirmation or entry of the username. - FM */ if (Msg != NULL) { *username = HTPrompt(Msg, *username); } else { *username = HTPrompt(USERNAME_PROMPT, *username); } if ((IsProxy == FALSE && authentication_info[1]) || (IsProxy == TRUE && proxyauth_info[1])) { /* ** Use the command line password for the first realm. - FM */ StrAllocCopy(*password, (IsProxy ? proxyauth_info[1] : authentication_info[1])); if (IsProxy) { FREE(proxyauth_info[1]); } else { FREE(authentication_info[1]); } } else if (*username != NULL && *username[0] != '\0') { /* ** We have a non-zero length username, ** so prompt for the password. - FM */ *password = HTPromptPassword(PASSWORD_PROMPT); } else { /* ** Return a zero-length password. - FM */ StrAllocCopy(*password, ""); } } } /* Confirm a cookie operation. HTConfirmCookie() ** --------------------------- ** ** On entry, ** server is the server sending the Set-Cookie. ** domain is the domain of the cookie. ** path is the path of the cookie. ** name is the name of the cookie. ** value is the value of the cookie. ** ** On exit, ** Returns FALSE on cancel, ** TRUE if the cookie should be set. */ PUBLIC BOOL HTConfirmCookie ARGS4( void *, dp, CONST char *, server, CONST char *, name, CONST char *, value) { domain_entry *de; int ch; if ((de = (domain_entry *)dp) == NULL) return FALSE; /* If the user has specified a list of domains to allow or deny ** from the config file, then they'll already have de->bv set to ** ACCEPT_ALWAYS or REJECT_ALWAYS so we can relax and let the ** default cookie handling code cope with this fine. */ /* ** If the user has specified a constant action, don't prompt at all. */ if (de->bv == ACCEPT_ALWAYS) return TRUE; if (de->bv == REJECT_ALWAYS) return FALSE; if (dump_output_immediately) { /* ** Non-interactive, can't respond. Use the LYSetCookies value * based on its compilation or configuration setting, or on the ** command line toggle. - FM */ return LYSetCookies; } /* ** Estimate how much of the cookie we can show. */ if(!LYAcceptAllCookies) { int namelen, valuelen, space_free, percentage; char *message = 0; space_free = ((LYcols - 1) - (strlen(ADVANCED_COOKIE_CONFIRMATION) - 10) /* %s and %.*s and %.*s chars */ - strlen(server)); if (space_free < 0) space_free = 0; namelen = strlen(name); valuelen = strlen(value); if ((namelen + valuelen) > space_free) { /* ** Argh... there isn't enough space on our single line for ** the whole cookie. Reduce them both by a percentage. ** This should be smarter. */ percentage = (100 * space_free) / (namelen + valuelen); namelen = (percentage * namelen) / 100; valuelen = (percentage * valuelen) / 100; } HTSprintf(&message, ADVANCED_COOKIE_CONFIRMATION, server, namelen, name, valuelen, value); _statusline(message); FREE(message); } while (1) { if(!LYAcceptAllCookies) { ch = LYgetch_for(FOR_SINGLEKEY); } else { ch = 'A'; } #ifdef VMS if (HadVMSInterrupt) { HadVMSInterrupt = FALSE; ch = 'N'; } #endif /* VMS */ switch(TOUPPER(ch)) { case 'A': /* ** Set to accept all cookies for this domain. */ de->bv = ACCEPT_ALWAYS; HTUserMsg2(ALWAYS_ALLOWING_COOKIES, de->domain); return TRUE; case 'N': case 7: /* Ctrl-G */ case 3: /* Ctrl-C */ /* ** Reject the cookie. */ HTUserMsg(REJECTING_COOKIE); return FALSE; case 'V': /* ** Set to reject all cookies from this domain. */ de->bv = REJECT_ALWAYS; HTUserMsg2(NEVER_ALLOWING_COOKIES, de->domain); return FALSE; case 'Y': /* ** Accept the cookie. */ HTInfoMsg(ALLOWING_COOKIE); return TRUE; default: continue; } } } /* Confirm redirection of POST. HTConfirmPostRedirect() ** ---------------------------- ** ** On entry, ** Redirecting_url is the Location. ** server_status is the server status code. ** ** On exit, ** Returns 0 on cancel, ** 1 for redirect of POST with content, ** 303 for redirect as GET without content */ PUBLIC int HTConfirmPostRedirect ARGS2( CONST char *, Redirecting_url, int, server_status) { int result = -1; char *show_POST_url = NULL; char *StatusInfo = 0; char *url = 0; int on_screen = 0; /* 0 - show menu * 1 - show url * 2 - menu is already on screen */ if (server_status == 303 || server_status == 302) { /* * HTTP.c should not have called us for either of * these because we're treating 302 as historical, * so just return 303. - FM */ return 303; } if (dump_output_immediately) { if (server_status == 301) { /* ** Treat 301 as historical, i.e., like 303 (GET ** without content), when not interactive. - FM */ return 303; } else { /* ** Treat anything else (e.g., 305, 306 or 307) as too ** dangerous to redirect without confirmation, and thus ** cancel when not interactive. - FM */ return 0; } } if (user_mode == NOVICE_MODE) { on_screen = 2; move(LYlines-2, 0); HTSprintf0(&StatusInfo, SERVER_ASKED_FOR_REDIRECTION, server_status); addstr(StatusInfo); clrtoeol(); move(LYlines-1, 0); HTSprintf0(&url, "URL: %.*s", (LYcols < 250 ? LYcols-6 : 250), Redirecting_url); addstr(url); clrtoeol(); if (server_status == 301) { _statusline(PROCEED_GET_CANCEL); } else { _statusline(PROCEED_OR_CANCEL); } } else { HTSprintf0(&StatusInfo, "%d %.*s", server_status, 251, ((server_status == 301) ? ADVANCED_POST_GET_REDIRECT : ADVANCED_POST_REDIRECT)); StrAllocCopy(show_POST_url, LOCATION_HEADER); StrAllocCat(show_POST_url, Redirecting_url); } while (result < 0) { int c; switch (on_screen) { case 0: _statusline(StatusInfo); break; case 1: _statusline(show_POST_url); } c = LYgetch_for(FOR_SINGLEKEY); switch (TOUPPER(c)) { case 'P': /* ** Proceed with 301 or 307 redirect of POST ** with same method and POST content. - FM */ FREE(show_POST_url); result = 1; break; case 7: case 'C': /* ** Cancel request. */ FREE(show_POST_url); result = 0; break; case 'U': /* ** Show URL for intermediate or advanced mode. */ if (user_mode != NOVICE_MODE) { if (on_screen == 1) { on_screen = 0; } else { on_screen = 1; } } break; case 'G': if (server_status == 301) { /* ** Treat as 303 (GET without content). */ FREE(show_POST_url); result = 303; break; } /* fall through to default */ default: /* ** Get another character. */ if (on_screen == 1) { on_screen = 0; } else { on_screen = 2; } } } FREE(StatusInfo); FREE(url); return (result); } #define okToSleep() (!crawl && !traversal && LYCursesON) /* * Sleep for the given message class's time. */ PUBLIC void LYSleepAlert NOARGS { if (okToSleep()) sleep(AlertSecs); } PUBLIC void LYSleepInfo NOARGS { if (okToSleep()) sleep(InfoSecs); } PUBLIC void LYSleepMsg NOARGS { if (okToSleep()) sleep(MessageSecs); } /* * LYstrerror emulates the ANSI strerror() function. */ #ifdef LYStrerror /* defined as macro in .h file. */ #else PUBLIC char *LYStrerror ARGS1(int, code) { static char temp[80]; sprintf(temp, "System errno is %d.\r\n", code); return temp; } #endif /* HAVE_STRERROR */