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