1 /* retawq/stuff.c - general stuff
2 This file is part of retawq (<http://retawq.sourceforge.net/>), a network
3 client created by Arne Thomassen; retawq is basically released under certain
4 versions of the GNU General Public License and WITHOUT ANY WARRANTY.
5 Read the file COPYING for license details, README for program information.
6 Copyright (C) 2001-2006 Arne Thomassen <arne@arne-thomassen.de>
7 */
8
9 #include "stuff.h"
10 #include "resource.h"
11
12 #include <time.h>
13
14 declare_local_i18n_buffer
15
16 const_after_init int fd_stdin = -1, fd_stdout = -1, fd_stderr = -1,
17 fd_keyboard_input = -1;
18 tConfiguration config;
19
20 const_after_init tProgramMode program_mode =
21 #if CONFIG_TG == TG_CONSOLE
22 pmConsole
23 #else
24 pmEnvironed
25 #endif
26 ;
27
28 const char* initial_messages = NULL;
29 #if CONFIG_CONSOLE
30 const char* initial_console_msgs = NULL;
31 #endif
32 unsigned char launch_uri_count = 0;
33 const char* launch_uri[2]; /* documents to load on launch (if environed) */
34
35 #if MIGHT_DO_TERMIOS
36 tBoolean do_restore_termios = falsE;
37 struct termios saved_termios;
38 #endif
39
40 tBoolean need_tglib_cleanup = falsE;
41 const_after_init unsigned char __lfdmbs = 0;
42
43 #if MIGHT_USE_COLORS
44 tBoolean use_colors = falsE;
45 #if TGC_IS_CURSES
46 tColorBitmask color_bitmask[cpnMax + 1];
47 #endif
48 #endif
49
50 #if CONFIG_DEBUG
51 const_after_init int debugfd = 2;
52 static char debugstrbuf[STRBUF_SIZE];
53 #endif
54
55
56 /* Strings */
57
58 const char strRetawq[] = "retawq", strCopyright[] = "retawq " RETAWQ_VERSION
59 " - Copyright (C) 2001-2006 Arne Thomassen <arne@arne-thomassen.de>\n",
60 strProgramVersion[] = "retawq " RETAWQ_VERSION, strProgramLegalese[] =
61 "retawq is basically released under certain versions of the\n"
62 "GNU General Public License and WITHOUT ANY WARRANTY.\n"
63 "The project home page is <http://retawq.sourceforge.net/>.\n";
64 const char strEmpty[] = "", strNewline[] = "\n", strMinus[] = "-",
65 strSlash[] = "/", strAsterisk[] = "*", strLt[] = "<", strGt[] = ">",
66 strHm[] = "#", strQm[] = "?", strDoubleQuote[] = "\"",
67 strSpacedDash[] = " - ", strDoubleDot[] = "..", str1dot0[] = "1.0",
68 str1dot1[] = "1.1", strHexnum[] = "0123456789abcdef";
69 static const char strPercld[] = "%ld",
70 strTooManyObjects[] = N_("too many objects");
71 const char strPercsPercs[] = "%s%s", strPercd[] = "%d",
72 strBracedNumstr[] = " (%d %s)"; /* for sprintf()-style formatting */
73 const char strA[] = "a", strShtml[] = "shtml", strReset[] = "reset",
74 strSelect[] = "select", strSubmit[] = "submit", strOn[] = "on",
75 strClose[] = "close", strQuit[] = "quit", strGet[] = "GET",
76 strPost[] = "POST", strLocalhost[] = "localhost", strOptHelp[] = "--help",
77 strOptVersion[] = "--version";
78 const char strText[] = N_("text"), strGraphics[] = N_("graphics"),
79 strUnknown[] = N_("(unknown)"), strCheckbox[] = N_("checkbox"),
80 strBracedDisabled[] = N_("(disabled)"), strButton[] = N_("button"),
81 strImage[] = N_("image"), strYes[] = N_("yes"), strNo[] = N_("no"),
82 strFileUc[] = N_("File"), strDirectoryUc[] = N_("Directory"),
83 strLink[] = N_("link"), strLoginFailed[] = N_("Login failed"),
84 strFwdact[] = N_("feature \"%s\" was disabled at compile-time%s"),
85 strByte[] = N_("byte"), strBytes[] = N_("bytes"), strOff[] = N_("off"),
86 strBadValue[] = N_("bad value"), strErrorTrail[] = N_("; error #%d, %s\n");
87 #if !CONFIG_SESSIONS
88 const char strFsessions[] = N_("Fsessions");
89 #endif
90 #if !CONFIG_JUMPS
91 const char strFjumps[] = N_("Fjumps");
92 #endif
93 #if OPTION_EXECEXT != EXECEXT_ALL
94 const char strFexecext[] =
95 N_("F(this kind of) execution of external programs"); /* CHECKME! */
96 #endif
97 #if CONFIG_CONSOLE
98 const char strConsoleDiscard[] = "cdis";
99 #endif
100 #if (CONFIG_TG == TG_X) || (CONFIG_TG == TG_XCURSES)
101 const char strCantOpenXws[] = N_("can't open X Window System display");
102 #endif
103 #if OPTION_LOCAL_CGI || OPTION_TRAP || (OPTION_EXECEXT & EXECEXT_SHELL)
104 const char strSoftwareId[] = "retawq/" RETAWQ_VERSION;
105 #endif
106
107
108 /* scheme strings: */
109 const char strHttp[] = "http", strFtp[] = "ftp", strLocal[] = "local",
110 strFile[] = "file", strAbout[] = "about", strFinger[] = "finger",
111 strLocalCgi[] = "local-cgi", strNntp[] = "nntp", strNews[] = "news",
112 strCvs[] = "cvs", strGopher[] = "gopher", strInfo[] = "info",
113 strMailto[] = "mailto", strJavascript[] = "javascript", strHttps[] = "https",
114 strFtps[] = "ftps", strPop[] = "pop", strPops[] = "pops",
115 strExecextShell[] = "execext-shell";
116
117
118 /* Functions */
119
streqcase3(const char * _str1,const char * _str2)120 int streqcase3(const char* _str1, const char* _str2)
121 /* This function is a useful mixture of strcmp() and strcasecmp(): it compares
122 the lowercase variant of <str1> with <str2> (not "with the lowercase
123 variant of <str2>"!). The "3" in the function name reminds of the
124 "tristate" nature of the return value (< 0, == 0, > 0), as opposed to
125 the "two-state" (== 0, != 0) return value of streqcase(). */
126 { const unsigned char *str1 = (const unsigned char*) _str1,
127 *str2 = (const unsigned char*) _str2;
128 unsigned char ch1, ch2;
129 do
130 { ch1 = *str1++; ch2 = *str2++;
131 if (ch1 == '\0') break;
132 ch1 = (unsigned char) my_tolower(ch1);
133 } while (ch1 == ch2);
134 return((int) (ch1 - ch2));
135 }
136
strneqcase3(const char * _str1,const char * _str2,size_t maxlen)137 int strneqcase3(const char* _str1, const char* _str2, size_t maxlen)
138 /* like streqcase3(), but compares at most <maxlen> characters */
139 { int retval;
140 if (maxlen <= 0) retval = 0;
141 else
142 { const unsigned char *str1 = (const unsigned char*) _str1,
143 *str2 = (const unsigned char*) _str2;
144 unsigned char ch1, ch2;
145 do
146 { ch1 = *str1++; ch2 = *str2++;
147 if (ch1 == '\0') break;
148 ch1 = (unsigned char) my_tolower(ch1);
149 } while ( (ch1 == ch2) && (--maxlen > 0) );
150 retval = ((int) (ch1 - ch2));
151 }
152 return(retval);
153 }
154
my_atoi(const char * src,int * value,const char ** next,int maxvalue)155 void my_atoi(const char* src, int* value, const char** next, int maxvalue)
156 /* similar to atoi(), but allows only non-negative values, allows to define a
157 maximum possible value, and additionally returns the position of the first
158 non-numeric character */
159 { int v = 0;
160 while (1)
161 { char ch = *src;
162 if (my_isdigit(ch))
163 { v = 10 * v + ((int)(ch - '0'));
164 src++;
165 if (v > maxvalue)
166 { v = maxvalue;
167 while (my_isdigit(*src)) { src++; } /* skip number */
168 goto done;
169 }
170 }
171 else
172 { done:
173 *value = v;
174 if (next != NULL) *next = src;
175 return;
176 }
177 }
178 }
179
my_strnchr(const char * str,int _ch,int count)180 char* my_strnchr(const char* str, int _ch, int count)
181 /* like strchr(), but checks at most <count> characters */
182 { char ch = ((char) _ch);
183 while (count-- > 0)
184 { char c = *str++;
185 if (c == '\0') break;
186 else if (c == ch) return(unconstify(str) - 1);
187 }
188 return(NULL);
189 }
190
my_strncasestr(const char * haystack,const char * needle,const size_t hlen)191 char* my_strncasestr(const char* haystack, const char* needle,
192 const size_t hlen)
193 /* some mixture of strncasecmp() and strstr() */
194 { const size_t nlen = strlen(needle);
195 size_t h = 0, n = 0;
196 do
197 { if (my_tolower(haystack[h]) == my_tolower(needle[n])) n++;
198 else { h -= n; n = 0; }
199 h++;
200 } while ( (n < nlen) && (h < hlen) );
201 if (n >= nlen) return(unconstify(haystack) + h - nlen - 1); /* found */
202 else return(NULL);
203 }
204
is_suffix(const char * str,const char * suff)205 my_inline tBoolean is_suffix(const char* str, const char* suff)
206 /* returns whether <str> ends with (or equals!) <suff> */
207 { size_t str_len = strlen(str), suff_len = strlen(suff);
208 if ( (str_len >= suff_len) && (!strcmp(str + (str_len - suff_len), suff)) )
209 return(truE);
210 else return(falsE);
211 }
212
my_pattern_matcher(const char * pattern,const char * str)213 tBoolean my_pattern_matcher(const char* pattern, const char* str)
214 /* a _very_ simple pattern matching algorithm - but it's enough for us */
215 { if (*pattern == '*') return(is_suffix(str, pattern + 1));
216 else
217 { if (!strcmp(pattern, str)) return(truE);
218 else return(falsE);
219 }
220 }
221
do_quit_msg(int exitcode,const char * msg)222 static void does_not_return do_quit_msg(int exitcode, const char* msg)
223 { if (need_tglib_cleanup)
224 {
225 #if CONFIG_TG == TG_X
226 (void) XCloseDisplay(xws_display);
227 #elif CONFIG_TG == TG_GTK
228 gtk_main_quit();
229 #elif TGC_IS_CURSES
230 (void) endwin();
231 #endif
232 }
233 #if MIGHT_DO_TERMIOS
234 if (do_restore_termios)
235 { int err;
236 unsigned char loopcount = 0;
237 do
238 { err = tcsetattr(0, TCSAFLUSH, &saved_termios);
239 } while ( (err == -1) && (errno == EINTR) && (++loopcount < 100) );
240 }
241 #endif
242 if (msg != NULL)
243 { const int fd = ( (exitcode != 0) ? fd_stderr : fd_stdout );
244 /* if (lfdmbs(....)) */ my_write_str(fd, msg);
245 debugmsg(msg);
246 }
247 resource_quit();
248 exit(exitcode);
249 }
250
do_quit(void)251 void do_quit(void)
252 /* quit without errors */
253 { do_quit_msg(0, NULL);
254 }
255
do_quit_sig(void)256 void do_quit_sig(void)
257 /* terminated by a signal */
258 { do_quit_msg(2, _("retawq: terminated by a signal\n"));
259 }
260
fatal_error(int err,const char * str)261 void fatal_error(int err, const char* str)
262 /* quits the program due to a fatal error; <err> must be errno or an E...
263 constant or 0. */
264 { static const char prefix[] = N_("retawq: fatal error: ");
265 /* ugly colon-ialism... */
266 static tBoolean is_recursive = falsE;
267 const tBoolean is_rec = is_recursive;
268 char buf[1000];
269 const char* ptr;
270 is_recursive = truE;
271 /* if (lfdmbs(2)) */
272 { const char* p = ( is_rec ? prefix : _(prefix) );
273 my_write_str(fd_stderr, p); my_write_str(fd_stderr, str);
274 debugmsg(p); debugmsg(str);
275 }
276 if (err <= 0) ptr = strNewline;
277 else
278 { const char* errstr = (is_rec ? __my_strerror(err) : my_strerror(err));
279 const tBoolean too_long = cond2boolean(strlen(errstr) > 200);
280 sprint_safe(buf, (is_rec ? strErrorTrail : _(strErrorTrail)), err,
281 (too_long ? (is_rec ? strUnknown : _(strUnknown)) : errstr));
282 ptr = buf;
283 }
284 do_quit_msg(1, ptr);
285 }
286
out_of_memory(void)287 static void does_not_return out_of_memory(void)
288 { fatal_error(ENOMEM, _("out of memory"));
289 }
290
291
292 #if !CONFIG_DEBUG
293 my_inline /* gcc/gdb and line numbers... */
294 #endif
__memory_allocate_ll(size_t size)295 void* __memory_allocate_ll(size_t size)
296 { void* ptr = malloc(size);
297 if (ptr == NULL) out_of_memory();
298 return(ptr);
299 }
300
memory_allocate_ll(size_t size)301 void* memory_allocate_ll(size_t size)
302 { void* ptr = __memory_allocate_ll(size);
303 my_memclr(ptr, size);
304 return(ptr);
305 }
306
memory_reallocate_ll(void * ptr,size_t size)307 void* memory_reallocate_ll(void* ptr, size_t size)
308 { void* ptr2 = realloc(ptr, size);
309 if (ptr2 == NULL) out_of_memory();
310 return(ptr2);
311 }
312
memory_deallocate_ll(const void * ptr)313 void memory_deallocate_ll(const void* ptr)
314 { /* guess what... */
315 free(__unconstify(void*, ptr));
316 }
317
318 #if NEED_SPECIAL_GETTEXT
319 #define is_special_char(ch) ( (ch == '�') || (ch == '�') || (ch == '�') || \
320 (ch == '�') || (ch == '�') || (ch == '�') || (ch == '�') )
my_do_gettext(char ** buffer,const char * str)321 char* my_do_gettext(char** buffer, const char* str)
322 { if (str == NULL) /* deallocate old strings */
323 { char* ptr = *buffer;
324 while (ptr != NULL)
325 { char* next = *((char**) ptr);
326 memory_deallocate(ptr);
327 ptr = next;
328 }
329 *buffer = NULL;
330 return(NULL);
331 }
332 else /* translate and make seven-bit-clean */
333 { static const char charmap[] = "?????????\t\n??\r?????????????????? !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~??????????????????????????????????!????|????\"?-?-????'??????\"????AAAAAA?CEEEEIIIIDNOOOOO*OUUUUYPsaaaaaa?ceeeeiiiidnooooo/ouuuuypy";
334 const char *s = gettext(str), *src;
335 char *buf, *dest, *retval, ch;
336 size_t len;
337 if (s == NULL) return(NULL); /* "should not happen" */
338 src = s; len = 0;
339 while ( (ch = *src++) != '\0' ) { len++; if (is_special_char(ch)) len++; }
340 buf = __memory_allocate(sizeof(char*) + len + 1, mapString);
341 dest = retval = buf + sizeof(char*);
342 src = s;
343 while ( (ch = *src++) != '\0' )
344 { *dest++ = charmap[(unsigned short) ((unsigned char) ch)];
345 if (is_special_char(ch))
346 { if (ch == '�') *dest++ = 's';
347 else *dest++ = 'e';
348 }
349 }
350 *dest = '\0';
351 *((char**) buf) = *buffer;
352 *buffer = buf;
353 return(retval);
354 }
355 }
356 #undef is_special_char
357 #endif /* #if NEED_SPECIAL_GETTEXT */
358
my_strdup(const char * str)359 char* my_strdup(const char* str)
360 /* duplicates a string; the standard library function strdup() isn't portable,
361 and callers always have to check against a NULL return value - two reasons
362 for using something different... */
363 { size_t size = strlen(str) + 1;
364 char* retval = __memory_allocate(size, mapString);
365 my_memcpy(retval, str, size);
366 return(retval);
367 }
368
my_strndup(const char * str,size_t len)369 char* my_strndup(const char* str, size_t len)
370 { char* retval = __memory_allocate(len + 1, mapString);
371 strncpy(retval, str, len); retval[len] = '\0';
372 return(retval);
373 }
374
my_strdup_tolower(const char * str)375 char* my_strdup_tolower(const char* str)
376 /* duplicates a string, changing uppercase to lowercase */
377 { char* retval = __memory_allocate(strlen(str) + 1, mapString);
378 my_strcpy_tolower(retval, str);
379 return(retval);
380 }
381
my_strndup_tolower(const char * str,size_t len)382 char* my_strndup_tolower(const char* str, size_t len)
383 { char *retval = __memory_allocate(len + 1, mapString), *ptr = retval;
384 while (len-- > 0)
385 { const char ch = *str++;
386 *ptr++ = my_tolower(ch);
387 }
388 *ptr = '\0';
389 return(retval);
390 }
391
392 tBoolean is_time_valid = falsE;
my_time(void)393 time_t my_time(void)
394 { static time_t current_time = 0;
395 if (!is_time_valid)
396 { time_t t = time(NULL);
397 if (t != -1) { current_time = t; is_time_valid = truE; }
398 }
399 return(current_time);
400 }
401
my_memdiff(const void * _a,const void * _b,size_t count)402 tBoolean my_memdiff(const void* _a, const void* _b, size_t count)
403 /* returns whether the two memory regions have different contents; somewhat
404 similar to memcmp(), but I didn't want to go through all the portability
405 mess related to memcmp() for this simple thing... */
406 { tBoolean retval = falsE;
407 const char *a = (const char*) _a, *b = (const char*) _b;
408 while (count-- > 0) { if (*a++ != *b++) { retval = truE; break; } }
409 return(retval);
410 }
411
get_homepath(void)412 const char* get_homepath(void)
413 /* tries to find out the user's home directory */
414 { static const char* homepath = NULL;
415 if (homepath == NULL) /* not yet calculated */
416 { const char* const env = getenv("HOME"); /* user's home directory */
417 if ( (env == NULL) || (*env == '\0') ) homepath = strEmpty;
418 else
419 { tBoolean has_trailslash = cond2boolean(env[strlen(env) - 1] == chDirsep);
420 char* spfbuf;
421 my_spf(NULL, 0, &spfbuf, strPercsPercs, env,
422 (has_trailslash ? strEmpty : strDirsep));
423 homepath = my_spf_use(spfbuf);
424 }
425 }
426 return(homepath);
427 }
428
429 #if CONFIG_LOCALDIR > 1
check_localdirsort(char * str)430 void check_localdirsort(char* str)
431 /* checks and possibly fixes the "sorting" string for local directory sorting,
432 in both run-time configuration entries and URI parts */
433 { const char* src;
434 char ch, *dest;
435 unsigned char bits = 0;
436 if (*str == '_')
437 { /* disables all sorting, no further check necessary */
438 *(str + 1) = '\0'; /* ignore any further characters */
439 return;
440 }
441 src = dest = str;
442 while ( (ch = *src++) != '\0' )
443 { static const char allowed[] = "gimnstu";
444 /* (group ID, case-insensitive name, modification time, name, size,
445 type (file/directory/...), user ID) */
446 const char lch = my_tolower(ch), *pos = my_strchr(allowed, lch);
447 if (pos != NULL) /* it's an _allowed_ sorting option */
448 { char val = (char) (pos - allowed);
449 if (!my_bit_test(&bits, val)) /* only if option not yet used */
450 { my_bit_set(&bits, val);
451 *dest++ = ch;
452 }
453 }
454 }
455 *dest = '\0';
456 }
457 #endif
458
459
460 /* Timeouts for I/O multiplexing */
461
462 #if MIGHT_NEED_TIMEOUTS
463
464 typedef struct tTimeout
465 { struct tTimeout* next;
466 tTimeoutHandler handler;
467 } tTimeout;
468
469 static tTimeout* timeout_handlers = NULL;
470
timeout_lookup(tTimeoutHandler handler)471 static tTimeout* timeout_lookup(tTimeoutHandler handler)
472 { tTimeout* retval = timeout_handlers;
473 while (retval != NULL)
474 { if (retval->handler == handler) break;
475 retval = retval->next;
476 }
477 return(retval);
478 }
479
timeout_register(tTimeoutHandler handler)480 void timeout_register(tTimeoutHandler handler)
481 { tTimeout* t = timeout_lookup(handler);
482 if (t == NULL)
483 { /* not yet registered; "should" be true, lookup is extra care */
484 t = (tTimeout*) __memory_allocate(sizeof(tTimeout), mapOther);
485 t->handler = handler; t->next = timeout_handlers; timeout_handlers = t;
486 }
487 }
488
timeout_unregister(tTimeoutHandler handler)489 void timeout_unregister(tTimeoutHandler handler)
490 { tTimeout* t = timeout_lookup(handler);
491 if (t != NULL) /* "should" be true */
492 { list_extract(&timeout_handlers, t, tTimeout); memory_deallocate(t); }
493 }
494
495 #endif /* #if MIGHT_NEED_TIMEOUTS */
496
497
498 /* File descriptor handling I: register */
499
500 #if NEED_FD_REGISTER
501
502 typedef struct tFdData
503 { struct tFdData* next;
504 int unique_fd, lib_fd;
505 tFdKind kind;
506 } tFdData;
507
508 #define FD_REGISTER_LEN (1 << 4)
509 #define fd_register_hash(fd) ((fd) & (FD_REGISTER_LEN - 1))
510 typedef unsigned char tFdRegisterIndex;
511 static tFdData* fd_data[FD_REGISTER_LEN];
512
fd_register_init(void)513 void __init fd_register_init(void)
514 { my_memclr_arr(fd_data);
515 }
516
get_unique_fd(void)517 static one_caller int get_unique_fd(void)
518 /* The returned number need not have any kind of "meaning", it just has to be
519 _unique_ within the register, so that the associated data can be looked up
520 reliably. */
521 { static int num = 0;
522 int retval = num++;
523 if (retval < 0) fatal_error(0, _(strTooManyObjects)); /* overflow; "rare" */
524 return(retval);
525 }
526
fd_register(int * _lib_fd,tFdKind kind)527 void fd_register(int* _lib_fd, tFdKind kind)
528 { tFdData **head, *data;
529 int lib_fd = *_lib_fd, unique_fd = *_lib_fd = get_unique_fd();
530 head = &(fd_data[fd_register_hash(unique_fd)]);
531 data = (tFdData*) __memory_allocate(sizeof(tFdData), mapOther);
532 data->unique_fd = unique_fd; data->lib_fd = lib_fd;
533 data->kind = kind; data->next = *head; *head = data;
534 #if CONFIG_DEBUG
535 sprint_safe(debugstrbuf, "fd_register(): %d, %d, %d\n", unique_fd, lib_fd,
536 kind);
537 debugmsg(debugstrbuf);
538 #endif
539 }
540
__fd_register_lookup(int unique_fd)541 static tFdData* __fd_register_lookup(int unique_fd)
542 { tFdData* data;
543 if (unique_fd < 0) return(NULL); /* may happen, e.g. if no stdin fd exists */
544 data = fd_data[fd_register_hash(unique_fd)];
545 while (data != NULL)
546 { if (data->unique_fd == unique_fd) break;
547 data = data->next;
548 }
549 if (data == NULL) /* "should not happen" */
550 fatal_error(0, _("can't lookup file descriptor"));
551 return(data);
552 }
553
fd_register_lookup(int * fd)554 tFdKind fd_register_lookup(int* fd)
555 { tFdKind retval;
556 const tFdData* data = __fd_register_lookup(*fd);
557 #if CONFIG_DEBUG
558 sprint_safe(debugstrbuf, "fd_register_lookup(): %d, ", *fd);
559 debugmsg(debugstrbuf);
560 #endif
561 if (data != NULL) { *fd = data->lib_fd; retval = data->kind; }
562 else retval = fdkNone;
563 #if CONFIG_DEBUG
564 sprint_safe(debugstrbuf, "%d, %d\n", *fd, retval); debugmsg(debugstrbuf);
565 #endif
566 return(retval);
567 }
568
569 #if CONFIG_TG == TG_XCURSES
fd_register_rlookup(int * fd,tFdKind kind)570 tBoolean fd_register_rlookup(int* fd, tFdKind kind)
571 /* reverse lookup; not (yet?) used, just for completeness... */
572 { tBoolean retval = falsE;
573 tFdRegisterIndex idx;
574 for (idx = 0; idx < FD_REGISTER_LEN; idx++)
575 { tFdData* data = fd_data[idx];
576 while (data != NULL)
577 { if ( (data->lib_fd == *fd) && (data->kind & kind) )
578 { *fd = data->unique_fd; retval = truE; goto out; }
579 data = data->next;
580 }
581 }
582 out:
583 return(retval);
584 }
585 #endif
586
fd_unregister(int * fd)587 static tFdKind fd_unregister(int* fd)
588 { tFdKind retval;
589 tFdData **head, *data;
590 if (*fd < 0) retval = fdkNone;
591 else
592 { head = &(fd_data[fd_register_hash(*fd)]); data = __fd_register_lookup(*fd);
593 retval = data->kind; *fd = data->lib_fd; list_extract(head, data, tFdData);
594 memory_deallocate(data);
595 }
596 return(retval);
597 }
598
599 #endif /* #if NEED_FD_REGISTER */
600
601
602 /* File descriptor handling II: observing, multiplexing */
603
604 static size_t fd_observatory_trashcount = 0;
605
606 #if OPTION_POLL == 0
607
608 #if (HAVE_SYS_TIME_H) && (MIGHT_NEED_TIMEOUTS)
609 #include <sys/time.h> /* might be needed for struct timeval */
610 #endif
611
612 #if HAVE_SYS_SELECT_H
613 #include <sys/select.h>
614 #endif
615
616 static fd_set fds_read, fds_write, fds_r, fds_w, fds_e;
617 static int highest_select_fd = 0;
618
fd_is_observable(int fd)619 tBoolean fd_is_observable(int fd)
620 { tBoolean retval;
621 #if NEED_FD_REGISTER
622 tFdKind kind = fd_register_lookup(&fd);
623 #endif
624 #if USE_LWIP
625 if (kind & fdkSocket) retval = truE;
626 else
627 #endif
628 { retval = cond2boolean(fd < FD_SETSIZE); }
629 return(retval);
630 }
631
632 #elif OPTION_POLL == 1
633
634 #if HAVE_SYS_POLL_H
635 #include <sys/poll.h>
636 #endif
637
638 static size_t fd_observatory_count = 0;
639
640 #else
641
642 #error "Bad value of compile-time configuration variable OPTION_POLL"
643
644 #endif
645
646 typedef struct tFdObservationData
647 { struct tFdObservationData* next;
648 tFdObservationHandler handler;
649 void* handler_data; /* (e.g. tConnection*) */
650 int fd; /* (unique_fd, that is) */
651 tFdObservationFlags flags;
652 tBoolean is_trashed;
653 } tFdObservationData;
654
655 #define FD_OBSERVATORY_LEN (1 << 3)
656 #define fd_observe_hash(fd) ((fd) & (FD_OBSERVATORY_LEN - 1))
657 static tFdObservationData* fd_observatory[FD_OBSERVATORY_LEN];
658
fd_observe_init(void)659 one_caller void __init fd_observe_init(void)
660 { my_memclr_arr(fd_observatory);
661 #if OPTION_POLL == 0
662 FD_ZERO(&fds_read); FD_ZERO(&fds_write);
663 fds_r = fds_e = fds_read; fds_w = fds_write;
664 #endif
665 }
666
fd_observatory_cleanup(void)667 static void fd_observatory_cleanup(void)
668 { if (fd_observatory_trashcount > 0) /* must do some cleanup; IMPROVEME! */
669 { unsigned char cnt;
670 for (cnt = 0; cnt < FD_OBSERVATORY_LEN; cnt++)
671 { tFdObservationData* data;
672 recheck_list: data = fd_observatory[cnt];
673 while (data != NULL)
674 { if (data->is_trashed)
675 { list_extract(&(fd_observatory[cnt]), data, tFdObservationData);
676 memory_deallocate(data); fd_observatory_trashcount--;
677 if (fd_observatory_trashcount > 0) goto recheck_list;
678 else goto trash_disposed;
679 }
680 data = data->next;
681 }
682 }
683 trash_disposed: {}
684 }
685 }
686
fd_observe_lookup(int fd)687 static tFdObservationData* fd_observe_lookup(int fd)
688 { tFdObservationData* data = fd_observatory[fd_observe_hash(fd)];
689 while (data != NULL)
690 { if ( (data->fd == fd) && (!(data->is_trashed)) ) break;
691 data = data->next;
692 }
693 return(data);
694 }
695
fd_observe(int fd,tFdObservationHandler handler,void * handler_data,tFdObservationFlags flags)696 void fd_observe(int fd, tFdObservationHandler handler, void* handler_data,
697 tFdObservationFlags flags)
698 { tFdObservationData* data = fd_observe_lookup(fd);
699 #if NEED_FD_REGISTER
700 tFdKind kind;
701 #endif
702 #if CONFIG_DEBUG
703 sprint_safe(debugstrbuf,
704 "fd_observe(): fd=%d, flags=%d, handler=%p, handler_data=%p\n", fd, flags,
705 handler, handler_data);
706 debugmsg(debugstrbuf);
707 #endif
708 if (data == NULL)
709 { const int h = fd_observe_hash(fd);
710 data = (tFdObservationData*) memory_allocate(sizeof(tFdObservationData),
711 mapOther);
712 data->fd = fd; data->next = fd_observatory[h]; fd_observatory[h] = data;
713 #if OPTION_POLL == 1
714 fd_observatory_count++;
715 #endif
716 }
717 data->handler = handler; data->handler_data = handler_data;
718 data->flags = flags;
719
720 #if NEED_FD_REGISTER
721 kind = fd_register_lookup(&fd);
722 #endif
723 #if USE_LWIP
724 if (kind & fdkSocket)
725 #endif
726 {
727 #if OPTION_POLL == 0
728 if (highest_select_fd < fd) highest_select_fd = fd;
729 if (flags & fdofRead) FD_SET(fd, &fds_read);
730 else { FD_CLR(fd, &fds_read); FD_CLR(fd, &fds_r); FD_CLR(fd, &fds_e); }
731 if (flags & fdofWrite) FD_SET(fd, &fds_write);
732 else { FD_CLR(fd, &fds_write); FD_CLR(fd, &fds_w); }
733 #endif
734 }
735 }
736
fd_unobserve(int fd)737 void fd_unobserve(int fd)
738 { tFdObservationData* data = fd_observe_lookup(fd);
739 #if NEED_FD_REGISTER
740 tFdKind kind;
741 #endif
742 #if CONFIG_DEBUG
743 sprint_safe(debugstrbuf, "fd_unobserve(%d): %p\n", fd, data);
744 debugmsg(debugstrbuf);
745 #endif
746 if (data != NULL)
747 { /* (We only clear here and deallocate at the end of fd_multiplex().) */
748 data->is_trashed = truE; fd_observatory_trashcount++;
749 #if OPTION_POLL == 1
750 if (fd_observatory_count > 0) fd_observatory_count--; /* "should" be true*/
751 #endif
752 #if NEED_FD_REGISTER
753 kind = fd_register_lookup(&fd);
754 #endif
755 #if USE_LWIP
756 if (kind & fdkSocket)
757 #endif
758 {
759 #if OPTION_POLL == 0
760 FD_CLR(fd, &fds_read); FD_CLR(fd, &fds_r); FD_CLR(fd, &fds_e);
761 FD_CLR(fd, &fds_write); FD_CLR(fd, &fds_w); debugmsg("FD_CLR()\n");
762 #endif
763 }
764 }
765 }
766
fd_multiplex(void)767 void fd_multiplex(void)
768 { unsigned short loopcount = 0;
769 int count;
770 unsigned char cnt;
771 #if MIGHT_NEED_TIMEOUTS
772 tTimeout* toh;
773 tBoolean do_timeout;
774 int msec;
775 #endif
776
777 fd_observatory_cleanup(); loop:
778
779 /* step #1: calculate the timeout, if any */
780
781 #if MIGHT_NEED_TIMEOUTS
782 toh = timeout_handlers; msec = 10000000; do_timeout = falsE;
783 while (toh != NULL)
784 { int m;
785 if ((toh->handler)(&m)) { do_timeout = truE; if (msec > m) msec = m; }
786 toh = toh->next;
787 }
788 #endif
789
790 /* step #2: call into the operating system */
791
792 #if OPTION_POLL == 0
793
794 {
795 #if MIGHT_NEED_TIMEOUTS
796 struct timeval tov, *tovp;
797 if (!do_timeout) tovp = NULL;
798 else { my_memclr_var(tov); tov.tv_usec = msec * 1000; tovp = &tov; }
799 #else
800 #define tovp (NULL)
801 #endif
802 #if CONFIG_DEBUG
803 { int c = 0;
804 debugmsg("multiplex A");
805 while (c <= highest_select_fd)
806 { if (FD_ISSET(c, &fds_read))
807 { sprint_safe(debugstrbuf, " r%d", c); debugmsg(debugstrbuf); }
808 if (FD_ISSET(c, &fds_write))
809 { sprint_safe(debugstrbuf, " w%d", c); debugmsg(debugstrbuf); }
810 c++;
811 }
812 debugmsg(strNewline);
813 }
814 #endif
815 fds_r = fds_e = fds_read; fds_w = fds_write;
816 #if USE_LWIP
817 count = lwip_select(highest_select_fd + 1, &fds_r, &fds_w, &fds_e, tovp);
818 #else
819 count = select(highest_select_fd + 1, &fds_r, &fds_w, &fds_e, tovp);
820 #endif
821 #if CONFIG_DEBUG
822 { const int e = errno;
823 sprint_safe(debugstrbuf, "multiplex B: %d, %d\n", count, errno);
824 debugmsg(debugstrbuf); errno = e;
825 }
826 #endif
827 #undef tovp
828 }
829
830 #endif
831
832 /* step #3: handle the returned file descriptors */
833
834 is_time_valid = falsE; /* we probably slept */
835 if (count <= 0)
836 { if ( (count == -1) && (errno == EINTR) && (++loopcount < 10000) )
837 goto loop;
838 #if MIGHT_NEED_TIMEOUTS
839 else if ( (count == 0) && (do_timeout) ) return; /* timed out */
840 #endif
841 else fatal_error(((count == -1) ? errno : 0),_("I/O multiplexing failed"));
842 }
843
844 resource_preplex();
845 for (cnt = 0; cnt < FD_OBSERVATORY_LEN; cnt++)
846 { const tFdObservationData* data = fd_observatory[cnt];
847 while (data != NULL)
848 { int fd;
849 tFdObservationFlags flags;
850 #if NEED_FD_REGISTER
851 tFdKind kind;
852 #endif
853 if (data->is_trashed) goto do_next;
854 fd = data->fd; flags = data->flags;
855 #if NEED_FD_REGISTER
856 kind = fd_register_lookup(&fd);
857 #endif
858 #if USE_LWIP
859 /* if (kind & fdkSocket) goto do_next; -- CHECKME! */
860 #endif
861 #if OPTION_POLL == 0
862 if (!(FD_ISSET(fd, &fds_r) || FD_ISSET(fd, &fds_e))) flags &= ~fdofRead;
863 if (!FD_ISSET(fd, &fds_w)) flags &= ~fdofWrite;
864 #endif
865 if (flags) /* something can be done */
866 { tFdObservationHandler handler = data->handler;
867 void* handler_data = data->handler_data;
868 #if CONFIG_DEBUG
869 sprint_safe(debugstrbuf,
870 "fd observatory: fd=%d, flags=%d, handler=%p, handler_data=%p\n",
871 fd, flags, handler, handler_data);
872 debugmsg(debugstrbuf);
873 #endif
874 (handler)(handler_data, flags);
875 count--; if (count <= 0) goto finish; /* done */
876 }
877 do_next: data = data->next;
878 }
879 }
880 finish: fd_observatory_cleanup(); resource_postplex();
881 }
882
fatal_tmofd(int fd)883 void fatal_tmofd(int fd)
884 {
885 #if OPTION_POLL == 0
886 #if NEED_FD_REGISTER
887 tFdKind kind = fd_register_lookup(&fd);
888 #endif
889 #if USE_LWIP
890 if (!(kind & fdkSocket))
891 #endif
892 { char buf[1024];
893 sprint_safe(buf,
894 _("%s (given value is %d, highest select()-able value is %d)"),
895 _(strResourceError[reTmofd]), fd, FD_SETSIZE - 1);
896 fatal_error(0, buf);
897 }
898 #endif
899 fatal_error(0, _(strResourceError[reTmofd]));
900 }
901
902
903 /* File descriptor handling III: the usual stuff */
904
my_create(const char * path,int flags,mode_t mode)905 int my_create(const char* path, int flags, mode_t mode)
906 { int retval;
907 unsigned char loopcount = 0;
908 do
909 { retval = open(path, flags | O_NOCTTY, mode);
910 } while ( (retval == -1) && (errno == EINTR) && (++loopcount < 100) );
911 if (retval >= 0) fd_register(&retval, fdkFile);
912 return(retval);
913 }
914
my_open(const char * path,int flags)915 int my_open(const char* path, int flags)
916 { int retval;
917 unsigned char loopcount = 0;
918 do
919 { retval = open(path, flags | O_NOCTTY);
920 } while ( (retval == -1) && (errno == EINTR) && (++loopcount < 100) );
921 if (retval >= 0) fd_register(&retval, fdkFile);
922 return(retval);
923 }
924
my_close(int fd)925 void my_close(int fd)
926 {
927 #if NEED_FD_REGISTER
928 tFdKind kind;
929 #endif
930 fd_unobserve(fd);
931 #if NEED_FD_REGISTER
932 kind = fd_unregister(&fd);
933 #endif
934 #if USE_LWIP
935 if (kind & fdkSocket) (void) lwip_close(fd);
936 else
937 #endif
938 { int err;
939 unsigned short loopcount = 0;
940 #if CONFIG_DEBUG
941 char buf[100];
942 sprint_safe(buf, "my_close(%d)\n", fd); debugmsg(buf);
943 #endif
944 do
945 { err = close(fd);
946 } while ( (err == -1) && (errno == EINTR) && (++loopcount < 10000) );
947 }
948 }
949
my_read(int fd,void * buf,size_t count)950 ssize_t my_read(int fd, void* buf, size_t count)
951 { ssize_t retval;
952 #if NEED_FD_REGISTER
953 tFdKind kind = fd_register_lookup(&fd);
954 #endif
955 #if USE_LWIP
956 if (kind & fdkSocket)
957 { errno = 0; /* silly old lwIP versions didn't set errno on error */
958 retval = lwip_read(fd, buf, count);
959 }
960 else
961 #endif
962 { unsigned char loopcount = 0;
963 do
964 { retval = read(fd, buf, count);
965 } while ( (retval == -1) && (errno == EINTR) && (++loopcount < 100) );
966 }
967 return(retval);
968 }
969
__my_write(int fd,const void * buf,size_t count)970 ssize_t __my_write(int fd, const void* buf, size_t count)
971 /* may write less than <count> bytes */
972 { ssize_t retval;
973 #if NEED_FD_REGISTER
974 tFdKind kind = fd_register_lookup(&fd);
975 #endif
976 #if USE_LWIP
977 if (kind & fdkSocket)
978 { errno = 0; /* silly old lwIP versions didn't set errno on error */
979 retval = lwip_write(fd, buf, count);
980 }
981 else
982 #endif
983 { unsigned char loopcount = 0;
984 do
985 { retval = write(fd, buf, count);
986 } while ( (retval == -1) && (errno == EINTR) && (++loopcount < 100) );
987 }
988 return(retval);
989 }
990
my_write(int fd,const void * buf,size_t count)991 ssize_t my_write(int fd, const void* buf, size_t count)
992 /* will write <count> bytes (or fail on error) */
993 { size_t countleft = count;
994 while (countleft > 0)
995 { ssize_t err = __my_write(fd, buf, countleft);
996 if (err == 0) { err = -1; errno = EIO; } /* CHECKME! */
997 if (err < 0) return(err);
998 else if (err > (ssize_t) countleft) break; /* "can't happen" */
999 countleft -= err; buf = ((char*) buf) + err;
1000 }
1001 return((ssize_t) count);
1002 }
1003
my_write_crucial(int fd,const void * buf,size_t count)1004 void my_write_crucial(int fd, const void* buf, size_t count)
1005 { ssize_t err = my_write(fd, buf, count);
1006 if (err != (ssize_t) count)
1007 fatal_error(((err == -1) ? errno : 0), _("write() failed"));
1008 }
1009
my_mmap_file_readonly(const char * filename,void ** _b,size_t * _s)1010 unsigned char my_mmap_file_readonly(const char* filename, void** _b,
1011 size_t* _s)
1012 /* tries to open a regular file in read-only mode and to map it into memory;
1013 return value: 0=error, 1=empty, 2=fine */
1014 { unsigned char retval = 0; /* assume failure */
1015 int fd = my_open(filename, O_RDONLY);
1016 struct stat statbuf;
1017 size_t size;
1018 void* filebuf;
1019 if (fd < 0) goto out;
1020 if (my_fstat(fd, &statbuf) != 0) goto cleanup;
1021 if (!S_ISREG(statbuf.st_mode))
1022 { if (S_ISDIR(statbuf.st_mode)) errno = EISDIR;
1023 goto cleanup;
1024 }
1025 size = statbuf.st_size;
1026 if (size <= 0) { retval = 1; goto cleanup; }
1027 filebuf = my_mmap(size, fd);
1028 if (filebuf == MAP_FAILED) goto cleanup;
1029 my_madvise_sequential(filebuf, size);
1030 *_b = filebuf; *_s = size; retval = 2;
1031 cleanup: { int e = errno; my_close(fd); errno = e; }
1032 out: return(retval);
1033 }
1034
my_stat(const char * filename,struct stat * sb)1035 int my_stat(const char* filename, struct stat* sb)
1036 { int retval;
1037 unsigned char loopcount = 0;
1038 do
1039 { retval = stat(filename, sb);
1040 } while ( (retval == -1) && (errno == EINTR) && (++loopcount < 100) );
1041 return(retval);
1042 }
1043
my_fstat(int fd,struct stat * sb)1044 int my_fstat(int fd, struct stat* sb)
1045 { int retval;
1046 unsigned char loopcount = 0;
1047 #if NEED_FD_REGISTER
1048 (void) fd_register_lookup(&fd);
1049 #endif
1050 do
1051 { retval = fstat(fd, sb);
1052 } while ( (retval == -1) && (errno == EINTR) && (++loopcount < 100) );
1053 return(retval);
1054 }
1055
1056 #if NEED_FD_REGISTER
1057
my_pipe(int * fdpair)1058 int my_pipe(int* fdpair)
1059 { int retval = pipe(fdpair);
1060 if (retval == 0)
1061 { fd_register(&(fdpair[0]), fdkPipe); fd_register(&(fdpair[1]), fdkPipe); }
1062 return(retval);
1063 }
1064
my_isatty(int fd)1065 int my_isatty(int fd)
1066 { (void) fd_register_lookup(&fd);
1067 return(isatty(fd));
1068 }
1069
1070 #endif /* #if NEED_FD_REGISTER */
1071
1072 #if MIGHT_FORK_EXEC && defined(FD_CLOEXEC)
make_fd_cloexec(int fd)1073 void make_fd_cloexec(int fd)
1074 {
1075 #if NEED_FD_REGISTER
1076 tFdKind kind = fd_register_lookup(&fd);
1077 #endif
1078 #if USE_LWIP
1079 if (!(kind & fdkSocket))
1080 #endif
1081 { int flags = fcntl(fd, F_GETFD, 0);
1082 if (flags != -1) (void) fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
1083 }
1084 }
1085 #endif
1086
1087
1088 /* Data handling mechanism */
1089
dhm_generic_handler(tDhmGenericData ** _dhm_data,const void * cmddata,tDhmCommand cmd)1090 void dhm_generic_handler(tDhmGenericData** _dhm_data, const void* cmddata,
1091 tDhmCommand cmd)
1092 { tDhmGenericData* data = *_dhm_data;
1093 tDhmNotificationFlags old_flags, new_flags, flags;
1094 const tDhmNotificationSetup* dns;
1095 tDhmNotificationEntry* dne;
1096 const tDhmControlData* dcd;
1097 tDhmControlCode dcc;
1098 void* dcdd;
1099 #if CONFIG_DEBUG
1100 static size_t depth = 0;
1101 depth++;
1102 sprint_safe(debugstrbuf,
1103 "dhm_generic_handler(%p, %p, %d:%d) - %s@%p, depth=%d\n", data,cmddata,cmd,
1104 ( (cmd == dhmcNotify) ? (*((const tDhmNotificationFlags*) cmddata)) : 0 ),
1105 ( (data != NULL) ? data->debugstr : strQm ), ( (data != NULL) ?
1106 data->object : NULL ), depth);
1107 debugmsg(debugstrbuf);
1108 #endif
1109 switch (cmd)
1110 { case dhmcInit:
1111 data = *_dhm_data = memory_allocate(sizeof(tDhmGenericData), mapOther);
1112 data->object = __unconstify(void*, cmddata); break;
1113 case dhmcGet: data->refcount++; break;
1114 case dhmcPut:
1115 if (data->refcount > 0) data->refcount--; /* "should" be true */
1116 if (data->refcount <= 0)
1117 { dcc = dhmccRefcount0; dcdd = NULL; goto do_control; }
1118 break;
1119 case dhmcNotificationSetup:
1120 dns = (const tDhmNotificationSetup*) cmddata;
1121 dne = data->notifications;
1122 while (dne != NULL)
1123 { if ( (dne->callback == dns->callback) &&
1124 (dne->callback_data == dns->callback_data) )
1125 break; /* found */
1126 dne = dne->next;
1127 }
1128 old_flags = ( (dne != NULL) ? dne->flags : dhmnfNone );
1129 flags = dns->flags;
1130 switch (dns->mode)
1131 { case dhmnSet: new_flags = flags; break;
1132 case dhmnOr: new_flags = old_flags | flags; break;
1133 case dhmnAndnot: new_flags = old_flags & ~flags; break;
1134 case dhmnXor: new_flags = old_flags ^ flags; break;
1135 default: debugmsg("BUG: dhmn\n"); new_flags = dhmnfNone; break;
1136 }
1137 if (dne != NULL)
1138 { if (new_flags != dhmnfNone) dne->flags = new_flags;
1139 else /* lost interest */
1140 { list_extract(&(data->notifications), dne, tDhmNotificationEntry);
1141 memory_deallocate(dne);
1142 }
1143 }
1144 else if (new_flags != dhmnfNone)
1145 { dne = __memory_allocate(sizeof(tDhmNotificationEntry), mapOther);
1146 dne->next = data->notifications; data->notifications = dne;
1147 dne->callback = dns->callback; dne->callback_data = dns->callback_data;
1148 dne->flags = new_flags;
1149 }
1150 break;
1151 case dhmcNotify:
1152 flags = *((const tDhmNotificationFlags*) cmddata);
1153 dne = data->notifications;
1154 while (dne != NULL)
1155 { new_flags = flags & dne->flags;
1156 if (new_flags != dhmnfNone) /* this callback wants to be notified */
1157 (dne->callback)(dne->callback_data, new_flags);
1158 dne = dne->next;
1159 }
1160 if (flags & (dhmnfRemoval | dhmnfOnce))
1161 { /* The object might be deallocated soon, so we better clean up... */
1162 dne = data->notifications;
1163 while (dne != NULL)
1164 { tDhmNotificationEntry* next = dne->next;
1165 memory_deallocate(dne); dne = next;
1166 }
1167 memory_deallocate(data); *_dhm_data = NULL;
1168 }
1169 break;
1170 case dhmcControl:
1171 dcd = (const tDhmControlData*) cmddata;
1172 dcc = dcd->code; dcdd = dcd->data;
1173 do_control:
1174 if (data->control_handler != NULL)
1175 (data->control_handler)(data->object, dcdd, dcc);
1176 break;
1177 }
1178 #if CONFIG_DEBUG
1179 sprint_safe(debugstrbuf, "dhm_generic_handler(): leaving depth %d\n", depth);
1180 debugmsg(debugstrbuf); depth--;
1181 #endif
1182 }
1183
1184
1185 /* sprintf() replacement */
1186
1187 #if HAVE_STDARG_H
1188 #include <stdarg.h>
1189 #elif HAVE_VARARGS_H
1190 #include <varargs.h>
1191 #endif
1192
my_spf(char * staticbuf,size_t staticbufsize,char ** spfbuf,const char * format,...)1193 void my_spf(char* staticbuf, size_t staticbufsize, char** spfbuf,
1194 const char* format, ...)
1195 /* similar to sprintf(), but without the problem of buffer overflows; if the
1196 resulting string would not fit in the static buffer, a dynamic buffer is
1197 allocated. - Call either my_spf_cleanup() or my_spf_use() afterwards. */
1198 /* The design of this function is based on the fact that the static buffer is
1199 "almost always" large enough, so we do the expensive dynamic allocation
1200 only in the few cases where it's absolutely necessary for correctness or
1201 where we'd want a dynamic buffer anyway. */
1202 { va_list vl;
1203 char numbuf[50], ch, *dest, *desttmp;
1204 const char* tmp;
1205 size_t destlen;
1206 tBoolean inside, is_long SHUT_UP_COMPILER(falsE);
1207
1208 /* Find out how long the resulting string can become at most: */
1209 destlen = strlen(format) + 1;
1210 tmp = format; inside = falsE;
1211 va_start(vl, format);
1212 while ( (ch = *tmp++) != '\0' )
1213 { if (inside)
1214 { inside = falsE;
1215 if (ch == 's') /* found a "%s" specifier */
1216 { const char* const s = va_arg(vl, char*);
1217 if (s != NULL) destlen += strlen(s);
1218 }
1219 else if (ch == 'd') /* found a "%d" specifier */
1220 { if (is_long)
1221 { const long int num = va_arg(vl, long int);
1222 sprint_safe(numbuf, strPercld, num); /* IMPROVEME! */
1223 }
1224 else
1225 { const int num = va_arg(vl, int);
1226 sprint_safe(numbuf, strPercd, num); /* IMPROVEME! */
1227 }
1228 destlen += strlen(numbuf);
1229 }
1230 else if (ch == 'c') /* found a "%c" specifier */
1231 { (void) va_arg(vl, int); /* (only needed to proceed in the vl list) */
1232 destlen++;
1233 }
1234 else if (ch == 'p') /* found a "%p" specifier */
1235 { (void) va_arg(vl, void*); /* (only needed to proceed in the vl list) */
1236 destlen += (2 + 2 * sizeof(void*));
1237 }
1238 else if (ch == 'l') { inside = is_long = truE; destlen--; goto dontdec; }
1239 else goto dontdec;
1240 destlen -= 2; /* we'll strip the two characters "%s"/"%d"/... */
1241 dontdec: {}
1242 }
1243 else
1244 { if (ch == '%') { inside = truE; is_long = falsE; }
1245 }
1246 }
1247 va_end(vl);
1248
1249 /* Build the string: */
1250 if ( (staticbuf != NULL) && (staticbufsize >= destlen) ) dest = staticbuf;
1251 else dest = __memory_allocate(destlen, mapString);
1252 desttmp = dest; tmp = format; inside = falsE;
1253 va_start(vl, format);
1254 while ( (ch = *tmp++) != '\0' )
1255 { if (inside)
1256 { inside = falsE;
1257 if (ch == 's') /* found a "%s" specifier */
1258 { const char* s = va_arg(vl, char*);
1259 char ch0;
1260 if (s != NULL) { while ( (ch0 = *s++) != '\0' ) *desttmp++ = ch0; }
1261 }
1262 else if (ch == 'd') /* found a "%d" specifier */
1263 { char ch0, *s = numbuf;
1264 if (is_long)
1265 { const long int num = va_arg(vl, long int);
1266 sprint_safe(numbuf, strPercld, num); /* IMPROVEME! */
1267 }
1268 else
1269 { const int num = va_arg(vl, int);
1270 sprint_safe(numbuf, strPercd, num); /* IMPROVEME! */
1271 }
1272 while ( (ch0 = *s++) != '\0' ) *desttmp++ = ch0;
1273 }
1274 else if (ch == 'c') /* found a "%c" specifier */
1275 { const char ch0 = (char) va_arg(vl, int);
1276 if (ch0 != '\0') *desttmp++ = ch0;
1277 }
1278 else if (ch == 'p') /* found a "%p" specifier */
1279 { void* p = va_arg(vl, void*);
1280 unsigned long int l = (unsigned long int) p;
1281 unsigned short count = 2 * sizeof(void*);
1282 char* t;
1283 *desttmp++ = '0'; *desttmp++ = 'x';
1284 t = desttmp = desttmp + count;
1285 while (count-- > 0)
1286 { *--t = strHexnum[l & 15];
1287 l >>= 4;
1288 }
1289 /* CHECKME: don't output leading zeroes? */
1290 }
1291 else if (ch == 'l') { inside = is_long = truE; }
1292 else goto append;
1293 }
1294 else
1295 { if (ch == '%') { inside = truE; is_long = falsE; }
1296 else { append: *desttmp++ = ch; }
1297 }
1298 }
1299 va_end(vl);
1300 *desttmp = '\0';
1301 *spfbuf = dest;
1302 }
1303
1304
1305 /* Replacements for broken/missing C library functions; we don't care about
1306 performance here - the only purpose of these functions is to make the
1307 program compile and work, nothing more. */
1308
1309 #if !HAVE_MMAP
nonbroken_mmap(size_t len,int fd)1310 void* nonbroken_mmap(size_t len, int fd)
1311 { if (len > 0)
1312 { void *retval = __memory_allocate(len, mapOther), *p = retval;
1313 size_t lenleft = len;
1314 while (1)
1315 { ssize_t err = my_read(fd, p, lenleft);
1316 if (err <= 0) { memory_deallocate(retval); goto failed; }
1317 else if (err >= (ssize_t) lenleft) return(retval); /* done */
1318 lenleft -= err; p += err;
1319 }
1320 }
1321 failed:
1322 return(MAP_FAILED);
1323 }
1324 #endif
1325
1326 #if !HAVE_STRCASECMP
nonbroken_strcasecmp(const char * _s1,const char * _s2)1327 int nonbroken_strcasecmp(const char* _s1, const char* _s2)
1328 { const unsigned char *s1 = (const unsigned char*) _s1,
1329 *s2 = (const unsigned char*) _s2;
1330 while (1)
1331 { unsigned char _a = *s1++, a = my_tolower(_a),
1332 _b = *s2++, b = my_tolower(_b);
1333 if (a != b) return(a - b);
1334 else if (a == '\0') return(0);
1335 }
1336 }
1337 #endif
1338
1339 #if 0
1340 #if !HAVE_STRNCASECMP
1341 int nonbroken_strncasecmp(const char* _s1, const char* _s2, size_t n)
1342 /* Nowadays we seem to use strneqcase() instead of my_strncasecmp() everywhere,
1343 so REMOVEME? */
1344 { const unsigned char *s1 = (const unsigned char*) _s1,
1345 *s2 = (const unsigned char*) _s2;
1346 while (n-- > 0)
1347 { unsigned char _a = *s1++, a = my_tolower(_a),
1348 _b = *s2++, b = my_tolower(_b);
1349 if (a != b) return(a - b);
1350 else if (a == '\0') return(0);
1351 }
1352 return(0);
1353 }
1354 #endif
1355 #endif
1356
1357 #if !HAVE_STRCHR
nonbroken_strchr(const char * s,int _wanted_ch)1358 char* nonbroken_strchr(const char* s, int _wanted_ch)
1359 { const char wanted_ch = (char) _wanted_ch;
1360 char ch;
1361 while ( (ch = *s) != '\0' )
1362 { if (ch == wanted_ch) return(unconstify(s));
1363 s++;
1364 }
1365 return(NULL);
1366 }
1367 #endif
1368
1369 #if !HAVE_STRRCHR
nonbroken_strrchr(const char * s,int _wanted_ch)1370 char* nonbroken_strrchr(const char* s, int _wanted_ch)
1371 { size_t len = strlen(s);
1372 if (len > 0)
1373 { const char wanted_ch = (char) _wanted_ch;
1374 s += len - 1;
1375 while (len-- > 0)
1376 { if (*s == wanted_ch) return(unconstify(s));
1377 s--;
1378 }
1379 }
1380 return(NULL);
1381 }
1382 #endif
1383
1384 #if !HAVE_STRSTR
nonbroken_strstr(const char * haystack,const char * needle)1385 char* nonbroken_strstr(const char* haystack, const char* needle)
1386 { const size_t hlen = strlen(haystack), nlen = strlen(needle);
1387 size_t h = 0, n = 0;
1388 do
1389 { if (haystack[h] == needle[n]) n++;
1390 else { h -= n; n = 0; }
1391 h++;
1392 } while ( (n < nlen) && (h < hlen) );
1393 if (n >= nlen) return(unconstify(haystack) + h - nlen - 1); /* found */
1394 else return(NULL);
1395 }
1396 #endif
1397