ARB
adsocket.cxx
Go to the documentation of this file.
1 // =============================================================== //
2 // //
3 // File : adsocket.cxx //
4 // Purpose : //
5 // //
6 // Institute of Microbiology (Technical University Munich) //
7 // http://www.arb-home.de/ //
8 // //
9 // =============================================================== //
10 
11 #include <unistd.h>
12 
13 #include <climits>
14 #include <cstdarg>
15 #include <cctype>
16 
17 #include <netdb.h>
18 #include <netinet/tcp.h>
19 #include <signal.h>
20 #include <sys/mman.h>
21 #include <sys/socket.h>
22 #include <sys/stat.h>
23 #include <sys/time.h>
24 #include <sys/un.h>
25 
26 #if defined(DARWIN)
27 # include <sys/sysctl.h>
28 #endif // DARWIN
29 
30 #include <arb_cs.h>
31 #include <arb_str.h>
32 #include <arb_strbuf.h>
33 #include <arb_file.h>
34 #include <arb_sleep.h>
35 #include <arb_pathlen.h>
36 
37 #include "gb_comm.h"
38 #include "gb_data.h"
39 #include "gb_localdata.h"
40 
41 #include <SigHandler.h>
42 
43 #include <algorithm>
44 #include <arb_misc.h>
45 #include <arb_defs.h>
46 
47 // ------------------------------------------------
48 // private read and write socket functions
49 
53 }
54 
55 static long gbcm_read_buffered(int socket, char *ptr, long size) {
56  /* write_ptr ptr to not read data
57  write_free = write_bufsize-size of non read data;
58  */
59  long holding;
61  if (holding <= 0) {
62  holding = read(socket, gb_local->write_buffer, (size_t)gb_local->write_bufsize);
63 
64  if (holding < 0) {
65  fprintf(stderr, "Cannot read data from client: len=%li (%s, errno %i)\n",
66  holding, strerror(errno), errno);
67  return 0;
68  }
70  gb_local->write_free-=holding;
71  }
72  if (size>holding) size = holding;
73  memcpy(ptr, gb_local->write_ptr, (int)size);
74  gb_local->write_ptr += size;
75  gb_local->write_free += size;
76  return size;
77 }
78 
79 long gbcm_read(int socket, char *ptr, long size) {
80  long leftsize = size;
81  while (leftsize) {
82  long readsize = gbcm_read_buffered(socket, ptr, leftsize);
83  if (readsize<=0) return 0;
84  ptr += readsize;
85  leftsize -= readsize;
86  }
87 
88  return size;
89 }
90 
92  long leftsize = gb_local->write_ptr - gb_local->write_buffer;
93  ssize_t writesize;
94  char *ptr = gb_local->write_buffer;
95 
96  // once we're done, the buffer will be free
99 
100  while (leftsize) {
101 #ifdef MSG_NOSIGNAL
102  // Linux has MSG_NOSIGNAL, but not SO_NOSIGPIPE
103  // prevent SIGPIPE here
104  writesize = send(socket, ptr, leftsize, MSG_NOSIGNAL);
105 #else
106  writesize = write(socket, ptr, leftsize);
107 #endif
108 
109  if (writesize<0) {
110  if (gb_local->iamclient) {
111  fprintf(stderr,
112  "Client (pid=%i) terminating after failure to contact database (%s).",
113  getpid(), strerror(errno));
114  exit(EXIT_SUCCESS);
115  }
116  else {
117  fprintf(stderr, "Error sending data to client (%s).", strerror(errno));
118  return GBCM_SERVER_FAULT;
119  }
120  }
121  ptr += writesize;
122  leftsize -= writesize;
123  }
124 
125  return GBCM_SERVER_OK;
126 }
127 
128 GBCM_ServerResult gbcm_write(int socket, const char *ptr, long size) {
129  while (size >= gb_local->write_free) {
130  memcpy(gb_local->write_ptr, ptr, (int)gb_local->write_free);
132  size -= gb_local->write_free;
133  ptr += gb_local->write_free;
134 
135  gb_local->write_free = 0;
136  if (gbcm_write_flush(socket)) return GBCM_SERVER_FAULT;
137  }
138  memcpy(gb_local->write_ptr, ptr, (int)size);
139  gb_local->write_ptr += size;
140  gb_local->write_free -= size;
141  return GBCM_SERVER_OK;
142 }
143 
144 GB_ERROR gbcm_open_socket(const char *path, bool do_connect, int *psocket, char **unix_name) {
145  if (path && strcmp(path, ":") == 0) {
146  path = GBS_read_arb_tcp("ARB_DB_SERVER");
147  if (!path) {
148  return GB_await_error();
149  }
150  }
151 
152  return arb_open_socket(path, do_connect, psocket, unix_name);
153 }
154 
155 #if 0
156 // never used code
157 // (similar is done in GBCMS_shutdown, called e.g. from nt_disconnect_from_db)
158 long gbcms_close(gbcmc_comm *link) {
159  if (link->socket) {
160  close(link->socket);
161  link->socket = 0;
162  if (link->unix_name) {
163  unlink(link->unix_name);
164  }
165  }
166  return 0;
167 }
168 #endif
169 
170 gbcmc_comm *gbcmc_open(const char *path) {
171  gbcmc_comm *link = ARB_calloc<gbcmc_comm>(1);
172  GB_ERROR err = gbcm_open_socket(path, true, &link->socket, &link->unix_name);
173 
174  if (err) {
175  if (link->unix_name) free(link->unix_name); // @@@
176  free(link);
177  if (*err) {
178  GB_internal_errorf("ARB_DB_CLIENT_OPEN\n(Reason: %s)", err);
179  }
180  return NULp;
181  }
182  gb_local->iamclient = true;
183  return link;
184 }
185 
186 long gbcm_write_two(int socket, long a, long c) {
187  long ia[3];
188  ia[0] = a;
189  ia[1] = 3;
190  ia[2] = c;
191  if (!socket) return 1;
192  return gbcm_write(socket, (const char *)ia, sizeof(long)*3);
193 }
194 
195 
196 GBCM_ServerResult gbcm_read_two(int socket, long a, long *b, long *c) {
203  long ia[3];
204  long size;
205  size = gbcm_read(socket, (char *)&(ia[0]), sizeof(long)*3);
206  if (size != sizeof(long) * 3) {
207  GB_internal_errorf("receive failed: %zu bytes expected, %li got, keyword %lX",
208  sizeof(long) * 3, size, a);
209  return GBCM_SERVER_FAULT;
210  }
211  if (ia[0] != a) {
212  GB_internal_errorf("received keyword failed %lx != %lx\n", ia[0], a);
213  return GBCM_SERVER_FAULT;
214  }
215  if (b) {
216  *b = ia[1];
217  }
218  else {
219  if (ia[1]!=3) {
220  GB_internal_error("receive failed: size not 3\n");
221  return GBCM_SERVER_FAULT;
222  }
223  }
224  *c = ia[2];
225  return GBCM_SERVER_OK;
226 }
227 
228 GBCM_ServerResult gbcm_write_string(int socket, const char *key) {
229  if (key) {
230  size_t len = strlen(key);
231  gbcm_write_long(socket, len);
232  if (len) gbcm_write(socket, key, len);
233  }
234  else {
235  gbcm_write_long(socket, -1);
236  }
237  return GBCM_SERVER_OK;
238 }
239 
240 char *gbcm_read_string(int socket) {
241  char *key;
242  long len = gbcm_read_long(socket);
243 
244  if (len) {
245  if (len>0) {
246  ARB_calloc(key, len+1);
247  gbcm_read(socket, key, len);
248  }
249  else {
250  key = NULp;
251  }
252  }
253  else {
254  key = ARB_strdup("");
255  }
256 
257  return key;
258 }
259 
260 GBCM_ServerResult gbcm_write_long(int socket, long data) {
261  gbcm_write(socket, (char*)&data, sizeof(data));
262  return GBCM_SERVER_OK;
263 }
264 
265 long gbcm_read_long(int socket) {
266  long data;
267  gbcm_read(socket, (char*)&data, sizeof(data));
268  return data;
269 }
270 
271 char *GB_read_fp(FILE *in) {
278  GBS_strstruct buf(4096);
279 
280  int c;
281  while (EOF != (c = getc(in))) {
282  buf.put(c);
283  }
284  return buf.release_memfriendly();
285 }
286 
287 char *GB_read_file(const char *path) { // consider using class FileContent instead
294  char *result = NULp;
295 
296  if (strcmp(path, "-") == 0) {
297  result = GB_read_fp(stdin);
298  }
299  else {
300  char *epath = GBS_eval_env(path);
301 
302  if (epath) {
303  FILE *in = fopen(epath, "rt");
304 
305  if (!in) GB_export_error(GB_IO_error("reading", epath));
306  else {
307  long data_size = GB_size_of_file(epath);
308 
309  if (data_size >= 0) {
310  result = ARB_alloc<char>(data_size+1);
311 
312  data_size = fread(result, 1, data_size, in);
313  result[data_size] = 0;
314  }
315  fclose(in);
316  }
317  }
318  free(epath);
319  }
320  return result;
321 }
322 
323 char *GB_map_FILE(FILE *in, int writeable) {
324  int fi = fileno(in);
325  size_t size = GB_size_of_FILE(in);
326  char *buffer;
327  if (size<=0) {
328  GB_export_error("GB_map_file: sorry file not found");
329  return NULp;
330  }
331  if (writeable) {
332  buffer = (char*)mmap(NULp, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fi, 0);
333  }
334  else {
335  buffer = (char*)mmap(NULp, size, PROT_READ, MAP_SHARED, fi, 0);
336  }
337  if (buffer == MAP_FAILED) {
338  GB_export_errorf("GB_map_file: Error: Out of Memory: mmap failed (errno: %i)", errno);
339  return NULp;
340  }
341  return buffer;
342 }
343 
344 char *GB_map_file(const char *path, int writeable) {
345  FILE *in;
346  char *buffer;
347  in = fopen(path, "r");
348  if (!in) {
349  GB_export_errorf("GB_map_file: sorry file '%s' not readable", path);
350  return NULp;
351  }
352  buffer = GB_map_FILE(in, writeable);
353  fclose(in);
354  return buffer;
355 }
356 
358  timeval tp;
359  if (gettimeofday(&tp, NULp)) return 0;
360  return tp.tv_sec;
361 }
362 
363 GB_ERROR GB_textprint(const char *path) {
364  // goes to header: __ATTR__USERESULT
365  char *fpath = GBS_eval_env(path);
366  char *quoted_fpath = GBK_singlequote(fpath);
367  const char *command = GBS_global_string("arb_textprint %s &", quoted_fpath);
368  GB_ERROR error = GBK_system(command);
369  error = GB_failedTo_error("print textfile", fpath, error);
370  free(quoted_fpath);
371  free(fpath);
372  return error;
373 }
374 
375 // --------------------------------------------------------------------------------
376 
378  static const char *path = NULp;
379  if (!path) {
380  path = ARB_getenv_ignore_empty("PATH");
381  if (!path) {
382  path = GBS_eval_env("/bin:/usr/bin:$(ARBHOME)/bin");
383  GB_informationf("Your PATH variable is empty - using '%s' as search path.", path);
384  }
385  else {
386  char *arbbin = GBS_eval_env("$(ARBHOME)/bin");
387  if (!strstr(path, arbbin)) {
388  GB_warningf("Your PATH variable does not contain '%s'. Things may not work as expected.", arbbin);
389  }
390  free(arbbin);
391  }
392  }
393  return path;
394 }
395 
396 // --------------------------------------------------------------------------------
397 // Functions to find an executable
398 
399 static char *GB_find_executable(GB_CSTR description_of_executable, ...) {
400  // goes to header: __ATTR__SENTINEL
401  /* search the path for an executable with any of the given names (...)
402  * if any is found, it's full path is returned
403  * if none is found, a warning call is returned (which can be executed without harm)
404  */
405 
406  GB_CSTR name;
407  char *found = NULp;
408  va_list args;
409 
410  va_start(args, description_of_executable);
411  while (!found && (name = va_arg(args, GB_CSTR))) found = ARB_executable(name, GB_getenvPATH());
412  va_end(args);
413 
414  if (!found) { // none of the executables has been found
415  char *looked_for;
416  char *msg;
417  {
418  GBS_strstruct buf(100);
419  bool first = true;
420 
421  va_start(args, description_of_executable);
422  while ((name = va_arg(args, GB_CSTR))) {
423  if (!first) buf.cat(", ");
424  first = false;
425  buf.cat(name);
426  }
427  va_end(args);
428  looked_for = buf.release();
429  }
430 
431  msg = GBS_global_string_copy("Could not find a %s (looked for: %s)", description_of_executable, looked_for);
432  GB_warning(msg);
433  found = GBS_global_string_copy("echo \"%s\" ; arb_ign Parameters", msg);
434  free(msg);
435  free(looked_for);
436  }
437  else {
439  char *description = GBS_global_string_copy("the %s derived from environment variable PATH", description_of_executable);
440  ARB_warn_about_unwanted_chars(found, description);
441  free(description);
442  }
443  GB_informationf("Using %s '%s' ('%s')", description_of_executable, name, found);
444  }
445  return found;
446 }
447 
448 // --------------------------------------------------------------------------------
449 // Functions to access the environment variables used by ARB:
450 
451 static char *getenv_executable(GB_CSTR envvar) {
452  // get full path of executable defined by 'envvar'
453  // returns 0 if
454  // - envvar not defined or
455  // - not defining an executable (warns about that)
456 
457  char *result = NULp;
458  const char *exe_name = ARB_getenv_ignore_empty(envvar);
459 
460  if (exe_name) {
461 #if defined(DARWIN)
462  // On MacOS, the open command can be used to open files with MacOS
463  // applications. The open command fails the ARB executable test. If no
464  // application is passed to the open command, the default application
465  // for the file type is used or the user is presented with a dialog to
466  // chose an application if no default application is used. This is very
467  // hard to test from within ARB. Therefore, commands that start with
468  // open are considered to be valid applications and the executable test
469  // is not run.
470  if (!strncasecmp(exe_name, "open", 4)) {
471  return ARB_strdup(exe_name);
472  }
473 #endif
474 
475  result = ARB_executable(exe_name, GB_getenvPATH());
476  if (!result) {
477  GB_warningf("Environment variable '%s' contains '%s' (which is not an executable)", envvar, exe_name);
478  }
479 
480  if (ARB_path_contains_unwanted_chars(result)) {
481  char *description = GBS_global_string_copy("the executable derived from environment variables '%s' and PATH", envvar);
482  ARB_warn_about_unwanted_chars(result, description);
483  free(description);
484  }
485  }
486 
487  return result;
488 }
489 
490 static char *getenv_existing_directory(GB_CSTR envvar) {
491  // get full path of directory defined by 'envvar'
492  // return 0 if
493  // - envvar is not defined or
494  // - does not point to a directory (warns about that)
495 
496  char *result = NULp;
497  const char *dir_name = ARB_getenv_ignore_empty(envvar);
498 
499  if (dir_name) {
500  if (GB_is_directory(dir_name)) {
501  result = ARB_strdup(dir_name);
502  if (ARB_path_contains_unwanted_chars(result)) {
503  char *description = GBS_global_string_copy("the directory derived from environment variable '%s'", envvar);
504  ARB_warn_about_unwanted_chars(result, description);
505  free(description);
506  }
507  }
508  else {
509  GB_warningf("Environment variable '%s' should contain the path of an existing directory.\n"
510  "(current content '%s' has been ignored.)", envvar, dir_name);
511  }
512  }
513  return result;
514 }
515 
516 static void GB_setenv(const char *var, const char *value) {
517  if (setenv(var, value, 1) != 0) {
518  GB_warningf("Could not set environment variable '%s'. This might cause problems in subprocesses.\n"
519  "(Reason: %s)", var, strerror(errno));
520  }
521 }
522 
524  static const char *xterm = NULp;
525  if (!xterm) {
526  xterm = ARB_getenv_ignore_empty("ARB_XTERM"); // doc in ../HELP_SOURCE/source/arb_envar.hlp@ARB_XTERM
527  if (!xterm) xterm = "xterm -sl 1000 -sb -geometry 150x60";
528  }
529  return xterm;
530 }
531 
533  static const char *xcmd = NULp;
534  if (!xcmd) {
535  xcmd = ARB_getenv_ignore_empty("ARB_XCMD"); // doc in ../HELP_SOURCE/source/arb_envar.hlp@ARB_XCMD
536  if (!xcmd) {
537  const char *xterm = GB_getenvARB_XTERM();
538  gb_assert(xterm);
539  xcmd = GBS_global_string_copy("%s -e", xterm);
540  }
541  }
542  return xcmd;
543 }
544 
546  static const char *user = NULp;
547  if (!user) {
548  user = ARB_getenv_ignore_empty("USER");
549  if (!user) user = ARB_getenv_ignore_empty("LOGNAME");
550  if (!user) {
551  user = ARB_getenv_ignore_empty("HOME");
552  if (user && strrchr(user, '/')) user = strrchr(user, '/')+1;
553  }
554  if (!user) {
555  fprintf(stderr, "WARNING: Cannot identify user: environment variables USER, LOGNAME and HOME not set\n");
556  user = "UnknownUser";
557  }
558  }
559  return user;
560 }
561 
562 
564  static SmartCharPtr Home;
565  if (Home.isNull()) {
566  char *home = getenv_existing_directory("HOME");
567  if (!home) {
568  home = nulldup(GB_getcwd());
569  if (!home) home = ARB_strdup(".");
570  fprintf(stderr, "WARNING: Cannot identify user's home directory: environment variable HOME not set\n"
571  "Using current directory (%s) as home.\n", home);
572  }
573  gb_assert(home);
574  Home = home;
575  }
576  return &*Home;
577 }
578 
580  static SmartCharPtr Arbhome;
581  if (Arbhome.isNull()) {
582  char *arbhome = getenv_existing_directory("ARBHOME"); // doc in ../HELP_SOURCE/source/arb_envar.hlp@ARBHOME
583  if (!arbhome) {
584  fprintf(stderr, "Fatal ERROR: Environment Variable ARBHOME not found !!!\n"
585  " Please set 'ARBHOME' to the installation path of ARB\n");
586  exit(EXIT_FAILURE);
587  }
588  Arbhome = arbhome;
589  }
590  return &*Arbhome;
591 }
592 
594  static const char *am = NULp;
595  if (!am) {
596  am = getenv_existing_directory("ARBMACRO"); // doc in ../HELP_SOURCE/source/arb_envar.hlp@ARBMACRO
597  if (!am) am = ARB_strdup(GB_path_in_ARBLIB("macros"));
598  }
599  return am;
600 }
601 
602 static char *getenv_autodirectory(const char *envvar, const char *defaultDirectory) {
603  // if environment variable 'envvar' contains an existing directory -> use that
604  // otherwise fallback to 'defaultDirectory' (create if not existing)
605  // return heap-copy of full directory name
606  char *dir = getenv_existing_directory(envvar);
607  if (!dir) {
608  dir = GBS_eval_env(defaultDirectory);
610  char *description = GBS_global_string_copy("the default directory expanded from expression '%s'", defaultDirectory);
611  ARB_warn_about_unwanted_chars(dir, description);
612  free(description);
613  }
614  if (!GB_is_directory(dir)) {
616  if (error) GB_warning(error);
617  }
618  }
619  return dir;
620 }
621 
623  static SmartCharPtr ArbProps;
624  if (ArbProps.isNull()) ArbProps = getenv_autodirectory("ARB_PROP", GB_path_in_HOME(".arb_prop")); // doc in ../HELP_SOURCE/source/arb_envar.hlp@ARB_PROP
625  return &*ArbProps;
626 }
627 
629  static SmartCharPtr ArbMacroHome;
630  if (ArbMacroHome.isNull()) ArbMacroHome = getenv_autodirectory("ARBMACROHOME", GB_path_in_arbprop("macros")); // doc in ../HELP_SOURCE/source/arb_envar.hlp@ARBMACROHOME
631  return &*ArbMacroHome;
632 }
633 
635  static SmartCharPtr ArbConfig;
636  if (ArbConfig.isNull()) ArbConfig = getenv_autodirectory("ARBCONFIG", GB_path_in_arbprop("cfgSave")); // doc in ../HELP_SOURCE/source/arb_envar.hlp@ARBCONFIG
637  return &*ArbConfig;
638 }
639 
641  static const char *gs = NULp;
642  if (!gs) {
643  gs = getenv_executable("ARB_GS"); // doc in ../HELP_SOURCE/source/arb_envar.hlp@ARB_GS
644  if (!gs) {
645 #if defined(DARWIN)
646  // use default application for PS files on MacOS if envvar is not
647  // set - defaults to Apple Preview application
648  // -W waits until the user has deleted the application this is
649  // required
650  gs = ARB_strdup("open -W");
651 #else
652  gs = GB_find_executable("Postscript viewer", "xreader", "gv", "ghostview", NULp);
653 #endif
654  }
655  }
656  return gs;
657 }
658 
660  static const char *pdfview = NULp;
661  if (!pdfview) {
662  pdfview = getenv_executable("ARB_PDFVIEW"); // doc in ../HELP_SOURCE/source/arb_envar.hlp@ARB_PDFVIEW
663  if (!pdfview) {
664 #if defined(DARWIN)
665  // use default application for PDF files on MacOS if envvar is not
666  // set - defaults to Apple Preview application
667  pdfview = ARB_strdup("open -W");
668 #else
669  pdfview = GB_find_executable("PDF viewer", "xreader", "epdfview", "xpdf", "kpdf", "acroread", "gv", NULp);
670 #endif
671  }
672  }
673  return pdfview;
674 }
675 
677  static const char *dp = NULp;
678  if (!dp) {
679  char *res = getenv_existing_directory("ARB_DOC"); // doc in ../HELP_SOURCE/source/arb_envar.hlp@ARB_DOC
680  if (res) dp = res;
681  else dp = ARB_strdup(GB_path_in_ARBLIB("help"));
682  }
683  return dp;
684 }
685 
687  static const char *dp = NULp;
688  if (!dp) {
689  char *res = getenv_existing_directory("ARB_HTMLDOC"); // doc in ../HELP_SOURCE/source/arb_envar.hlp@ARB_HTMLDOC
690  if (res) dp = res;
691  else dp = ARB_strdup(GB_path_in_ARBLIB("help_html"));
692  }
693  return dp;
694 
695 }
696 
698 
700  // Install 'hook' to be called by GB_getenv().
701  // If the 'hook' returns NULp, normal expansion takes place.
702  // Otherwise GB_getenv() returns result from 'hook'
703 
704  gb_getenv_hook oldHook = getenv_hook;
705  getenv_hook = hook;
706  return oldHook;
707 }
708 
709 GB_CSTR GB_getenv(const char *env) {
710  if (getenv_hook) {
711  const char *result = getenv_hook(env);
712  if (result) return result;
713  }
714  if (strncmp(env, "ARB", 3) == 0) {
715  // doc in ../HELP_SOURCE/source/arb_envar.hlp
716 
717  if (strcmp(env, "ARBHOME") == 0) return GB_getenvARBHOME();
718  if (strcmp(env, "ARB_PROP") == 0) return GB_getenvARB_PROP();
719  if (strcmp(env, "ARBCONFIG") == 0) return GB_getenvARBCONFIG();
720  if (strcmp(env, "ARBMACROHOME") == 0) return GB_getenvARBMACROHOME();
721  if (strcmp(env, "ARBMACRO") == 0) return GB_getenvARBMACRO();
722 
723  if (strcmp(env, "ARB_GS") == 0) return GB_getenvARB_GS();
724  if (strcmp(env, "ARB_PDFVIEW") == 0) return GB_getenvARB_PDFVIEW();
725  if (strcmp(env, "ARB_DOC") == 0) return GB_getenvDOCPATH();
726  if (strcmp(env, "ARB_XTERM") == 0) return GB_getenvARB_XTERM();
727  if (strcmp(env, "ARB_XCMD") == 0) return GB_getenvARB_XCMD();
728  }
729  else {
730  if (strcmp(env, "HOME") == 0) return GB_getenvHOME();
731  if (strcmp(env, "USER") == 0) return GB_getenvUSER();
732  }
733 
734  return ARB_getenv_ignore_empty(env);
735 }
736 
739  // set all variables needed in ARB subprocesses
740  GB_setenv("ARB_XCMD", GB_getenvARB_XCMD());
741  }
742 };
743 
745 
746 bool GB_host_is_local(const char *hostname) {
747  // returns true if host is local
748 
749  arb_assert(hostname);
750  arb_assert(hostname[0]);
751 
752  return
753  ARB_stricmp(hostname, "localhost") == 0 ||
754  ARB_strBeginsWith(hostname, "127.0.0.") ||
755  ARB_stricmp(hostname, arb_gethostname()) == 0;
756 }
757 
759  // Returns the physical available memory size in k available for one process
760  static GB_ULONG physical_memsize = 0;
761  if (!physical_memsize) {
762  GB_ULONG memsize; // real existing memory in k
763 #if defined(LINUX)
764  {
765  long pagesize = sysconf(_SC_PAGESIZE);
766  long pages = sysconf(_SC_PHYS_PAGES);
767 
768  memsize = (pagesize/1024) * pages;
769  }
770 #elif defined(DARWIN)
771 #warning memsize detection needs to be tested for Darwin
772  {
773  int mib[2];
774  uint64_t bytes;
775  size_t len;
776 
777  mib[0] = CTL_HW;
778  mib[1] = HW_MEMSIZE; // uint64_t: physical ram size
779  len = sizeof(bytes);
780  sysctl(mib, 2, &bytes, &len, NULp, 0);
781 
782  memsize = bytes/1024;
783  }
784 #else
785  memsize = 1024*1024; // assume 1 Gb
786  printf("\n"
787  "Warning: ARB is not prepared to detect the memory size on your system!\n"
788  " (it assumes you have %ul Mb, but does not use more)\n\n", memsize/1024);
789 #endif
790 
791  GB_ULONG net_memsize = memsize - 10240; // reduce by 10Mb
792 
793  // detect max allocateable memory by ... allocating
794  GB_ULONG max_malloc_try = net_memsize*1024;
795  GB_ULONG max_malloc = 0;
796  {
797  GB_ULONG step_size = 4096;
798  void *head = NULp;
799 
800  do {
801  void **tmp;
802  while ((tmp=(void**)malloc(step_size))) { // do NOT use ARB_alloc here!
803  *tmp = head;
804  head = tmp;
805  max_malloc += step_size;
806  if (max_malloc >= max_malloc_try) break;
807  step_size *= 2;
808  }
809  } while ((step_size=step_size/2) > sizeof(void*));
810 
811  while (head) freeset(head, *(void**)head);
812  max_malloc /= 1024;
813  }
814 
815  physical_memsize = std::min(net_memsize, max_malloc);
816 
817 #if defined(DEBUG) && 0
818  printf("- memsize(real) = %20lu k\n", memsize);
819  printf("- memsize(net) = %20lu k\n", net_memsize);
820  printf("- memsize(max_malloc) = %20lu k\n", max_malloc);
821 #endif // DEBUG
822 
823  GB_informationf("Visible memory: %s", GBS_readable_size(physical_memsize*1024, "b"));
824  }
825 
826  arb_assert(physical_memsize>0);
827  return physical_memsize;
828 }
829 
830 static GB_ULONG parse_env_mem_definition(const char *env_override, GB_ERROR& error) {
831  const char *end;
832  GB_ULONG num = strtoul(env_override, const_cast<char**>(&end), 10);
833 
834  error = NULp;
835 
836  bool valid = num>0 || env_override[0] == '0';
837  if (valid) {
838  const char *formatSpec = end;
839  double factor = 1;
840 
841  switch (tolower(formatSpec[0])) {
842  case 0:
843  num = GB_ULONG(num/1024.0+0.5); // byte->kb
844  break; // no format given
845 
846  case 'g': factor *= 1024; FALLTHROUGH;
847  case 'm': factor *= 1024; FALLTHROUGH;
848  case 'k': break;
849 
850  case '%':
851  factor = num/100.0;
852  num = get_physical_memory();
853  break;
854 
855  default: valid = false; break;
856  }
857 
858  if (valid) return GB_ULONG(num*factor+0.5);
859  }
860 
861  error = "expected digits (optionally followed by k, M, G or %)";
862  return 0;
863 }
864 
866  // memory allowed to be used by a single ARB process (in kbyte)
867 
868  static GB_ULONG useable_memory = 0;
869  if (!useable_memory) {
870  bool allow_fallback = true;
871  const char *env_override = GB_getenv("ARB_MEMORY");
872  const char *via_whom;
873  if (env_override) {
874  via_whom = "via envar ARB_MEMORY";
875  }
876  else {
877  FALLBACK:
878  env_override = "90%"; // ARB processes do not use more than 90% of physical memory
879  via_whom = "by internal default";
880  allow_fallback = false;
881  }
882 
883  gb_assert(env_override);
884 
885  GB_ERROR env_error;
886  GB_ULONG env_memory = parse_env_mem_definition(env_override, env_error);
887  if (env_error) {
888  GB_warningf("Ignoring invalid setting '%s' %s (%s)", env_override, via_whom, env_error);
889  if (allow_fallback) goto FALLBACK;
890  GBK_terminate("failed to detect usable memory");
891  }
892 
893  GB_informationf("Restricting used memory (%s '%s') to %s", via_whom, env_override, GBS_readable_size(env_memory*1024, "b"));
894  if (!allow_fallback) {
895  GB_informationf("Note: Setting envar ARB_MEMORY will override that restriction (percentage or absolute memsize)");
896  }
897  useable_memory = env_memory;
898  gb_assert(useable_memory>0 && useable_memory<get_physical_memory());
899  }
900  return useable_memory;
901 }
902 
903 // ---------------------------
904 // external commands
905 
906 NOT4PERL GB_ERROR GB_xcmd(const char *cmd, XCMD_TYPE exectype) {
907  // goes to header: __ATTR__USERESULT_TODO
908 
909  // runs a command in an xterm
910 
911  bool background = exectype & _XCMD__ASYNC; // true -> run asynchronous
912  bool wait_only_if_error = !(exectype & _XCMD__WAITKEY); // true -> asynchronous does wait for keypress only if cmd fails
913 
914  gb_assert(exectype != XCMD_SYNC_WAITKEY); // @@@ previously unused; check how it works and whether that makes sense; fix!
915 
916  const int BUFSIZE = 1024;
917  GBS_strstruct system_call(BUFSIZE);
918 
919  const char *xcmd = GB_getenvARB_XCMD();
920 
921  system_call.put('(');
922  system_call.cat(xcmd);
923 
924  {
925  GBS_strstruct bash_command(BUFSIZE);
926 
927  GB_CSTR ldlibpath = GB_getenv("LD_LIBRARY_PATH"); // due to SIP ldlibpath is always NULL under osx
928  // (should not matter because all binaries are linked statically)
929 
930  if (ldlibpath) {
931  bash_command.cat("LD_LIBRARY_PATH=");
932  {
933  char *dquoted_library_path = GBK_doublequote(ldlibpath);
934  bash_command.cat(dquoted_library_path);
935  free(dquoted_library_path);
936  }
937  bash_command.cat(";export LD_LIBRARY_PATH;");
938  }
939 #if !defined(DARWIN)
940  else {
941  // under Linux it normally is a problem if LD_LIBRARY_PATH is undefined
942  // -> warn in terminal to provide a hint:
943  bash_command.cat("echo 'Warning: LD_LIBRARY_PATH is undefined';");
944  }
945 #endif
946 
947  bash_command.cat(" (");
948  bash_command.cat(cmd);
949 
950  const char *wait_commands = "echo; echo Press ENTER to close this window; read a";
951  if (wait_only_if_error) {
952  bash_command.cat(") || (");
953  bash_command.cat(wait_commands);
954  }
955  else if (background) {
956  bash_command.cat("; ");
957  bash_command.cat(wait_commands);
958  }
959  bash_command.put(')');
960 
961  system_call.cat(" bash -c ");
962  char *squoted_bash_command = GBK_singlequote(bash_command.get_data());
963  system_call.cat(squoted_bash_command);
964  free(squoted_bash_command);
965  }
966  system_call.cat(" )");
967  if (background) system_call.cat(" &");
968 
969  return GBK_system(system_call.get_data());
970 }
971 
972 // ---------------------------------------------
973 // path completion (parts former located in AWT)
974 // @@@ whole section (+ corresponding tests) should move to adfile.cxx
975 
976 static int path_toggle = 0;
977 static char path_buf[2][ARB_PATH_MAX];
978 
979 static char *use_other_path_buf() {
980  path_toggle = 1-path_toggle;
981  return path_buf[path_toggle];
982 }
983 
984 GB_CSTR GB_append_suffix(const char *name, const char *suffix) {
985  // if suffix isnt NULp -> append .suffix
986  // (automatically removes duplicated '.'s)
987  //
988  // Note: see also GB_split_full_path
989 
990  GB_CSTR result = name;
991  if (suffix) {
992  while (suffix[0] == '.') suffix++;
993  if (suffix[0]) {
994  result = GBS_global_string_to_buffer(use_other_path_buf(), ARB_PATH_MAX, "%s.%s", name, suffix);
995  }
996  }
997  return result;
998 }
999 
1000 GB_CSTR GB_canonical_path(const char *anypath) {
1001  // expands '~' '..' symbolic links etc in 'anypath'.
1002  //
1003  // Never returns NULp (if called correctly)
1004  // Instead might return non-canonical path (when a directory
1005  // in 'anypath' does not exist)
1006 
1007  GB_CSTR result = NULp;
1008  if (!anypath) {
1009  GB_export_error("NULp path (internal error)");
1010  }
1011  else if (!anypath[0]) {
1012  result = "/";
1013  }
1014  else if (strlen(anypath) >= ARB_PATH_MAX) {
1015  GB_export_errorf("Path too long (> %i chars)", ARB_PATH_MAX-1);
1016  }
1017  else {
1018  if (anypath[0] == '~' && (!anypath[1] || anypath[1] == '/')) {
1019  GB_CSTR home = GB_getenvHOME();
1020  GB_CSTR homeexp = GBS_global_string("%s%s", home, anypath+1);
1021  result = GB_canonical_path(homeexp);
1022  GBS_reuse_buffer(homeexp);
1023  }
1024  else {
1025  result = realpath(anypath, path_buf[1-path_toggle]);
1026  if (result) {
1027  path_toggle = 1-path_toggle;
1028  }
1029  else { // realpath failed (happens e.g. when using a non-existing path, e.g. if user entered the name of a new file)
1030  // => content of path_buf[path_toggle] is UNDEFINED!
1031  char *dir, *fullname;
1032  GB_split_full_path(anypath, &dir, &fullname, NULp, NULp);
1033 
1034  const char *canonical_dir = NULp;
1035  if (!dir) {
1036  gb_assert(!strchr(anypath, '/'));
1037  canonical_dir = GB_canonical_path("."); // use working directory
1038  }
1039  else {
1040  gb_assert(strcmp(dir, anypath) != 0); // avoid deadlock
1041  canonical_dir = GB_canonical_path(dir);
1042  }
1043  gb_assert(canonical_dir);
1044 
1045  // manually resolve '.' and '..' in non-existing parent directories
1046  if (strcmp(fullname, "..") == 0) {
1047  char *parent;
1048  GB_split_full_path(canonical_dir, &parent, NULp, NULp, NULp);
1049  if (parent) {
1050  result = strcpy(use_other_path_buf(), parent);
1051  free(parent);
1052  }
1053  }
1054  else if (strcmp(fullname, ".") == 0) {
1055  result = canonical_dir;
1056  }
1057 
1058  if (!result) result = GB_concat_path(canonical_dir, fullname);
1059 
1060  free(dir);
1061  free(fullname);
1062  }
1063  }
1064  gb_assert(result);
1065  }
1066  return result;
1067 }
1068 
1069 GB_CSTR GB_concat_path(GB_CSTR anypath_left, GB_CSTR anypath_right) {
1070  // concats left and right part of a path.
1071  // '/' is inserted in-between
1072  //
1073  // if one of the arguments is NULp => returns the other argument
1074  // if both arguments are NULp => return NULp (@@@ maybe forbid?)
1075  //
1076  // Note: see also GB_split_full_path
1077 
1078  GB_CSTR result = NULp;
1079 
1080  if (anypath_right) {
1081  if (anypath_right[0] == '/') {
1082  result = GB_concat_path(anypath_left, anypath_right+1);
1083  }
1084  else if (anypath_left && anypath_left[0]) {
1085  if (anypath_left[strlen(anypath_left)-1] == '/') {
1086  result = GBS_global_string_to_buffer(use_other_path_buf(), sizeof(path_buf[0]), "%s%s", anypath_left, anypath_right);
1087  }
1088  else {
1089  result = GBS_global_string_to_buffer(use_other_path_buf(), sizeof(path_buf[0]), "%s/%s", anypath_left, anypath_right);
1090  }
1091  }
1092  else {
1093  result = anypath_right;
1094  }
1095  }
1096  else {
1097  result = anypath_left;
1098  }
1099 
1100  return result;
1101 }
1102 
1103 GB_CSTR GB_concat_full_path(const char *anypath_left, const char *anypath_right) {
1104  // like GB_concat_path(), but returns the canonical path
1105  GB_CSTR result = GB_concat_path(anypath_left, anypath_right);
1106 
1107  gb_assert(result != anypath_left); // consider using GB_canonical_path() directly
1108  gb_assert(result != anypath_right);
1109 
1110  if (result) result = GB_canonical_path(result);
1111  return result;
1112 }
1113 
1114 inline bool is_absolute_path(const char *path) { return path[0] == '/' || path[0] == '~'; }
1115 inline bool is_name_of_envvar(const char *name) {
1116  for (int i = 0; name[i]; ++i) {
1117  if (isalnum(name[i]) || name[i] == '_') continue;
1118  return false;
1119  }
1120  return true;
1121 }
1122 
1123 GB_CSTR GB_unfold_in_directory(const char *relative_directory, const char *path) {
1124  // If 'path' is an absolute path, return canonical path.
1125  //
1126  // Otherwise unfolds relative 'path' using 'relative_directory' as start directory.
1127 
1128  if (is_absolute_path(path)) return GB_canonical_path(path);
1129  return GB_concat_full_path(relative_directory, path);
1130 }
1131 
1132 GB_CSTR GB_unfold_path(const char *pwd_envar, const char *path) {
1133  // If 'path' is an absolute path, return canonical path.
1134  //
1135  // Otherwise unfolds relative 'path' using content of environment
1136  // variable 'pwd_envar' as start directory.
1137  // If environment variable is not defined, fall-back to current directory
1138 
1139  gb_assert(is_name_of_envvar(pwd_envar));
1140  if (is_absolute_path(path)) {
1141  return GB_canonical_path(path);
1142  }
1143 
1144  const char *pwd = GB_getenv(pwd_envar);
1145  if (!pwd) pwd = GB_getcwd(); // @@@ really wanted ?
1146  return GB_concat_full_path(pwd, path);
1147 }
1148 
1149 GB_CSTR GB_path_in_ARBHOME(const char *relative_path) {
1150  return GB_unfold_path("ARBHOME", relative_path);
1151 }
1152 static GB_CSTR GB_concat_path_in_ARBHOME(const char *relative_path_left, const char *anypath_right) {
1153  return GB_path_in_ARBHOME(GB_concat_path(relative_path_left, anypath_right));
1154 }
1155 
1156 GB_CSTR GB_path_in_ARBLIB(const char *relative_path) {
1157  return GB_concat_path_in_ARBHOME("lib", relative_path);
1158 }
1159 GB_CSTR GB_concat_path_in_ARBLIB(const char *relative_path_left, const char *anypath_right) {
1160  return GB_path_in_ARBLIB(GB_concat_path(relative_path_left, anypath_right));
1161 }
1162 
1163 GB_CSTR GB_path_in_HOME(const char *relative_path) {
1164  return GB_unfold_path("HOME", relative_path);
1165 }
1166 GB_CSTR GB_path_in_arbprop(const char *relative_path) {
1167  return GB_unfold_path("ARB_PROP", relative_path);
1168 }
1169 static GB_CSTR GB_path_in_arb_temp(const char *relative_path) {
1170  return GB_path_in_HOME(GB_concat_path(".arb_tmp", relative_path));
1171 }
1172 
1173 
1174 #define GB_PATH_TMP GB_path_in_arb_temp("tmp") // = "~/.arb_tmp/tmp" (used wherever '/tmp' was used in the past)
1175 
1176 FILE *GB_fopen_tempfile(const char *filename, const char *fmode, char **res_fullname) {
1177  // fopens a tempfile
1178  //
1179  // Returns
1180  // - NULp in case of error (which is exported then)
1181  // - otherwise returns open filehandle
1182  //
1183  // Always sets
1184  // - heap-copy of used filename in 'res_fullname' (if res_fullname isnt NULp)
1185  // (even if fopen failed)
1186 
1187  char *file = ARB_strdup(GB_concat_path(GB_PATH_TMP, filename));
1189  FILE *fp = NULp;
1190 
1191  if (!error) {
1192  bool write = strpbrk(fmode, "wa");
1193 
1194  fp = fopen(file, fmode);
1195  if (fp) {
1196  // make file private
1197  if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) != 0) {
1198  error = GB_IO_error("changing permissions of", file);
1199  }
1200  }
1201  else {
1202  error = GB_IO_error(GBS_global_string("opening(%s) tempfile", write ? "write" : "read"), file);
1203  }
1204 
1205  if (res_fullname) {
1206  *res_fullname = file ? ARB_strdup(file) : NULp;
1207  }
1208  }
1209 
1210  if (error) {
1211  // don't care if anything fails here..
1212  if (fp) { fclose(fp); fp = NULp; }
1213  if (file) unlink(file);
1214  GB_export_error(error);
1215  }
1216 
1217  free(file);
1218 
1219  return fp;
1220 }
1221 
1222 char *GB_create_tempfile(const char *name) {
1223  // creates a tempfile and returns full name of created file
1224  // returns NULp in case of error (which is exported then)
1225 
1226  char *fullname;
1227  FILE *out = GB_fopen_tempfile(name, "wt", &fullname);
1228 
1229  if (out) fclose(out);
1230  return fullname;
1231 }
1232 
1233 char *GB_unique_filename(const char *name_prefix, const char *suffix) {
1234  // generates a unique (enough) filename
1235  //
1236  // scheme: name_prefix_USER_PID_COUNT.suffix
1237 
1238  static int counter = 0;
1239  return GBS_global_string_copy("%s_%s_%i_%i.%s",
1240  name_prefix,
1241  GB_getenvUSER(), getpid(), counter++,
1242  suffix);
1243 }
1244 
1246 static void exit_remove_file(const char *file, long, void *) {
1247  if (unlink(file) != 0) {
1248  fprintf(stderr, "Warning: %s\n", GB_IO_error("removing", file));
1249  }
1250 }
1251 static void exit_removal() {
1252  if (files_to_remove_on_exit) {
1253  GBS_hash_do_const_loop(files_to_remove_on_exit, exit_remove_file, NULp);
1254  GBS_free_hash(files_to_remove_on_exit);
1255  files_to_remove_on_exit = NULp;
1256  }
1257 }
1258 void GB_remove_on_exit(const char *filename) {
1259  // mark a file for removal on exit
1260 
1261  if (!files_to_remove_on_exit) {
1262  files_to_remove_on_exit = GBS_create_hash(20, GB_MIND_CASE);
1264  }
1265  GBS_write_hash(files_to_remove_on_exit, filename, 1);
1266 }
1267 
1268 void GB_split_full_path(const char *fullpath, char **res_dir, char **res_fullname, char **res_name_only, char **res_suffix) {
1269  // Takes a file (or directory) name and splits it into "path/name.suffix".
1270  // If result pointers (res_*) are non-NULp, they'll get assigned heap-copies of the splitted parts:
1271  // "path" -> res_dir
1272  // "name.suffix" -> res_fullname
1273  // "name" -> res_name_only
1274  // "suffix" -> res_suffix
1275  //
1276  // If parts are not valid (e.g. cause 'fullpath' doesn't have a .suffix) the corresponding result pointer
1277  // is set to NULp.
1278  //
1279  // The '/' and '.' characters at the split-positions will be removed (not included in the results-strings).
1280  // Exceptions:
1281  // - the '.' in 'res_fullname'
1282  // - the '/' if directory part is the rootdir
1283  //
1284  // Note:
1285  // - if the filename starts with '.' (and that is the only '.' in the filename, an empty filename is returned: "")
1286  //
1287  // Reverse functionality is provided by GB_concat_path and GB_append_suffix.
1288 
1289  if (fullpath && fullpath[0]) {
1290  const char *lslash = strrchr(fullpath, '/');
1291  const char *name_start = lslash ? lslash+1 : fullpath;
1292  const char *ldot = strrchr(lslash ? lslash : fullpath, '.');
1293  const char *terminal = strchr(name_start, 0);
1294 
1295  gb_assert(terminal);
1296  gb_assert(name_start);
1297  gb_assert(terminal > fullpath); // ensure (terminal-1) is a valid character position in path
1298 
1299  if (!lslash && fullpath[0] == '.' && (fullpath[1] == 0 || (fullpath[1] == '.' && fullpath[2] == 0))) { // '.' and '..'
1300  if (res_dir) *res_dir = ARB_strdup(fullpath);
1301  if (res_fullname) *res_fullname = NULp;
1302  if (res_name_only) *res_name_only = NULp;
1303  if (res_suffix) *res_suffix = NULp;
1304  }
1305  else {
1306  if (res_dir) *res_dir = lslash ? ARB_strpartdup(fullpath, lslash == fullpath ? lslash : lslash-1) : NULp;
1307  if (res_fullname) *res_fullname = ARB_strpartdup(name_start, terminal-1);
1308  if (res_name_only) *res_name_only = ARB_strpartdup(name_start, ldot ? ldot-1 : terminal-1);
1309  if (res_suffix) *res_suffix = ldot ? ARB_strpartdup(ldot+1, terminal-1) : NULp;
1310  }
1311  }
1312  else {
1313  if (res_dir) *res_dir = NULp;
1314  if (res_fullname) *res_fullname = NULp;
1315  if (res_name_only) *res_name_only = NULp;
1316  if (res_suffix) *res_suffix = NULp;
1317  }
1318 }
1319 
1320 
1321 // --------------------------------------------------------------------------------
1322 
1323 #ifdef UNIT_TESTS
1324 
1325 #include <test_unit.h>
1326 
1327 #define TEST_EXPECT_IS_CANONICAL(file) \
1328  do { \
1329  char *dup = ARB_strdup(file); \
1330  TEST_EXPECT_EQUAL(GB_canonical_path(dup), dup); \
1331  free(dup); \
1332  } while(0)
1333 
1334 #define TEST_EXPECT_CANONICAL_TO(not_cano,cano) \
1335  do { \
1336  char *arb_not_cano = ARB_strdup(GB_concat_path(arbhome, not_cano)); \
1337  char *arb_cano = ARB_strdup(GB_concat_path(arbhome, cano)); \
1338  TEST_EXPECT_EQUAL(GB_canonical_path(arb_not_cano), arb_cano); \
1339  free(arb_cano); \
1340  free(arb_not_cano); \
1341  } while (0)
1342 
1343 static arb_test::match_expectation path_splits_into(const char *path, const char *Edir, const char *Enameext, const char *Ename, const char *Eext) {
1344  using namespace arb_test;
1345  expectation_group expected;
1346 
1347  char *Sdir,*Snameext,*Sname,*Sext;
1348  GB_split_full_path(path, &Sdir, &Snameext, &Sname, &Sext);
1349 
1350  expected.add(that(Sdir).is_equal_to(Edir));
1351  expected.add(that(Snameext).is_equal_to(Enameext));
1352  expected.add(that(Sname).is_equal_to(Ename));
1353  expected.add(that(Sext).is_equal_to(Eext));
1354 
1355  free(Sdir);
1356  free(Snameext);
1357  free(Sname);
1358  free(Sext);
1359 
1360  return all().ofgroup(expected);
1361 }
1362 
1363 #define TEST_EXPECT_PATH_SPLITS_INTO(path,dir,nameext,name,ext) TEST_EXPECTATION(path_splits_into(path,dir,nameext,name,ext))
1364 #define TEST_EXPECT_PATH_SPLITS_INTO__BROKEN(path,dir,nameext,name,ext) TEST_EXPECTATION__BROKEN(path_splits_into(path,dir,nameext,name,ext))
1365 
1366 static arb_test::match_expectation path_splits_reversible(const char *path) {
1367  using namespace arb_test;
1368  expectation_group expected;
1369 
1370  char *Sdir,*Snameext,*Sname,*Sext;
1371  GB_split_full_path(path, &Sdir, &Snameext, &Sname, &Sext);
1372 
1373  expected.add(that(GB_append_suffix(Sname, Sext)).is_equal_to(Snameext)); // GB_append_suffix should reverse name.ext-split
1374  expected.add(that(GB_concat_path(Sdir, Snameext)).is_equal_to(path)); // GB_concat_path should reverse dir/file-split
1375 
1376  free(Sdir);
1377  free(Snameext);
1378  free(Sname);
1379  free(Sext);
1380 
1381  return all().ofgroup(expected);
1382 }
1383 
1384 #define TEST_SPLIT_REVERSIBILITY(path) TEST_EXPECTATION(path_splits_reversible(path))
1385 #define TEST_SPLIT_REVERSIBILITY__BROKEN(path) TEST_EXPECTATION__BROKEN(path_splits_reversible(path))
1386 
1387 void TEST_paths() {
1388  // test GB_concat_path
1391  TEST_EXPECT_EQUAL(GB_concat_path("a", "b"), "a/b");
1392 
1393  TEST_EXPECT_EQUAL(GB_concat_path("/", "test.fig"), "/test.fig");
1394 
1395  // test GB_split_full_path
1396  TEST_EXPECT_PATH_SPLITS_INTO("dir/sub/.ext", "dir/sub", ".ext", "", "ext");
1397  TEST_EXPECT_PATH_SPLITS_INTO("/root/sub/file.notext.ext", "/root/sub", "file.notext.ext", "file.notext", "ext");
1398 
1399  TEST_EXPECT_PATH_SPLITS_INTO("./file.ext", ".", "file.ext", "file", "ext");
1400  TEST_EXPECT_PATH_SPLITS_INTO("/file", "/", "file", "file", NULp);
1401  TEST_EXPECT_PATH_SPLITS_INTO(".", ".", NULp, NULp, NULp);
1402 
1403  // test reversibility of GB_split_full_path and GB_concat_path/GB_append_suffix
1404  {
1405  const char *prefix[] = {
1406  "",
1407  "dir/",
1408  "dir/sub/",
1409  "/dir/",
1410  "/dir/sub/",
1411  "/",
1412  "./",
1413  "../",
1414  };
1415 
1416  for (size_t d = 0; d<ARRAY_ELEMS(prefix); ++d) {
1417  TEST_ANNOTATE(GBS_global_string("prefix='%s'", prefix[d]));
1418 
1419  TEST_SPLIT_REVERSIBILITY(GBS_global_string("%sfile.ext", prefix[d]));
1420  TEST_SPLIT_REVERSIBILITY(GBS_global_string("%sfile", prefix[d]));
1421  TEST_SPLIT_REVERSIBILITY(GBS_global_string("%s.ext", prefix[d]));
1422  if (prefix[d][0]) { // empty string "" reverts to NULp
1423  TEST_SPLIT_REVERSIBILITY(prefix[d]);
1424  }
1425  }
1426  }
1427 
1428  // GB_canonical_path basics
1429  TEST_EXPECT_CONTAINS(GB_canonical_path("./bla"), "UNIT_TESTER/run/bla");
1430  TEST_EXPECT_CONTAINS(GB_canonical_path("bla"), "UNIT_TESTER/run/bla");
1431 
1432  {
1433  char *arbhome = ARB_strdup(GB_getenvARBHOME());
1434  const char* nosuchfile = "nosuchfile";
1435  const char* somefile = "arb_README.txt";
1436 
1437  char *somefile_in_arbhome = ARB_strdup(GB_concat_path(arbhome, somefile));
1438  char *nosuchfile_in_arbhome = ARB_strdup(GB_concat_path(arbhome, nosuchfile));
1439  char *nosuchpath_in_arbhome = ARB_strdup(GB_concat_path(arbhome, "nosuchpath"));
1440  char *somepath_in_arbhome = ARB_strdup(GB_concat_path(arbhome, "lib"));
1441  char *file_in_nosuchpath = ARB_strdup(GB_concat_path(nosuchpath_in_arbhome, "whatever"));
1442 
1443  TEST_REJECT(GB_is_directory(nosuchpath_in_arbhome));
1444 
1445  // test GB_get_full_path
1446  TEST_EXPECT_IS_CANONICAL(somefile_in_arbhome);
1447  TEST_EXPECT_IS_CANONICAL(nosuchpath_in_arbhome);
1448  TEST_EXPECT_IS_CANONICAL(file_in_nosuchpath);
1449 
1450  TEST_EXPECT_IS_CANONICAL("/usr"); // existing (most likely)
1451 #if !defined(DARWIN)
1452  // TEST_DISABLED_OSX: fails for darwin on jenkins (/tmp seems to be a symbolic link there)
1453  TEST_EXPECT_IS_CANONICAL("/tmp/arbtest.fig");
1454 #endif
1455  TEST_EXPECT_IS_CANONICAL("/arbtest.fig"); // not existing (most likely)
1456 
1457  TEST_EXPECT_CANONICAL_TO("./PARSIMONY/./../ARBDB/./arbdb.h", "ARBDB/arbdb.h"); // test parent-path
1458  TEST_EXPECT_CANONICAL_TO("INCLUDE/arbdb.h", "ARBDB/arbdb.h"); // test symbolic link to file
1459  TEST_EXPECT_CANONICAL_TO("NAMES_COM/AISC/aisc.pa", "AISC_COM/AISC/aisc.pa"); // test symbolic link to directory
1460  TEST_EXPECT_CANONICAL_TO("./NAMES_COM/AISC/..", "AISC_COM"); // test parent-path through links
1461 
1462  TEST_EXPECT_CANONICAL_TO("./PARSIMONY/./../ARBDB/../nosuchpath", "nosuchpath"); // nosuchpath does not exist, but involved parent dirs do
1463  // test resolving of non-existent parent dirs:
1464  TEST_EXPECT_CANONICAL_TO("./PARSIMONY/./../nosuchpath/../ARBDB", "ARBDB");
1465  TEST_EXPECT_CANONICAL_TO("./nosuchpath/./../ARBDB", "ARBDB");
1466 
1467  // test GB_unfold_path
1468  TEST_EXPECT_EQUAL(GB_unfold_path("ARBHOME", somefile), somefile_in_arbhome);
1469  TEST_EXPECT_EQUAL(GB_unfold_path("ARBHOME", nosuchfile), nosuchfile_in_arbhome);
1470 
1471  char *inhome = ARB_strdup(GB_unfold_path("HOME", "whatever"));
1472  TEST_EXPECT_EQUAL(inhome, GB_canonical_path("~/whatever"));
1473  free(inhome);
1474 
1475  // test GB_unfold_in_directory
1476  TEST_EXPECT_EQUAL(GB_unfold_in_directory(arbhome, somefile), somefile_in_arbhome);
1477  TEST_EXPECT_EQUAL(GB_unfold_in_directory(nosuchpath_in_arbhome, somefile_in_arbhome), somefile_in_arbhome);
1478  TEST_EXPECT_EQUAL(GB_unfold_in_directory(arbhome, nosuchfile), nosuchfile_in_arbhome);
1479  TEST_EXPECT_EQUAL(GB_unfold_in_directory(nosuchpath_in_arbhome, "whatever"), file_in_nosuchpath);
1480  TEST_EXPECT_EQUAL(GB_unfold_in_directory(somepath_in_arbhome, "../nosuchfile"), nosuchfile_in_arbhome);
1481 
1482  // test unfolding absolute paths (HOME is ignored)
1483  TEST_EXPECT_EQUAL(GB_unfold_path("HOME", arbhome), arbhome);
1484  TEST_EXPECT_EQUAL(GB_unfold_path("HOME", somefile_in_arbhome), somefile_in_arbhome);
1485  TEST_EXPECT_EQUAL(GB_unfold_path("HOME", nosuchfile_in_arbhome), nosuchfile_in_arbhome);
1486 
1487  // test GB_path_in_ARBHOME
1488  TEST_EXPECT_EQUAL(GB_path_in_ARBHOME(somefile), somefile_in_arbhome);
1489  TEST_EXPECT_EQUAL(GB_path_in_ARBHOME(nosuchfile), nosuchfile_in_arbhome);
1490 
1491  free(file_in_nosuchpath);
1492  free(somepath_in_arbhome);
1493  free(nosuchpath_in_arbhome);
1494  free(nosuchfile_in_arbhome);
1495  free(somefile_in_arbhome);
1496  free(arbhome);
1497  }
1498 
1500 
1501 }
1502 
1503 // ----------------------------------------
1504 
1505 class TestFile : virtual Noncopyable {
1506  const char *name;
1507  bool open(const char *mode) {
1508  FILE *out = fopen(name, mode);
1509  if (out) fclose(out);
1510  return out;
1511  }
1512  void create() { ASSERT_RESULT(bool, true, open("w")); }
1513  void unlink() { ::unlink(name); }
1514 public:
1515  TestFile(const char *name_) : name(name_) { create(); }
1516  ~TestFile() { if (exists()) unlink(); }
1517  const char *get_name() const { return name; }
1518  bool exists() { return open("r"); }
1519 };
1520 
1521 void TEST_GB_remove_on_exit() {
1522  {
1523  // first test class TestFile
1524  TestFile file("test1");
1525  TEST_EXPECT(file.exists());
1526  TEST_EXPECT(TestFile(file.get_name()).exists()); // removes the file
1527  TEST_REJECT(file.exists());
1528  }
1529 
1530  TestFile t("test1");
1531  {
1532  GB_shell shell;
1533  GBDATA *gb_main = GB_open("no.arb", "c");
1534 
1535  GB_remove_on_exit(t.get_name());
1536  GB_close(gb_main);
1537  }
1538  TEST_REJECT(t.exists());
1539 }
1540 
1541 void TEST_some_paths() {
1542  gb_getenv_hook old = GB_install_getenv_hook(arb_test::fakeenv);
1543  {
1544  // ../UNIT_TESTER/run/homefake
1545 
1546  TEST_EXPECT_CONTAINS__BROKEN(GB_getenvHOME(), "/UNIT_TESTER/run/homefake"); // GB_getenvHOME() ignores the hook
1547  // @@@ this is a general problem - unit tested code cannot use GB_getenvHOME() w/o problems
1548 
1549  TEST_EXPECT_CONTAINS(GB_getenvARB_PROP(), "/UNIT_TESTER/run/homefake/.arb_prop");
1550  TEST_EXPECT_CONTAINS(GB_getenvARBMACRO(), "/lib/macros");
1551 
1552  TEST_EXPECT_CONTAINS(GB_getenvARBCONFIG(), "/UNIT_TESTER/run/homefake/.arb_prop/cfgSave");
1553  TEST_EXPECT_CONTAINS(GB_getenvARBMACROHOME(), "/UNIT_TESTER/run/homefake/.arb_prop/macros"); // works in [11068]
1554  }
1555  TEST_EXPECT_EQUAL((void*)arb_test::fakeenv, (void*)GB_install_getenv_hook(old));
1556 }
1557 
1558 #endif // UNIT_TESTS
1559 
static GB_CSTR GB_getenvARB_PROP()
Definition: adsocket.cxx:622
char * gbcm_read_string(int socket)
Definition: adsocket.cxx:240
GB_CSTR GB_path_in_HOME(const char *relative_path)
Definition: adsocket.cxx:1163
GB_ULONG GB_get_usable_memory()
Definition: adsocket.cxx:865
GB_ERROR GBK_system(const char *system_command)
Definition: arb_msg.cxx:571
#define arb_assert(cond)
Definition: arb_assert.h:245
static void GB_setenv(const char *var, const char *value)
Definition: adsocket.cxx:516
const char * GB_ERROR
Definition: arb_core.h:25
long GB_size_of_FILE(FILE *in)
Definition: arb_file.cxx:34
GB_CSTR GB_unfold_in_directory(const char *relative_directory, const char *path)
Definition: adsocket.cxx:1123
string result
GBDATA * GB_open(const char *path, const char *opent)
Definition: ad_load.cxx:1363
GBCM_ServerResult gbcm_write_flush(int socket)
Definition: adsocket.cxx:91
GB_CSTR GB_concat_full_path(const char *anypath_left, const char *anypath_right)
Definition: adsocket.cxx:1103
void GB_warning(const char *message)
Definition: arb_msg.cxx:530
#define ARB_PATH_MAX
Definition: arb_pathlen.h:31
group_matcher all()
Definition: test_unit.h:1011
#define TEST_EXPECT_CONTAINS__BROKEN(str, part)
Definition: test_unit.h:1317
void gbcm_read_flush()
Definition: adsocket.cxx:50
GB_CSTR GB_unfold_path(const char *pwd_envar, const char *path)
Definition: adsocket.cxx:1132
long GBS_write_hash(GB_HASH *hs, const char *key, long val)
Definition: adhash.cxx:454
void GB_split_full_path(const char *fullpath, char **res_dir, char **res_fullname, char **res_name_only, char **res_suffix)
Definition: adsocket.cxx:1268
static GB_ULONG get_physical_memory()
Definition: adsocket.cxx:758
NOT4PERL gb_getenv_hook GB_install_getenv_hook(gb_getenv_hook hook)
Definition: adsocket.cxx:699
const char * GBS_global_string_to_buffer(char *buffer, size_t bufsize, const char *templat,...)
Definition: arb_msg.cxx:178
char * GBK_doublequote(const char *arg)
Definition: arb_msg.cxx:624
GB_CSTR GB_getenvHTMLDOCPATH()
Definition: adsocket.cxx:686
void ARB_warn_about_unwanted_chars(const char *path, const char *path_description)
Definition: arb_misc.cxx:150
char * GB_read_file(const char *path)
Definition: adsocket.cxx:287
const char * arb_gethostname()
Definition: arb_cs.cxx:51
GB_CSTR GB_path_in_ARBLIB(const char *relative_path)
Definition: adsocket.cxx:1156
int ARB_stricmp(const char *s1, const char *s2)
Definition: arb_str.h:28
#define ASSERT_RESULT(Type, Expected, Expr)
Definition: arb_assert.h:336
const char * ARB_getenv_ignore_empty(const char *envvar)
Definition: arb_misc.cxx:102
GB_ERROR GB_IO_error(const char *action, const char *filename)
Definition: arb_msg.cxx:285
char * ARB_strdup(const char *str)
Definition: arb_string.h:27
static GB_CSTR GB_getenvARBCONFIG()
Definition: adsocket.cxx:634
void GB_internal_errorf(const char *templat,...)
Definition: arb_msg.cxx:504
GB_ERROR arb_open_socket(const char *name, bool do_connect, int *fd, char **filename_out)
Definition: arb_cs.cxx:124
GB_CSTR GB_getenvARBHOME()
Definition: adsocket.cxx:579
const char * GBS_global_string(const char *templat,...)
Definition: arb_msg.cxx:203
GB_CSTR GB_getenv(const char *env)
Definition: adsocket.cxx:709
GB_CSTR GB_getenvUSER()
Definition: adsocket.cxx:545
GB_ERROR GB_textprint(const char *path)
Definition: adsocket.cxx:363
static void exit_remove_file(const char *file, long, void *)
Definition: adsocket.cxx:1246
char * release()
Definition: arb_strbuf.h:129
XCMD_TYPE
Definition: arbdb.h:115
static char * getenv_executable(GB_CSTR envvar)
Definition: adsocket.cxx:451
void GBS_free_hash(GB_HASH *hs)
Definition: adhash.cxx:538
void cat(const char *from)
Definition: arb_strbuf.h:183
static GB_CSTR GB_getenvARB_XCMD()
Definition: adsocket.cxx:532
GBCM_ServerResult gbcm_write(int socket, const char *ptr, long size)
Definition: adsocket.cxx:128
char * ARB_strpartdup(const char *start, const char *end)
Definition: arb_string.h:51
GB_ERROR GB_create_parent_directory(const char *path)
#define EXIT_SUCCESS
Definition: arb_a2ps.c:154
GBCM_ServerResult gbcm_write_long(int socket, long data)
Definition: adsocket.cxx:260
char * unix_name
Definition: gb_comm.h:16
long GB_size_of_file(const char *path)
Definition: arb_file.cxx:28
static GB_CSTR GB_getenvPATH()
Definition: adsocket.cxx:377
#define ARRAY_ELEMS(array)
Definition: arb_defs.h:19
char buffer[MESSAGE_BUFFERSIZE]
Definition: seq_search.cxx:34
NOT4PERL GB_ERROR GB_xcmd(const char *cmd, XCMD_TYPE exectype)
Definition: adsocket.cxx:906
static char * getenv_autodirectory(const char *envvar, const char *defaultDirectory)
Definition: adsocket.cxx:602
unsigned long GB_ULONG
Definition: arbdb_base.h:42
#define NOT4PERL
Definition: arbdb_base.h:23
static char path_buf[2][ARB_PATH_MAX]
Definition: adsocket.cxx:977
GB_ULONG GB_time_of_day()
Definition: adsocket.cxx:357
char * ARB_executable(const char *exe_name, const char *path)
Definition: arb_misc.cxx:107
#define TEST_EXPECT_CONTAINS(str, part)
Definition: test_unit.h:1316
GB_ERROR GB_export_error(const char *error)
Definition: arb_msg.cxx:257
static long gbcm_read_buffered(int socket, char *ptr, long size)
Definition: adsocket.cxx:55
GB_ERROR GB_await_error()
Definition: arb_msg.cxx:342
#define TEST_EXPECT(cond)
Definition: test_unit.h:1328
const char * GBS_read_arb_tcp(const char *env)
Definition: adtcp.cxx:325
GBCM_ServerResult gbcm_read_two(int socket, long a, long *b, long *c)
Definition: adsocket.cxx:196
GBCM_ServerResult
Definition: gb_local.h:93
void GB_warningf(const char *templat,...)
Definition: arb_msg.cxx:536
GB_CSTR GB_append_suffix(const char *name, const char *suffix)
Definition: adsocket.cxx:984
static char * use_other_path_buf()
Definition: adsocket.cxx:979
static GB_CSTR GB_getenvHOME()
Definition: adsocket.cxx:563
void GBS_hash_do_const_loop(const GB_HASH *hs, gb_hash_const_loop_type func, void *client_data)
Definition: adhash.cxx:559
void GBK_terminate(const char *error) __ATTR__NORETURN
Definition: arb_msg.cxx:509
char * GB_unique_filename(const char *name_prefix, const char *suffix)
Definition: adsocket.cxx:1233
long gbcm_read(int socket, char *ptr, long size)
Definition: adsocket.cxx:79
GB_ERROR gbcm_open_socket(const char *path, bool do_connect, int *psocket, char **unix_name)
Definition: adsocket.cxx:144
const char * GBS_readable_size(unsigned long long size, const char *unit_suffix)
Definition: arb_misc.cxx:23
static char * GB_find_executable(GB_CSTR description_of_executable,...)
Definition: adsocket.cxx:399
#define TEST_REJECT(cond)
Definition: test_unit.h:1330
void GB_atexit(void(*exitfun)())
Definition: arbdb.cxx:402
long gbcm_read_long(int socket)
Definition: adsocket.cxx:265
static void error(const char *msg)
Definition: mkptypes.cxx:96
#define GB_PATH_TMP
Definition: adsocket.cxx:1174
static int path_toggle
Definition: adsocket.cxx:976
expectation_group & add(const expectation &e)
Definition: test_unit.h:812
char * GB_create_tempfile(const char *name)
Definition: adsocket.cxx:1222
GB_CSTR GB_getenvARBMACRO()
Definition: adsocket.cxx:593
#define that(thing)
Definition: test_unit.h:1043
static GB_CSTR GB_path_in_arb_temp(const char *relative_path)
Definition: adsocket.cxx:1169
GB_CSTR GB_concat_path(GB_CSTR anypath_left, GB_CSTR anypath_right)
Definition: adsocket.cxx:1069
bool is_name_of_envvar(const char *name)
Definition: adsocket.cxx:1115
static char * getenv_existing_directory(GB_CSTR envvar)
Definition: adsocket.cxx:490
static GB_CSTR GB_concat_path_in_ARBHOME(const char *relative_path_left, const char *anypath_right)
Definition: adsocket.cxx:1152
GBCM_ServerResult gbcm_write_string(int socket, const char *key)
Definition: adsocket.cxx:228
bool GB_host_is_local(const char *hostname)
Definition: adsocket.cxx:746
const char *(* gb_getenv_hook)(const char *varname)
Definition: arbdb.h:138
static gb_getenv_hook getenv_hook
Definition: adsocket.cxx:697
int socket
Definition: gb_comm.h:15
CONSTEXPR_INLINE bool valid(SpeciesCreationMode m)
Definition: ed4_class.hxx:2247
long gbcm_write_two(int socket, long a, long c)
Definition: adsocket.cxx:186
FILE * GB_fopen_tempfile(const char *filename, const char *fmode, char **res_fullname)
Definition: adsocket.cxx:1176
GB_CSTR GB_getcwd(void)
Definition: adfile.cxx:24
void GBS_reuse_buffer(const char *global_buffer)
Definition: arb_msg.cxx:563
va_end(argPtr)
#define EXIT_FAILURE
Definition: arb_a2ps.c:157
#define is_equal_to(val)
Definition: test_unit.h:1025
GB_CSTR GB_getenvDOCPATH()
Definition: adsocket.cxx:676
char * write_buffer
Definition: gb_localdata.h:52
char * write_ptr
Definition: gb_localdata.h:53
GB_CSTR GB_getenvARB_GS()
Definition: adsocket.cxx:640
long write_bufsize
Definition: gb_localdata.h:54
GB_ERROR GB_export_errorf(const char *templat,...)
Definition: arb_msg.cxx:262
void GB_internal_error(const char *message)
Definition: arb_msg.cxx:481
GB_CSTR GB_getenvARB_XTERM()
Definition: adsocket.cxx:523
TYPE * ARB_calloc(size_t nelem)
Definition: arb_mem.h:81
static void exit_removal()
Definition: adsocket.cxx:1251
#define gb_assert(cond)
Definition: arbdbt.h:11
char * GBK_singlequote(const char *arg)
Definition: arb_msg.cxx:599
GB_CSTR GB_concat_path_in_ARBLIB(const char *relative_path_left, const char *anypath_right)
Definition: adsocket.cxx:1159
void GB_remove_on_exit(const char *filename)
Definition: adsocket.cxx:1258
aisc_com * link
bool GB_is_directory(const char *path)
Definition: arb_file.cxx:176
char ARB_path_contains_unwanted_chars(const char *path)
Definition: arb_misc.cxx:143
GB_ERROR GB_failedTo_error(const char *do_something, const char *special, GB_ERROR error)
Definition: arb_msg.cxx:375
char * GB_map_FILE(FILE *in, int writeable)
Definition: adsocket.cxx:323
GB_CSTR GB_canonical_path(const char *anypath)
Definition: adsocket.cxx:1000
static int pages
Definition: arb_a2ps.c:298
va_start(argPtr, format)
static GB_HASH * files_to_remove_on_exit
Definition: adsocket.cxx:1245
char * GBS_eval_env(GB_CSTR p)
Definition: adstring.cxx:212
static export_environment expenv
Definition: adsocket.cxx:744
#define NULp
Definition: cxxforward.h:114
void GB_informationf(const char *templat,...)
Definition: arb_msg.cxx:555
gbcmc_comm * gbcmc_open(const char *path)
Definition: adsocket.cxx:170
static char * command
Definition: arb_a2ps.c:319
bool ARB_strBeginsWith(const char *str, const char *with)
Definition: arb_str.h:42
static GB_ULONG parse_env_mem_definition(const char *env_override, GB_ERROR &error)
Definition: adsocket.cxx:830
const char * get_data() const
Definition: arb_strbuf.h:120
GB_CSTR GB_getenvARBMACROHOME()
Definition: adsocket.cxx:628
#define FALLTHROUGH
Definition: cxxforward.h:128
char * GB_map_file(const char *path, int writeable)
Definition: adsocket.cxx:344
GB_CSTR GB_path_in_ARBHOME(const char *relative_path)
Definition: adsocket.cxx:1149
#define BUFSIZE
bool is_absolute_path(const char *path)
Definition: adsocket.cxx:1114
GBDATA * gb_main
Definition: adname.cxx:32
char * GB_read_fp(FILE *in)
Definition: adsocket.cxx:271
#define min(a, b)
Definition: f2c.h:153
const char * GB_CSTR
Definition: arbdb_base.h:25
#define TEST_EXPECT_EQUAL(expr, want)
Definition: test_unit.h:1294
gb_local_data * gb_local
Definition: arbdb.cxx:33
char * release_memfriendly()
Definition: arb_strbuf.h:133
GB_ERROR GB_create_directory(const char *path)
long gbcms_close(gbcmc_comm *link)
char * GBS_global_string_copy(const char *templat,...)
Definition: arb_msg.cxx:194
void GB_close(GBDATA *gbd)
Definition: arbdb.cxx:655
GB_CSTR GB_getenvARB_PDFVIEW()
Definition: adsocket.cxx:659
GB_CSTR GB_path_in_arbprop(const char *relative_path)
Definition: adsocket.cxx:1166
GB_HASH * GBS_create_hash(long estimated_elements, GB_CASE case_sens)
Definition: adhash.cxx:253
void put(char c)
Definition: arb_strbuf.h:158