CAEN Utility  2.0.2
Utilities for CAEN projects
CAENMultiplatform.c
Go to the documentation of this file.
1 /******************************************************************************
2 *
3 * CAEN SpA - Software Division
4 * Via Vetraia, 11 - 55049 - Viareggio ITALY
5 * +39 0594 388 398 - www.caen.it
6 *
7 *******************************************************************************
8 *
9 * Copyright (C) 2019-2022 CAEN SpA
10 *
11 * This file is part of the CAEN Utility.
12 *
13 * The CAEN Utility is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 3 of the License, or (at your option) any later version.
17 *
18 * The CAEN Utility is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with the CAEN Utility; if not, see
25 * https://www.gnu.org/licenses/.
26 *
27 * SPDX-License-Identifier: LGPL-3.0-or-later
28 *
29 ***************************************************************************/
37 #ifdef _WIN32
38 #include <WinSock2.h>
39 #endif
40 
41 #include <CAENMultiplatform.h>
42 
43 #include <errno.h>
44 #include <inttypes.h>
45 #include <limits.h>
46 #include <math.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <time.h>
50 
51 #include <CAENLogger.h>
52 #include <CAENMap.h>
53 
54 #ifndef _WIN32
55 #include <dlfcn.h> // dlopen, dlerror, dlsym, dlclose
56 #include <malloc.h> // malloc_usable_size
57 #include <signal.h>
58 #include <spawn.h>
59 #include <strings.h> // strcasecmp
60 #include <termios.h> // tcsetattr, termios, tcgetattr
61 #include <unistd.h> // for STDIN_FILENO, getcwd, access
62 #include <sys/stat.h>
63 #include <sys/time.h> // struct timeval, gettimeofday
64 #include <sys/types.h>
65 #include <sys/wait.h>
66 #else
67 #include <conio.h> // _getch
68 #include <direct.h> // _getcwd
69 #include <process.h> // _getpid
70 #include <tlhelp32.h> // CreateToolhelp32Snapshot, PROCESSENTRY32, ...
71 #include <sys/stat.h>
72 #include <sys/timeb.h> // _ftime_s
73 #include <sysinfoapi.h> // GetSystemInfo
74 
75 // see c_popcount implementation
76 #if defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC))
77 #define _is_machine_x86_64
78 #include <intrin.h>
79 #ifndef __AVX__
80 #include <isa_availability.h>
81 #endif
82 #endif
83 
84 #endif
85 
86 #define KILL_BUFSIZE (128)
87 
88 INIT_C_LOGGER("CAENMultiplatformLog.txt", "CAENMultiplatform.c");
89 
90 // Decode Error
91 static void _decodeError(const char *functName, int32_t resCode) {
92  const char *ErrMsg;
93 
94  switch (resCode) {
95  case MP_code_Generic:
96  ErrMsg = "Generic error.";
97  break;
98  case MP_code_LibLoad:
99  ErrMsg = "Error loading dynamic library.";
100  break;
101  case MP_code_LibClose:
102  ErrMsg = "Error closing dynamic library.";
103  break;
104  default:
105  ErrMsg = "Unknown error.";
106  break;
107  }
108 
109  logMsg(c_logger_Severity_ERROR, "From %s: %s", functName, ErrMsg);
110 }
111 
112 c_use_decl_annotations void* c_malloc(size_t size) {
113  void *res;
114 #ifdef _WIN32
115  res = HeapAlloc(GetProcessHeap(), 0, (SIZE_T)size);
116 #else
117  res = malloc(size);
118 #endif
119  return res;
120 }
121 
122 c_use_decl_annotations void* c_realloc(void* ptr, size_t size) {
123  void *res;
124 #ifdef _WIN32
125  /*
126  * On realloc(), if ptr is NULL, the behavior is the same as calling malloc(size).
127  * This feature is not true for HeapReAlloc(), that seems to return NULL.
128  */
129  if (c_likely(ptr != NULL))
130  res = HeapReAlloc(GetProcessHeap(), 0, ptr, (SIZE_T)size);
131  else
132  res = c_malloc(size);
133 #else
134  res = realloc(ptr, size);
135 #endif
136  return res;
137 }
138 
139 c_use_decl_annotations void* c_calloc(size_t nmemb, size_t size) {
140  void *res;
141 #ifdef _WIN32
142  res = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (SIZE_T)(nmemb * size));
143 #else
144  res = calloc(nmemb, size);
145 #endif
146  return res;
147 }
148 
149 void c_free(void *ptr) {
150 #ifdef _WIN32
151  /*
152  * On 2021 the HeapFree documentation has been update: now the last parameter
153  * can be NULL. Previously it was explictly reported as undefined behavior.
154  * Probably it was just a documentation bug, because the last parameter is
155  * marked with the SAL macro _Frees_ptr_opt_ also on the old SDK version
156  * 10.0.16299.0, currently used to compile this library on Windows. It is
157  * reasonable to remove the check.
158  * See:
159  * - https://docs.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapfree
160  * - https://github.com/MicrosoftDocs/sdk-api/pull/610
161  *
162  * The return value is not used for consistency with standard free.
163  */
164  const BOOL res = HeapFree(GetProcessHeap(), 0, ptr);
165  c_unused_parameter(res);
166 #else
167  free(ptr);
168 #endif
169 }
170 
172  size_t res;
173 #ifdef _WIN32
174  /*
175  * If the function fails, the return value is (SIZE_T)-1.
176  * The check for null is for consistency with GNU malloc_usable_size() behavior.
177  * See:
178  * - https://docs.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapsize
179  */
180  if (c_unlikely(ptr == NULL))
181  return 0;
182  const SIZE_T s = HeapSize(GetProcessHeap(), 0, ptr);
183  res = (s == (SIZE_T)-1) ? 0 : (size_t)s;
184 #else
185  /*
186  * Surprisingly, malloc_usable_size() argument is not marked as const.
187  * If ptr is NULL, 0 is returned.
188  */
189  res = malloc_usable_size(ptr);
190 #endif
191  return res;
192 }
193 
194 c_attribute_nonnull(1) static char* _strndup(const char *str, size_t size) {
195  char *res = c_malloc(size + 1);
196  if (c_likely(res != NULL)) {
197  res[size] = '\0';
198  res = memcpy(res, str, size); // c_memcpy is useless here, res and str cannot be null
199  }
200  return res;
201 }
202 
203 // Version of strdup to be used with c_free()
204 c_use_decl_annotations char* c_strdup(const char *str) {
205  if (c_unlikely(str == NULL))
206  return NULL;
207  const size_t len = strlen(str);
208  return _strndup(str, len);
209 }
210 
211 // Version of strndup to be used with c_free()
212 c_use_decl_annotations char* c_strndup(const char *str, size_t size) {
213  if (c_unlikely(str == NULL))
214  return NULL;
215  const size_t len = strnlen(str, size);
216  return _strndup(str, len);
217 }
218 
219 // Get Unix time in milliseconds since 00:00:00 UTC, January 1, 1970.
221  uint64_t sec;
222  uint64_t msec;
223 #ifdef _WIN32
224  struct _timeb tv;
225  errno_t ret = _ftime_s(&tv);
226  if (ret != 0) {
227  logMsg(c_logger_Severity_DEBUG, "%s(): _ftime_s() failed. Error %d", __func__, ret);
228  return 0;
229  }
230  sec = (uint64_t)tv.time;
231  msec = (uint64_t)tv.millitm;
232 #else
233  struct timeval tv;
234  int ret = gettimeofday(&tv, NULL);
235  if (c_unlikely(ret != 0)) {
236  logMsg(c_logger_Severity_DEBUG, "%s(): gettimeofday() failed. Error %d", __func__, errno);
237  return 0;
238  }
239  sec = (uint64_t)tv.tv_sec;
240  msec = (uint64_t)tv.tv_usec / UINT64_C(1000);
241 #endif
242  return (sec * UINT64_C(1000)) + msec;
243 }
244 
246 #ifdef _WIN32
247  return _getch();
248 #else
249  struct termios oldattr;
250  if (tcgetattr(STDIN_FILENO, &oldattr) == -1) perror(NULL);
251  struct termios newattr = oldattr;
252  newattr.c_lflag &= ~(ICANON | ECHO);
253  newattr.c_cc[VTIME] = 0;
254  newattr.c_cc[VMIN] = 1;
255  if (tcsetattr(STDIN_FILENO, TCSANOW, &newattr) == -1) perror(NULL);
256  const int ch = getchar();
257  if (tcsetattr(STDIN_FILENO, TCSANOW, &oldattr) == -1) perror(NULL);
258  return ch;
259 #endif
260 }
261 
263 #ifdef _WIN32
264  return _kbhit();
265 #else
266  struct termios oldattr;
267  if (tcgetattr(STDIN_FILENO, &oldattr) == -1) perror(NULL);
268  struct termios newattr = oldattr;
269  newattr.c_lflag &= ~(ICANON | ECHO);
270  newattr.c_cc[VTIME] = 0;
271  newattr.c_cc[VMIN] = 1;
272  if (tcsetattr(STDIN_FILENO, TCSANOW, &newattr) == -1) perror(NULL);
273  /* check stdin (fd 0) for activity */
274  fd_set read_handles;
275  FD_ZERO(&read_handles);
276  FD_SET(0, &read_handles);
277  struct timeval timeout;
278  timeout.tv_sec = timeout.tv_usec = 0;
279  int status = select(0 + 1, &read_handles, NULL, NULL, &timeout);
280  if (status < 0) perror(NULL);
281  if (tcsetattr(STDIN_FILENO, TCSANOW, &oldattr) == -1) perror(NULL);
282  return status;
283 #endif
284 }
285 
286 c_use_decl_annotations int c_access(const char* pathname, int mode) {
287 #ifdef _WIN32
288  // _access check for NULL
289  return _access(pathname, mode);
290 #else
291  if (pathname == NULL) {
292  errno = EINVAL;
293  return -1;
294  }
295  return access(pathname, mode);
296 #endif
297 }
298 
299 char* c_getcwd(char* buf, size_t size) {
300  // allocation of buf == NULL, supported on both Windows and Linux,
301  // must be handled because allocation is done with malloc on both platforms,
302  // making c_free unsuitable.
303  if (buf == NULL) {
304  char *local_buf;
305  // Platform dependent behaviour here:
306  // - Windows allocates AT LEAST size (works even if size required by _getcwd is > size)
307  // - Linux allocates AT MOST size, or return NULL
308  // For this reason, on Linux we use PATH_MAX as argument.
309 #ifdef _WIN32
310  if (size > (size_t)INT_MAX)
311  return NULL;
312  local_buf = _getcwd(NULL, (int)size);
313 #else
314  // we use the maximum size to be sure getcwd does not fail
315  // final size is not this one, as we use strdup later
316  local_buf = getcwd(NULL, PATH_MAX);
317 #endif
318  if (local_buf == NULL) {
319  errno = ENOMEM;
320  return NULL;
321  }
322  buf = c_strdup(local_buf);
323  if (buf == NULL) {
324  free(local_buf); // free is required, not c_free
325  errno = ENOMEM;
326  return NULL;
327  }
328  // getcwd allocates at least `size` bytes
329  if (c_malloc_size(buf) < size) {
330  char *realloc_buf = c_realloc(buf, size);
331  if (realloc_buf == NULL) {
332  c_free(buf);
333  free(local_buf); // free is required, not c_free
334  errno = ENOMEM;
335  return NULL;
336  }
337  buf = realloc_buf;
338  }
339  free(local_buf); // free is required, not c_free
340  return buf;
341  } else {
342 #ifdef _WIN32
343  if (size > (size_t)INT_MAX)
344  return NULL;
345  return _getcwd(buf, (int)size);
346 #else
347  return getcwd(buf, size);
348 #endif
349  }
350 }
351 
352 int c_chdir(const char* path) {
353 #ifdef _WIN32
354  return _chdir(path);
355 #else
356  return chdir(path);
357 #endif
358 }
359 
360 int c_rmdir(const char* path) {
361 #ifdef _WIN32
362  return _rmdir(path);
363 #else
364  return rmdir(path);
365 #endif
366 }
367 
369  // pid_t on Linux is a signed interer type, so we use int64_t to be conservative
370  // on windows _getpid return int
371 #ifdef _WIN32
372  return (int64_t)_getpid();
373 #else
374  return (int64_t)getpid();
375 #endif
376 }
377 
378 c_use_decl_annotations int c_strcasecmp(const char* s1, const char* s2) {
379 #ifdef _WIN32
380  return _stricmp(s1, s2);
381 #else
382  return strcasecmp(s1, s2);
383 #endif
384 }
385 
386 c_use_decl_annotations int c_strncasecmp(const char* s1, const char* s2, size_t n) {
387 #ifdef _WIN32
388  return _strnicmp(s1, s2, n);
389 #else
390  return strncasecmp(s1, s2, n);
391 #endif
392 }
393 
394 // The variable must be in the form $(VARNAME)
395 c_use_decl_annotations char* c_getExpandedString(const char *filename) {
396  if (filename == NULL)
397  return NULL;
398  char *result = NULL;
399  char *varValue = NULL, *varName = NULL;
400  char *p1 = strchr(filename, '$');
401  char *p2 = strchr(filename, ')');
402  char empty[] = "";
403 
404  size_t varNameLength, resultLength;
405 
406  if (p1 != NULL && p2 != NULL && p2 - p1 > 2) {
407  varNameLength = p2 - p1 - 2; // -2 to remove "$("
408  varName = c_malloc(varNameLength + 1);
409  if (varName == NULL)
410  goto QuitFunction;
411 
412  strncpy(varName, p1 + 2, varNameLength); // +2 to add "$("
413  varName[varNameLength] = '\0';
414 
415  // Get the variable value
416  varValue = getenv(varName);
417  if(varValue == NULL) // use empty string if var not found
418  varValue = empty;
419 
420  // Build the result
421  resultLength = strlen(filename) - (strlen(varName) + 3) + strlen(varValue) + 1;
422  result = c_malloc(resultLength);
423  if (result == NULL)
424  goto QuitFunction;
425 
426  strncpy(result, filename, p1 - filename);
427  // Put NUL terminator that could be missing in case p1 - filename == 0.
428  result[p1 - filename] = '\0';
429  strcat(result, varValue);
430  strcat(result, p2 + 1);
431  if (strchr(result, '$')) {
432  char* tmpResult = c_getExpandedString(result);
433  c_free(result);
434  result = tmpResult;
435  }
436  }
437  else {
438  result = c_strdup(filename);
439  if (result == NULL)
440  goto QuitFunction;
441  }
442 
443 QuitFunction:
444  c_free(varName);
445  return result;
446 }
447 
448 int32_t c_getDirName(char *dest, const char *source) {
449  const char dirseps[] = DIRSEPS_CHAR;
450 
451  logMsg(c_logger_Severity_DEBUG, "c_getDirName: source is: %s", source);
452  if (dest != source)
453  strcpy(dest, source);
454  char *p1 = strrchr(dest, dirseps[0]);
455 #ifdef _WIN32
456  // in Windows paths may also have '/'!
457  char *p2 = strrchr(dest, dirseps[1]);
458  char *last = max(p1, p2);
459 #else
460  char *last = p1;
461 #endif
462  // last may still be NULL (if source don't contain '\' or '/')
463  if (last == NULL)
464  dest[0] = '\0';
465  else
466  last[1] = '\0'; // setting last[1]=0 means truncate dest to the last '\' or '/'
467  logMsg(c_logger_Severity_DEBUG, "c_getDirName: dest is: %s", dest);
468 
469  return MP_code_Success;
470 }
471 
472 int32_t c_getBaseName(char *dest, const char *source) {
473  if (dest == NULL || source == NULL)
474  return MP_code_Generic;
475 
476  const char dirseps[] = DIRSEPS_CHAR;
477 
478  logMsg(c_logger_Severity_DEBUG, "c_getBaseName: source is: %s", source);
479  char *p1 = strrchr(source, dirseps[0]);
480 #ifdef _WIN32
481  // in Windows paths may also have '/'!
482  char *p2 = strrchr(source, dirseps[1]);
483  char *last = max(p1, p2);
484 #else
485  char *last = p1;
486 #endif
487  // last may still be NULL (if source don't contain '\' or '/')
488  if (last == NULL)
489  dest[0] = '\0';
490  else
491  strcpy(dest, last + 1);
492  logMsg(c_logger_Severity_DEBUG, "c_getBaseName: dest is: %s", dest);
493 
494  return MP_code_Success;
495 }
496 
497 static bool _isProcValid(c_Process_t *proc) {
498  return (proc != NULL);
499 }
500 
502  int32_t ret;
503  if (dest == NULL) {
504  logMsg(c_logger_Severity_ERROR, "%s(): invalid null argument", __func__);
506  }
507 #ifdef _WIN32
508  LPCH env = GetEnvironmentStringsA();
509  size_t sz, totsz = 0;
510  while ((sz = strlen(env + totsz)) != 0) {
511  char *var = env + totsz;
512  char *div = strchr(var, '=');
513  char *key, *value;
514  totsz += sz + 1;
515  if (div == NULL) {
516  logMsg(c_logger_Severity_ERROR, "%s(): Variable '%s' misses equal sign.", __func__, var);
518  }
519  if (div == var)
520  continue; // for some unknown reason, windows may give lines starting with '='. Just skip them...
521  *div = '\0';
522  key = var;
523  value = c_strdup(div + 1);
524  if (value == NULL) {
525  logMsg(c_logger_Severity_ERROR, "%s(): c_strdup failed", __func__);
527  }
528  if ((ret = c_map_set(dest, key, value)) != c_Utility_ErrorCode_Success) {
529  logMsg(c_logger_Severity_ERROR, "%s(): Error %" PRIi32 " setting keypair [%s] = %s", __func__, ret, key, value);
530  *div = '='; // put back equal sign
531  c_free(value);
532  return ret;
533  }
534  *div = '='; // put back equal sign
535  }
536  FreeEnvironmentStringsA(env);
538 #else
539  if (environ != NULL) {
540  int32_t i = 0;
541  char *s;
542  while ((s = environ[i++]) != NULL) {
543  char *div = strchr(s, '=');
544  char *key, *value;
545  if (div == NULL) {
546  logMsg(c_logger_Severity_ERROR, "%s(): Variable '%s' misses equal sign.", __func__, s);
548  }
549  *div = '\0';
550  key = s;
551  value = c_strdup(div + 1);
552  if (value == NULL) {
553  logMsg(c_logger_Severity_ERROR, "%s(): c_strdup failed", __func__);
555  }
556  if ((ret = c_map_set(dest, key, value)) != c_Utility_ErrorCode_Success) {
557  logMsg(c_logger_Severity_ERROR, "%s(): Error %" PRIi32 " setting keypair [%s] = %s", __func__, ret, key, value);
558  *div = '='; // put back equal sign
559  c_free(value);
560  return ret;
561  }
562  *div = '='; // put back equal sign
563  }
564  }
566 #endif
567  return ret;
568 }
569 
571  const uint32_t initial_nbuckets = 128;
572  int32_t ret = c_Utility_ErrorCode_Success;
573 
574  c_Process_t* proc = c_calloc(1, sizeof(*proc));
575 
576  if (proc == NULL) {
578  goto QuitFunction;
579  }
580 
581  proc->execName = NULL;
582  proc->argv[0] = NULL;
583  proc->argc = 0;
584 
585  if ((ret = c_map_init(&proc->environment, initial_nbuckets)) != c_Utility_ErrorCode_Success) {
586  logMsg(c_logger_Severity_ERROR, "Can't initialize environment map (err=%"PRIi32")", ret);
587  goto QuitFunction;
588  }
589 
591  logMsg(c_logger_Severity_ERROR, "Can't get current environment (err=%"PRIi32")", ret);
592  goto QuitFunction;
593  }
594 
595 QuitFunction:
596  if (ret != c_Utility_ErrorCode_Success) {
597  c_free(proc);
598  proc = NULL;
599  }
600  return proc;
601 }
602 
603 int32_t c_setProcExec(const char *exec, c_Process_t *proc) {
604  if (!_isProcValid(proc))
605  return -1;
606  proc->execName = c_strdup(exec);
607  if (proc->execName == NULL)
608  return -2;
609 
610  if (proc->argv[0] == NULL)
611  c_addProcArg(proc->execName, proc);
612  else {
613  c_free(proc->argv[0]);
614  proc->argv[0] = c_strdup(proc->execName);
615  if (proc->argv[0] == NULL)
616  return -3;
617  }
618  return 0;
619 }
620 
621 int32_t c_addProcArg(const char *arg, c_Process_t *proc) {
622  if (!_isProcValid(proc))
623  return -1;
624  if (proc->argc >= MAX_PROC_NARGS)
625  return -2;
626  proc->argv[proc->argc] = c_strdup(arg);
627  if (proc->argv[proc->argc] == NULL)
628  return -3;
629 
630  proc->argc++;
631  if (proc->argc < MAX_PROC_NARGS)
632  proc->argv[proc->argc] = NULL;
633  return 0;
634 }
635 
636 int32_t c_addProcEnv(const char *varname, const char *varvalue, c_Process_t *proc) {
637  char *val;
638  int32_t ret;
639 
640  if (!_isProcValid(proc))
641  return -1;
642 
643  // value must be copyed. key is strdup-ed inside set function
644  val = c_strdup(varvalue);
645  if ((ret = c_map_set(&proc->environment, varname, val)) != c_Utility_ErrorCode_Success) {
646  logMsg(c_logger_Severity_ERROR, "Error %" PRIi32 " adding keyvalue pair [%s]:[%s] to environment map.", ret, varname, varvalue);
647  return -2;
648  }
649 
650  return 0;
651 }
652 
653 static void _freePointersArray(char** arr, size_t size) {
654  if (arr == NULL)
655  return;
656  for (size_t i = 0; i < size; i++)
657  c_free(arr[i]);
658  c_free(arr);
659 }
660 
661 static int32_t _mapToStringArray(c_environment_t *map, char ***dest, size_t *count) {
662  int32_t ret = 0;
663  c_map_iter_t iter = c_map_iter();
664  char **result = NULL;
665  size_t totcnt = 0;
666  size_t cnt = 0;
667 
668  *dest = NULL;
669  *count = 0;
670 
671  if (map == NULL) {
672  logMsg(c_logger_Severity_ERROR, "Error getting map size");
673  ret = -1;
674  goto QuitFunction;
675  }
676 
677  totcnt = c_map_size(map);
678 
679  // allocate '*count' pointers to 'char'
680  result = c_malloc(totcnt * sizeof(*result));
681  if (result == NULL) {
682  logMsg(c_logger_Severity_ERROR, "c_malloc failed");
683  ret = -2;
684  goto QuitFunction;
685  }
686 
687  // initialize result array to avoid unlikely UB in the next loop
688  for (size_t i = 0; i < totcnt; ++i)
689  result[i] = NULL;
690 
691  for (const char* key = c_map_next(map, &iter); key != NULL; key = c_map_next(map, &iter)) {
692 
693  char **v = c_map_get(map, key);
694 
695  // kept for consistency: v cannot be NULL being key part of the iterator
696  if (c_unlikely(v == NULL)) {
697  logMsg(c_logger_Severity_ERROR, "Cannot get env. variable %s", key);
698  ret = -3;
699  goto QuitFunction;
700  }
701 
702  char *value = *v;
703  const size_t sz = strlen(key) + strlen(value) + 2;
704  result[cnt] = c_malloc(sz * sizeof(*result[cnt]));
705  if (result[cnt] == NULL) {
706  logMsg(c_logger_Severity_ERROR, "Cannot allocate string of size %zu", sz);
707  ret = -4;
708  goto QuitFunction;
709  }
710  sprintf(result[cnt], "%s=%s", key, value);
711  cnt++;
712  }
713 
714  if (cnt != totcnt) {
715  logMsg(c_logger_Severity_ERROR, "totcnt and cnt are not equal: %zu != %zu", totcnt, cnt);
716  ret = -5;
717  }
718 
719 QuitFunction:
720  if (ret != 0) {
721  _freePointersArray(result, totcnt);
722  }
723  else {
724  *dest = result;
725  *count = totcnt;
726  }
727  return ret;
728 }
729 
730 #ifdef _WIN32
731 static int _compareStringsInsensitive(const void* a, const void* b) {
732  return c_strcasecmp(a, b);
733 }
734 
735 static void _uninitHandlers(c_Process_t *proc) {
736  proc->procInfo.dwProcessId = 0;
737  proc->procInfo.dwThreadId = 0;
738  proc->procInfo.hProcess = NULL;
739  proc->procInfo.hThread = NULL;
740  proc->jobObject = NULL;
741 }
742 
743 static int32_t _startProcessWin(c_Process_t *proc) {
744  int32_t res = CAENPROC_RetCode_Success;
745  BOOL ok = TRUE;
746  size_t stringlen=0;
747  char *CommandString = NULL;
748  JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };
749  STARTUPINFOA si = { 0 };
750  const char *fName = NULL;
751  char **envarray = NULL;
752  char *env = NULL;
753  size_t envc = 0;
754 
755  if(!_isProcValid(proc))
756  return -1;
757  if(proc->execName == NULL)
758  return -2;
759  if(strlen(proc->execName) == 0)
760  return -2;
761 
762  _uninitHandlers(proc);
763 
764  stringlen = strlen(proc->execName) + 1;
765  for(int32_t i = 1; i < proc->argc; i++) {
766  if(proc->argv[i] != NULL)
767  stringlen += strlen(proc->argv[i]) + 1;
768  else
769  break;
770  }
771  CommandString = c_malloc(stringlen + 5);
772  if (CommandString == NULL)
773  return -3;
774  sprintf(CommandString, "\"%s\"", proc->execName);
775  for(int32_t i = 1; i < proc->argc; i++) {
776  if(proc->argv[i] != NULL) {
777  strcat(CommandString, " ");
778  strcat(CommandString, proc->argv[i]);
779  }
780  else
781  break;
782  }
783 
784  proc->jobObject = CreateJobObject(NULL, NULL);
785  if(proc->jobObject == NULL) {
786  proc->lastErr = GetLastError();
787  fName = "CreateJobObject";
789  goto QuitFunction;
790  }
791  jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_BREAKAWAY_OK;
792  if(SetInformationJobObject(proc->jobObject, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli)) == FALSE) {
793  proc->lastErr = GetLastError();
794  fName = "SetInformationJobObject";
796  goto QuitFunction;
797  }
798  //proc->startInfo.cb = sizeof(proc->startInfo);
799  si.cb = sizeof(si);
800 
801  // create env:
802  // Create an array of strings, sort it with qsort and put it
803  // in the format required by windows.
804  if (_mapToStringArray(&proc->environment, &envarray, &envc) != 0) {
805  fName = "mapToStringArray";
807  goto QuitFunction;
808  }
809 
810  if (envc > 0) {
811  size_t envsz = 1; // NULL-TERMINATING byte
812  qsort(envarray, envc, sizeof(*envarray), _compareStringsInsensitive);
813  for (size_t e = 0; e < envc; e++) {
814  const char *var = envarray[e];
815  size_t addsz = strlen(var) + 1;
816  env = c_realloc(env, envsz + addsz);
817  if (env == NULL) {
818  fName = "<AllocateEnv>";
819  proc->lastErr = 1;
821  goto QuitFunction;
822  }
823  strcpy(env + envsz - 1, var); // remove NULL-TERM byte from count
824  envsz += addsz;
825  }
826  env[envsz - 1] = '\0';
827  }
828 
829  ok = CreateProcessA(NULL,
830  (LPSTR)CommandString,
831  NULL, // process security attributes
832  NULL, // primary thread security attributes
833  TRUE, // handles are inherited
834  CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB | CREATE_NO_WINDOW, // creation flags
835  env, // use parent's environment
836  NULL, // use parent's current directory
837  &si, // STARTUPINFO pointer
838  &proc->procInfo); // receives PROCESS_INFORMATION
839  if (!ok) {
840  proc->lastErr = GetLastError();
841  fName = "CreateProcess";
843  goto QuitFunction;
844  }
845 
846  if(AssignProcessToJobObject(proc->jobObject, proc->procInfo.hProcess) == FALSE) {
847  proc->lastErr = GetLastError();
848  fName = "AssignProcessToJobObject";
850  goto QuitFunction;
851  }
852 
853  ResumeThread(proc->procInfo.hThread);
854 
855 QuitFunction:
856  if(res != CAENPROC_RetCode_Success) {
857  logMsg(c_logger_Severity_DEBUG, "%s(): Function %s exited with code %"PRIu32, __func__, fName, (uint32_t)proc->lastErr);
858  c_endProcess(proc);
859  }
860  _freePointersArray(envarray, envc);
861  return res;
862 }
863 
864 static int32_t _endProcessWin(c_Process_t *proc) {
865  UINT ret = 0;
866  if (!_isProcValid(proc))
867  return -1;
868 
869  if (proc->procInfo.hProcess != NULL) {
870  WaitForSingleObject(proc->procInfo.hProcess, INFINITE);
871  TerminateProcess(proc->procInfo.hProcess, ret);
872  CloseHandle(proc->procInfo.hProcess);
873  }
874  if (proc->procInfo.hThread != NULL)
875  CloseHandle(proc->procInfo.hThread);
876  if (proc->jobObject != NULL) {
877  TerminateJobObject(proc->jobObject, ret);
878  CloseHandle(proc->jobObject);
879  }
880 
881  _uninitHandlers(proc);
882 
883  return 0;
884 }
885 
886 static int32_t _getNCPUWin(void) {
887  SYSTEM_INFO info;
888  GetSystemInfo(&info);
889  return info.dwNumberOfProcessors;
890 }
891 
892 #else
893 
894 // WARNING, for some reason code definition of _GNU_SOURCE doesn't work, one have to give it to gcc in the Makefile
895 //#define _GNU_SOURCE 1
896 static int32_t _startProcessLin(c_Process_t *proc) {
897  int32_t res;
898  char **tempenv = NULL;
899  char **envarray = NULL;
900  size_t envc = 0;
901 
902  // create env:
903  // Create an array of strings, sort it with qsort and put it
904  // in the format required by windows.
905  if ((res = _mapToStringArray(&proc->environment, &tempenv, &envc)) != 0) {
906  logMsg(c_logger_Severity_ERROR, "_mapToStringArray() failed with error %" PRIi32, res);
908  goto QuitFunction;
909  }
910 
911  // envarray must be NULL terminated.
912  envarray = c_malloc((envc + 1) * sizeof(*envarray));
913  if (envarray == NULL) {
914  logMsg(c_logger_Severity_ERROR, "Can't alloc array for process environment");
916  goto QuitFunction;
917  }
918  c_memcpy(envarray, tempenv, envc * sizeof(*envarray));
919  envarray[envc] = NULL;
920 
921  // NOTE: uncomment the following to redirect stdout and sterr of the process to
922  // file "outlog". Also you need to pass '&child_fd_actions' as third arg. of
923  // posix_spawn(...) to let it work.
924 // char *outlog = "/tmp/foo-log";
925 // posix_spawn_file_actions_t child_fd_actions;
926 // if ((res = posix_spawn_file_actions_init (&child_fd_actions)) != 0) {
927 // res = CAENPROC_RetCode_ProcessFail;
928 // goto QuitFunction;
929 // }
930 // if ((res = posix_spawn_file_actions_addopen (&child_fd_actions, 1, outlog,
931 // O_WRONLY | O_CREAT | O_TRUNC, 0644)) != 0) {
932 // res = CAENPROC_RetCode_ProcessFail;
933 // goto QuitFunction;
934 // }
935 // if ((res = posix_spawn_file_actions_adddup2 (&child_fd_actions, 1, 2)) != 0) {
936 // res = CAENPROC_RetCode_ProcessFail;
937 // goto QuitFunction;
938 // }
939 
940  if ((res = posix_spawn(&proc->pid, proc->execName, NULL, NULL, proc->argv, envarray)) != 0) {
941  logMsg(c_logger_Severity_ERROR, "posix_spawn() failed with error %" PRIi32, res);
943  goto QuitFunction;
944  }
945 
946 QuitFunction:
947  c_free(envarray);
948  _freePointersArray(tempenv, envc);
949  return res;
950 }
951 
952 static int32_t _endProcessLin(c_Process_t *proc) {
953  int32_t status;
954 
955  if(!_isProcValid(proc))
956  return -1;
957 
958  waitpid(proc->pid, &status, 0);
959  return 0;
960 }
961 
962 static int32_t _getNCPULin(void) {
963  int32_t ret;
964 #ifdef _SC_NPROCESSORS_ONLN
965  ret = sysconf(_SC_NPROCESSORS_ONLN);
966 #else
967  ret = -1;
968 #endif
969  return ret;
970 }
971 
972 #endif
973 
974 c_use_decl_annotations DIR* c_opendir(const char* name) {
975 #ifdef _WIN32
976  DIR* dir = NULL;
977  if (name && name[0]) {
978  size_t base_length = strlen(name);
979  // search pattern must end with suitable wildcard
980  const char* all = strchr("/\\", name[base_length - 1]) ? "*" : "/*";
981  if ((dir = c_malloc(sizeof(*dir))) != NULL && (dir->name = c_malloc(base_length + strlen(all) + 1)) != NULL) {
982  strcat(strcpy(dir->name, name), all);
983  dir->handle = (handle_type)_findfirst(dir->name, &dir->info);
984  if (dir->handle != -1) {
985  dir->result.d_name = 0;
986  } else { // rollback
987  c_free(dir->name);
988  c_free(dir);
989  dir = NULL;
990  }
991  } else { // rollback
992  if (dir != NULL)
993  c_free(dir->name);
994  c_free(dir);
995  dir = NULL;
996  errno = ENOMEM;
997  }
998  } else {
999  errno = EINVAL;
1000  }
1001  return dir;
1002 #else
1003  return opendir(name);
1004 #endif
1005 }
1006 
1007 int c_closedir(DIR* dir) {
1008 #ifdef _WIN32
1009  int result = -1;
1010  if (dir) {
1011  if (dir->handle != -1) {
1012  result = _findclose(dir->handle);
1013  }
1014  c_free(dir->name);
1015  c_free(dir);
1016  }
1017  if (result == -1) { // map all errors to EBADF
1018  errno = EBADF;
1019  }
1020  return result;
1021 #else
1022  return closedir(dir);
1023 #endif
1024 }
1025 
1026 c_use_decl_annotations struct dirent* c_readdir(DIR* dir) {
1027 #ifdef _WIN32
1028  struct dirent* result = NULL;
1029  if (dir && dir->handle != -1) {
1030  if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
1031  result = &dir->result;
1032  result->d_name = dir->info.name;
1033  }
1034  } else {
1035  errno = EBADF;
1036  }
1037  return result;
1038 #else
1039  return readdir(dir);
1040 #endif
1041 }
1042 
1043 void c_rewinddir(DIR* dir) {
1044 #ifdef _WIN32
1045  if (dir && dir->handle != -1) {
1046  _findclose(dir->handle);
1047  dir->handle = (handle_type)_findfirst(dir->name, &dir->info);
1048  dir->result.d_name = 0;
1049  }
1050  else {
1051  errno = EBADF;
1052  }
1053 #else
1054  rewinddir(dir);
1055 #endif
1056 }
1057 
1058 int32_t c_startProcess(c_Process_t *proc) {
1059 #ifdef _WIN32
1060  return _startProcessWin(proc);
1061 #else
1062  return _startProcessLin(proc);
1063 #endif
1064 }
1065 
1066 int32_t c_endProcess(c_Process_t *proc) {
1067 #ifdef _WIN32
1068  return _endProcessWin(proc);
1069 #else
1070  return _endProcessLin(proc);
1071 #endif
1072 }
1073 
1075  c_map_iter_t iter = c_map_iter();
1076  if(proc == NULL)
1077  return;
1078 
1079  for(int32_t i = 0; i < proc->argc; i++) {
1080  if(proc->argv[i] == NULL)
1081  break;
1082  c_free(proc->argv[i]);
1083  }
1084  // Free environment
1085  for (const char* key = c_map_next(&proc->environment, &iter); key != NULL; key = c_map_next(&proc->environment, &iter)) {
1086  char **v = c_map_get(&proc->environment, key);
1087  if (v != NULL)
1088  c_free(*v);
1089  }
1090  c_map_deinit(&proc->environment);
1091 
1092  c_free(proc->execName);
1093  proc->argc=0;
1094 
1095 #ifdef _WIN32
1096  _uninitHandlers(proc);
1097 #endif
1098 }
1099 
1101 #ifdef _WIN32
1102  return _getNCPUWin();
1103 #else
1104  return _getNCPULin();
1105 #endif
1106 }
1107 
1108 int32_t c_pause(void) {
1109  puts("Press any key to continue...");
1110  if (c_getch() == 0) {} // to avoid warn_unused_result
1111  return MP_code_Success;
1112 }
1113 
1114 int32_t c_mkdir(const char* path) {
1115 #ifdef _WIN32
1116  const int r = _mkdir(path);
1117 #else
1118  const int r = mkdir(path, ACCESSPERMS);
1119 #endif
1120  return (r == 0 || errno == EEXIST) ? 0 : -1;
1121 }
1122 
1123 int32_t c_mkdirp(const char *dir) {
1124  char *tmp = c_strdup(dir);
1125  size_t len;
1126  char dirseps[] = DIRSEPS_STR;
1127  int32_t ret = 0;
1128 
1129  len = strlen(tmp);
1130 
1131  // remove trailing separators (if present)
1132  while (strchr(dirseps, tmp[len - 1]) != NULL) {
1133  tmp[len - 1] = '\0';
1134  // check if 'len' is already 0, if yes the path
1135  // is invalid.
1136  if (len == 0) {
1137  c_free(tmp);
1138  return -1;
1139  }
1140  len--;
1141  }
1142 
1143  // parse the string creating every directory we find
1144  for (char *p = tmp + 1; *p != '\0'; ++p) {
1145  if (strchr(dirseps, *p) != NULL) {
1146  char csave = *p;
1147  *p = '\0';
1148  c_mkdir(tmp);
1149  *p = csave;
1150 
1151  // go ahead until we exhaust consecutive dirseps
1152  for (; (p[1] != '\0') && strchr(dirseps, p[1]) != NULL; ++p);
1153  }
1154  }
1155  ret = c_mkdir(tmp);
1156  c_free(tmp);
1157  return ret;
1158 }
1159 
1160 int32_t c_dlload(const char* libName, c_libhandle_t *libHandle) {
1161  int32_t ret = MP_code_Success;
1162 #ifdef _WIN32
1163  *libHandle = LoadLibraryA(libName);
1164 #else
1165  size_t linuxLibNameSize = 3 + strlen(libName) + 3 + 1;
1166  char *linuxLibName = c_malloc(linuxLibNameSize);
1167  if (linuxLibName == NULL)
1168  return MP_code_Generic;
1169  snprintf(linuxLibName, linuxLibNameSize, "lib%s.so", libName);
1170  *libHandle = dlopen(linuxLibName, RTLD_LAZY);
1171  c_free(linuxLibName);
1172  char *error = dlerror();
1173  if (error != NULL) {
1174  logMsg(c_logger_Severity_ERROR, "%s", error);
1175  *libHandle = NULL;
1176  }
1177 #endif
1178  if (*libHandle == NULL) {
1179  ret = MP_code_LibLoad;
1180  _decodeError(__func__, ret);
1181  }
1182  return ret;
1183 }
1184 
1185 int32_t c_dfload(c_libhandle_t hLib, const char* fcnName, c_fcnhandle_t *funcPtr) {
1186  int32_t ret = MP_code_Success;
1187 #ifdef _WIN32
1188  *funcPtr = GetProcAddress(hLib, fcnName);
1189  if (*funcPtr == NULL) {
1190  logMsg(c_logger_Severity_ERROR, "GetProcAddress failed. GetLastError = %"PRIu32".", (uint32_t)GetLastError());
1191  }
1192 #else
1193  dlerror(); // clear any current error
1194  *funcPtr = dlsym(hLib, fcnName);
1195  char* error = dlerror();
1196  if (error != NULL) {
1197  logMsg(c_logger_Severity_ERROR, "%s", error);
1198  *funcPtr = NULL;
1199  }
1200 #endif
1201  if (*funcPtr == NULL) {
1202  ret = MP_code_LibLoad;
1203  _decodeError(__func__, ret);
1204  }
1205  return ret;
1206 }
1207 
1208 int32_t c_dlclose(c_libhandle_t hLib) {
1209  int32_t ret = MP_code_Success;
1210 #ifdef _WIN32
1211  if (!FreeLibrary(hLib))
1212  ret = MP_code_LibClose;
1213 #else
1214  dlclose(hLib);
1215  char *error = dlerror();
1216  if (error != NULL) {
1217  logMsg(c_logger_Severity_ERROR, "%s", error);
1218  ret = MP_code_LibClose;
1219  }
1220 #endif
1221  if (ret != MP_code_Success)
1222  _decodeError(__func__, ret);
1223  return ret;
1224 }
1225 
1226 static void _tmToSystemtime(const struct tm* src, c_systemtime_t *dest, uint16_t millis) {
1227  dest->wYear = (unsigned short)(src->tm_year) + 1900;
1228  dest->wMonth = (unsigned short)(src->tm_mon) + 1;
1229  dest->wDay = (unsigned short)(src->tm_mday);
1230  dest->wHour = (unsigned short)(src->tm_hour);
1231  dest->wMinute = (unsigned short)(src->tm_min);
1232  dest->wSecond = (unsigned short)(src->tm_sec);
1233  dest->wDayOfWeek = (unsigned short)(src->tm_wday);
1234  // assume milliseconds already range from 0:999
1235  dest->wMilliseconds = millis;
1236 
1237  // tm accepts seconds from 0->60, SYSTEMTIME from 0->59
1238  if (src->tm_sec == 60) {
1239  dest->wMinute++;
1240  dest->wSecond = 0;
1241  }
1242 }
1243 
1244 static void _systemtimeToTm(const c_systemtime_t* src, struct tm* dest) {
1245  dest->tm_year = src->wYear - 1900;
1246  dest->tm_mon = src->wMonth - 1;
1247  dest->tm_mday = src->wDay;
1248  dest->tm_hour = src->wHour;
1249  dest->tm_min = src->wMinute;
1250  dest->tm_sec = src->wSecond;
1251  dest->tm_wday = src->wDayOfWeek;
1252  dest->tm_isdst = 0; //this field must be initialized in order to avoid a random usage of DST as an offset of 3600 seconds
1253  //see https://en.cppreference.com/w/c/chrono/mktime
1254  //"A negative value of arg->tm_isdst causes mktime to attempt to determine if Daylight Saving Time was in effect in the specified time"
1255 }
1256 
1258 #ifdef _WIN32
1259  GetLocalTime(now);
1260 #else
1261  struct tm *timeinfo;
1262  struct timespec spec;
1263  uint16_t millis = 0;
1264  clock_gettime(CLOCK_REALTIME, &spec);
1265  timeinfo = localtime(&spec.tv_sec);
1266  millis = (uint16_t)(spec.tv_nsec / 1.e6);
1267  _tmToSystemtime(timeinfo, now, millis);
1268 #endif
1269 }
1270 
1272 #ifdef _WIN32
1273  GetSystemTime(t);
1274 #else
1275  struct tm *timeinfo;
1276  struct timespec spec;
1277  uint16_t millis = 0;
1278  clock_gettime(CLOCK_REALTIME, &spec);
1279  timeinfo = gmtime(&spec.tv_sec);
1280  millis = (uint16_t)(spec.tv_nsec / 1.e6);
1281  _tmToSystemtime(timeinfo, t, millis);
1282 #endif
1283 }
1284 
1286  double res;
1287 
1288 #ifdef _WIN32
1289  /*
1290  * According to SYSTEMTIME documentation, we have to:
1291  * - Convert the SYSTEMTIME structure to a FILETIME structure.
1292  * - Copy the resulting FILETIME structure to a ULARGE_INTEGER structure.
1293  * - Use normal 64-bit arithmetic on the ULARGE_INTEGER value.
1294  *
1295  * According to FILETIME documentation, it contains a 64-bit value
1296  * representing the number of 100-nanosecond intervals since
1297  * January 1, 1601 (UTC). So, it must be divide by 1.e7 to obtain seconds.
1298  *
1299  * See:
1300  * - https://docs.microsoft.com/it-it/windows/win32/api/minwinbase/ns-minwinbase-systemtime
1301  * - https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime
1302  */
1303 
1304  FILETIME f1, f2;
1305  ULARGE_INTEGER u1, u2;
1306 
1307  if (!(SystemTimeToFileTime(s1, &f1) && SystemTimeToFileTime(s2, &f2))) {
1308  logMsg(c_logger_Severity_ERROR, "SystemTimeToFileTime failed. GetLastError = %"PRIu32".", (uint32_t)GetLastError());
1309  return nan("");
1310  }
1311 
1312  u1.HighPart = f1.dwHighDateTime;
1313  u1.LowPart = f1.dwLowDateTime;
1314  u2.HighPart = f2.dwHighDateTime;
1315  u2.LowPart = f2.dwLowDateTime;
1316 
1317  /*
1318 
1319  */
1320  res = (u2.QuadPart - u1.QuadPart) / 1.e7;
1321 #else
1322  struct tm tm1, tm2;
1323  time_t t1, t2;
1324  /*
1325  * mdiff can also be negative, but this is ok... see comment
1326  * below on _systemtimeToTm() function.
1327  */
1328  int16_t mdiff = (int16_t)s2->wMilliseconds - (int16_t)s1->wMilliseconds;
1329  _systemtimeToTm(s1, &tm1);
1330  _systemtimeToTm(s2, &tm2);
1331  t1 = mktime(&tm1);
1332  t2 = mktime(&tm2);
1333  res = difftime(t2, t1);
1334  /*
1335  * Also consider millis, considering that _systemtimeToTm()
1336  * function truncates to seconds.
1337  */
1338  res += (mdiff / 1000.0);
1339 #endif
1340 
1341  return res;
1342 }
1343 
1344 #ifndef _WIN32
1345 // To be replaced with C11 _Generic, when available.
1346 // 32 bit
1347 #if UINT32_MAX == UINT_MAX
1348 #define _gcc_builtin(function, value) __builtin_##function(value)
1349 #elif UINT32_MAX == ULONG_MAX
1350 #define _gcc_builtin(function, value) __builtin_##function##l(value)
1351 #elif UINT32_MAX == ULLONG_MAX
1352 #define _gcc_builtin(function, value) __builtin_##function##ll(value)
1353 #else
1354 #error cannot implement GCC 32-bit builtin functions
1355 #endif
1356 // 64 bit
1357 #if UINT64_MAX == UINT_MAX
1358 #define _gcc_builtin_64(function, value) __builtin_##function(value)
1359 #elif UINT64_MAX == ULONG_MAX
1360 #define _gcc_builtin_64(function, value) __builtin_##function##l(value)
1361 #elif UINT64_MAX == ULLONG_MAX
1362 #define _gcc_builtin_64(function, value) __builtin_##function##ll(value)
1363 #else
1364 #error cannot implement GCC 64-bit builtin functions
1365 #endif
1366 #endif
1367 
1368 #ifdef _WIN32
1369 #ifdef _is_machine_x86_64
1370 /*
1371  * The assembly instructin popcnt is available only since SSE4.
1372  *
1373  * On MSVC __popcnt is not like GCC __builtin_popcount: the former generates
1374  * the assembly instruction popcnt, while the latter is smarter, and generates
1375  * a code independent on the target machine: it generates popcnt only if SSE4
1376  * is available, and uses a specific fallback library function in the other cases.
1377  *
1378  * This implementation is copied from MSVC std::popcount, available on C++20.
1379  * __AVX__ defined implies SSE4 support.
1380  *
1381  * Fallback implementation copied from https://stackoverflow.com/a/109025/3287591
1382  * Interestingly, GCC 10 and Clang 10 recognize this pattern and emit popcnt
1383  * if available on the target architecture.
1384  */
1385 
1386 static bool _definitely_have_popcnt() {
1387 #ifndef __AVX__
1388  extern int __isa_available;
1389  return (__isa_available >= __ISA_AVAILABLE_SSE42);
1390 #else
1391  return true;
1392 #endif
1393 }
1394 #endif
1395 static uint32_t _popcount_fallback(uint32_t value) {
1396  value -= (value >> 1) & UINT32_C(0x55555555);
1397  value = (value & UINT32_C(0x33333333)) + ((value >> 2) & UINT32_C(0x33333333));
1398  value = (value + (value >> 4)) & UINT32_C(0x0F0F0F0F);
1399  return (value * UINT32_C(0x01010101)) >> 24;
1400 }
1401 
1402 static uint32_t _popcount64_fallback(uint64_t value) {
1403  value -= (value >> 1) & UINT64_C(0x5555555555555555);
1404  value = (value & UINT64_C(0x3333333333333333)) + ((value >> 2) & UINT64_C(0x3333333333333333));
1405  value = (value + (value >> 4)) & UINT64_C(0x0F0F0F0F0F0F0F0F);
1406  return (value * UINT64_C(0x0101010101010101)) >> 56;
1407 }
1408 #endif
1409 
1410 // See https://stackoverflow.com/questions/355967/how-to-use-msvc-intrinsics-to-get-the-equivalent-of-this-gcc-code
1411 c_use_decl_annotations uint32_t c_ctz(uint32_t value) {
1412 #ifdef _WIN32
1413  DWORD res;
1414  // value == 0 -> BitScanForward return false, res undefined
1415  return BitScanForward(&res, (DWORD)value) ? (uint32_t)(res) : UINT32_C(32);
1416 #else
1417  // value == 0 -> __builtin_ctz undefined
1418  return (value != 0) ? (uint32_t)_gcc_builtin(ctz, value) : UINT32_C(32);
1419 #endif
1420 }
1421 
1422 // See https://stackoverflow.com/questions/355967/how-to-use-msvc-intrinsics-to-get-the-equivalent-of-this-gcc-code
1423 c_use_decl_annotations uint32_t c_clz(uint32_t value) {
1424 #ifdef _WIN32
1425  DWORD res;
1426  // value == 0 -> BitScanReverse return false, res undefined
1427  return BitScanReverse(&res, (DWORD)value) ? (uint32_t)(UINT32_C(31) - res) : UINT32_C(32);
1428 #else
1429  // value == 0 -> __builtin_clz undefined
1430  return (value != 0) ? (uint32_t)_gcc_builtin(clz, value) : UINT32_C(32);
1431 #endif
1432 }
1433 
1434 c_use_decl_annotations uint32_t c_popcount(uint32_t value) {
1435 #ifdef _WIN32
1436 #ifdef _is_machine_x86_64
1437  if (!_definitely_have_popcnt())
1438  return _popcount_fallback(value);
1439  return __popcnt(value);
1440 #else
1441  return _popcount_fallback(value);
1442 #endif
1443 #else
1444  return (uint32_t)_gcc_builtin(popcount, value);
1445 #endif
1446 }
1447 
1448 c_use_decl_annotations uint32_t c_ctz64(uint64_t value) {
1449 #ifdef _WIN32
1450  DWORD res;
1451  // value == 0 -> BitScanForward64 return false, res undefined
1452  return BitScanForward64(&res, (DWORD64)value) ? (uint32_t)(res) : UINT32_C(64);
1453 #else
1454  // value == 0 -> __builtin_ctz undefined
1455  return (value != 0) ? (uint32_t)_gcc_builtin_64(ctz, value) : UINT32_C(64);
1456 #endif
1457 }
1458 
1459 c_use_decl_annotations uint32_t c_clz64(uint64_t value) {
1460 #ifdef _WIN32
1461  DWORD res;
1462  // value == 0 -> BitScanReverse64 return false, res undefined
1463  return BitScanReverse64(&res, (DWORD64)value) ? (uint32_t)(UINT32_C(63) - res) : UINT32_C(64);
1464 #else
1465  // value == 0 -> __builtin_clz undefined
1466  return (value != 0) ? (uint32_t)_gcc_builtin_64(clz, value) : UINT32_C(64);
1467 #endif
1468 }
1469 
1470 c_use_decl_annotations uint32_t c_popcount64(uint64_t value) {
1471 #ifdef _WIN32
1472 #ifdef _is_machine_x86_64
1473  if (!_definitely_have_popcnt())
1474  return _popcount64_fallback(value);
1475 #ifdef _M_IX86
1476  // __popcnt64 available only on x64
1477  return __popcnt((uint32_t)(value >> 32)) + __popcnt((uint32_t)(value));
1478 
1479 #else
1480  return (uint32_t)__popcnt64((DWORD64)value);
1481 #endif
1482 #else
1483  return _popcount64_fallback(value);
1484 #endif
1485 #else
1486  return (uint32_t)_gcc_builtin_64(popcount, value);
1487 #endif
1488 }
1489 
1490 // Used to convert result of frexp to integer.
1491 // In IEEE 754 implementation this can be equal to 2^DBL_MANT_DIG (2^53), to avoid precision loss.
1492 // Anyway, we use a custom number to be sure the same conversion is used in all the architectures.
1493 // The exponent can be any number between DBL_MANT_DIG and 63.
1494 static const double twop60 = 1.152921504606846976e+18; // 2^(+60) "0x43b00000, 0x00000000"
1495 static const double twom60 = 8.673617379884035472e-19; // 2^(-60) "0x3c300000, 0x00000000"
1496 
1497 c_use_decl_annotations int64_t c_frexp(double value, int16_t *exponent, int8_t *classification) {
1498  if (c_unlikely(exponent == NULL || classification == NULL))
1499  return 0;
1500 
1501  // Get normalized fraction and exponent
1502  int exp;
1503  double norm_fraction;
1504  int64_t mantissa;
1505 
1506  // Get the classification and handle special cases
1507  switch (fpclassify(value)) {
1508  case FP_INFINITE:
1509  *classification = CAEN_FP_INFINITE;
1510  exp = 0;
1511  // Mantissa is used to carry the sign.
1512  mantissa = signbit(value) ? INT64_C(-1) : INT64_C(1);
1513  break;
1514  case FP_NAN:
1515  *classification = CAEN_FP_NAN;
1516  exp = 0;
1517  // Mantissa is used to carry the sign.
1518  // NaN payload is discarded.
1519  // signbit() is the only function that allow to get NaN sign
1520  mantissa = signbit(value) ? INT64_C(-1) : INT64_C(1);
1521  break;
1522  case FP_ZERO:
1523  *classification = CAEN_FP_ZERO;
1524  exp = 0;
1525  // Mantissa is used to carry the sign.
1526  mantissa = signbit(value) ? INT64_C(-1) : INT64_C(1);
1527  break;
1528  case FP_SUBNORMAL:
1529  *classification = CAEN_FP_SUBNORMAL;
1530  // Get integer mantissa
1531  norm_fraction = frexp(value, &exp);
1532  mantissa = (int64_t)(norm_fraction * twop60);
1533  break;
1534  case FP_NORMAL:
1535  default:
1536  *classification = CAEN_FP_NORMAL;
1537  // Get integer mantissa
1538  norm_fraction = frexp(value, &exp);
1539  mantissa = (int64_t)(norm_fraction * twop60);
1540  }
1541 
1542  *exponent = (int16_t)exp;
1543 
1544  return mantissa;
1545 }
1546 
1547 c_use_decl_annotations double c_ldexp(int64_t mantissa, int16_t exponent, int8_t classification) {
1548 
1549  // Get normalized fraction from integer mantissa
1550  const double norm_fraction = (double)mantissa * twom60;
1551  double value;
1552 
1553  // Get the classification and handle special cases
1554  switch (classification) {
1555  case CAEN_FP_INFINITE:
1556  // Mantissa is used to carry the sign.
1557  // NOTE: norm_fraction should have the same sign as mantissa, so we don't have to cast it
1558  value = copysign(HUGE_VAL, norm_fraction);
1559  break;
1560  case CAEN_FP_NAN:
1561  // Mantissa is used to carry the sign.
1562  // NaN payload is discarded.
1563  // copysign() is the only function that allow to set NaN sign
1564  // NOTE: norm_fraction should have the same sign as mantissa, so we don't have to cast it
1565  value = copysign(NAN, norm_fraction);
1566  break;
1567  case CAEN_FP_ZERO:
1568  // Mantissa is used to carry the sign.
1569  value = copysign(0., norm_fraction);
1570  break;
1571  case CAEN_FP_SUBNORMAL:
1572  case CAEN_FP_NORMAL:
1573  default:
1574  // Get the value from normalized fraction and exponent
1575  value = ldexp(norm_fraction, (int)exponent);
1576  }
1577 
1578  return value;
1579 }
1580 
1581 
1582 /*
1583  * Copied from "XGetopt - A Unix-compatible getopt() for MFC and Win32" v1.2, by Hans Dietrich,
1584  * released in the public domain.
1585  * https://www.codeproject.com/Articles/1940/XGetopt-A-Unix-compatible-getopt-for-MFC-and-Win
1586  */
1587 #ifdef _WIN32 // On Linux, defined by unistd.h
1588 
1589 int c_mp_opterr = 0; // global err. (unused)
1590 int c_mp_optopt = 0; // global opt. err.
1591 int c_mp_optind = 0; // global argv index
1592 char *c_mp_optarg; // global argument pointer
1593 
1594 c_use_decl_annotations int c_mp_getopt(int argc, char *const *argv, const char *options) {
1595  static char *next = NULL;
1596  if (c_mp_optind == 0)
1597  next = NULL;
1598 
1599  c_mp_optarg = NULL;
1600 
1601  if (next == NULL || *next == '\0') {
1602  if (c_mp_optind == 0)
1603  c_mp_optind++;
1604 
1605  if (c_mp_optind >= argc || argv[c_mp_optind][0] != '-' || argv[c_mp_optind][1] == '\0') {
1606  c_mp_optarg = NULL;
1607  if (c_mp_optind < argc)
1608  c_mp_optarg = argv[c_mp_optind];
1609  return -1;
1610  }
1611 
1612  if (strcmp(argv[c_mp_optind], "--") == 0) {
1613  c_mp_optind++;
1614  c_mp_optarg = NULL;
1615  if (c_mp_optind < argc)
1616  c_mp_optarg = argv[c_mp_optind];
1617  return -1;
1618  }
1619 
1620  next = argv[c_mp_optind];
1621  next++; // skip past -
1622  c_mp_optind++;
1623  }
1624 
1625  char c = *next++;
1626  char *cp = strchr(options, c);
1627 
1628  if (cp == NULL || c == ':') {
1629  c_mp_optopt = c;
1630  return '?';
1631  }
1632 
1633  cp++;
1634  if (*cp == ':') {
1635  if (*next != '\0') {
1636  c_mp_optarg = next;
1637  next = NULL;
1638  }
1639  else if (c_mp_optind < argc) {
1640  c_mp_optarg = argv[c_mp_optind];
1641  c_mp_optind++;
1642  }
1643  else {
1644  c_mp_optopt = c;
1645  return '?';
1646  }
1647  }
1648 
1649  return c;
1650 }
1651 
1652 #endif
1653 
1655  if (c_unlikely(fname == NULL))
1656  return FALSE;
1657  FILE * const f = fopen(fname, "r");
1658  if (f == NULL)
1659  return FALSE;
1660  return (fclose(f) == 0);
1661 }
1662 
1664  time_t rawtime;
1665  struct tm * timeinfo;
1666  char datetimestr[sizeof("YYYYMMDDHHMMSS")];
1667  char p1[sizeof("YYYYMMDD")];
1668  char p2[sizeof("HHMMSS")];
1669  int tmp;
1670  uint64_t res;
1671  size_t nc;
1672 
1673  time(&rawtime);
1674  timeinfo = localtime(&rawtime);
1675  strftime(datetimestr, sizeof(datetimestr), "%Y%m%d%H%M%S", timeinfo);
1676  nc = sizeof(p1) - 1;
1677  strncpy(p1, datetimestr, nc);
1678  p1[nc] = '\0';
1679  nc = sizeof(p2) - 1;
1680  strncpy(p2, datetimestr + sizeof(p1) - 1, nc);
1681  p2[nc] = '\0';
1682  tmp = atoi(p1);
1683  res = (uint64_t)tmp * 1000000;
1684  tmp = atoi(p2);
1685  res += tmp;
1686  return res;
1687 }
1688 
1689 int32_t c_remAllFiles(const char *dir) {
1690  struct dirent *de;
1691  DIR *dr = c_opendir(dir);
1692  char *path = c_malloc(1024); // HACK no check on size
1693  struct stat st;
1694  int32_t ret = 0;
1695 
1696  if (dr == NULL || path == NULL) {
1697  logMsg(c_logger_Severity_WARNING, "Can't alloc filename or open directory %s. Files won't be deleted.", dir);
1698  ret = -1;
1699  goto QuitFunction;
1700  }
1701 
1702  while ((de = c_readdir(dr)) != NULL) {
1703  const char *fname = de->d_name;
1704  if (!strcmp(fname, ".") || !strcmp(fname, "..")) // skip self and prev. dir.
1705  continue;
1706  sprintf(path, "%s" DIRSEP0 "%s", dir, fname);
1707  if (stat(path, &st) != 0) {
1708  logMsg(c_logger_Severity_WARNING, "Error on stat(%s)", path);
1709  ret = -2;
1710  goto QuitFunction;
1711  }
1712  if (st.st_mode & S_IFDIR) {
1713  if (c_remAllFiles(path) != 0) {
1714  ret = -3;
1715  goto QuitFunction;
1716  }
1717  if (c_rmdir(path) != 0) {
1718  logMsg(c_logger_Severity_WARNING, "Error on rmdir(%s)", path);
1719  ret = -4;
1720  goto QuitFunction;
1721  }
1722  }
1723  else {
1724  if (remove(path) != 0) {
1725  logMsg(c_logger_Severity_WARNING, "Error on remove(%s)", path);
1726  ret = -5;
1727  goto QuitFunction;
1728  }
1729  }
1730  }
1731 
1732 QuitFunction:
1733  if (dr != NULL)
1734  c_closedir(dr);
1735  c_free(path);
1736  return ret;
1737 }
1738 
1739 
1740 int32_t c_killProcess(const char *procName) {
1741  int32_t ret = 0;
1742 
1743  // len(basename) is <= len(procName)
1744  char *fname = c_malloc(strlen(procName) + 1);
1745  if (fname == NULL)
1746  return -1;
1747 
1748  c_getBaseName(fname, procName);
1749 
1750 #ifdef _WIN32
1751  CONST HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
1752  PROCESSENTRY32 pEntry;
1753  pEntry.dwSize = sizeof(pEntry);
1754  CONST DWORD currPID = GetCurrentProcessId();
1755 
1756  for (BOOL hRes = Process32First(hSnapShot, &pEntry); hRes; hRes = Process32Next(hSnapShot, &pEntry)) {
1757  // do not kill himself!
1758  if (strcmp(pEntry.szExeFile, fname) == 0 && pEntry.th32ProcessID != currPID) {
1759  HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, 0, pEntry.th32ProcessID);
1760  if (hProcess != NULL) {
1761  TerminateProcess(hProcess, 9);
1762  CloseHandle(hProcess);
1763  }
1764  }
1765  }
1766 
1767  CloseHandle(hSnapShot);
1768 
1769 #else
1770  char buf[KILL_BUFSIZE];
1771  FILE *fp;
1772  char *cmd = NULL;
1773 
1774  const char *format = "ps -A | grep -w '%s' | grep -v 'grep' | awk '{print $1}'";
1775 
1776  // Check size to allocate, and allocate
1777  int needed = snprintf(NULL, 0, format, fname);
1778  if (needed < 0) {
1779  ret = -1;
1780  goto QuitFunction;
1781  }
1782 
1783  logMsg(c_logger_Severity_DEBUG, "c_killProcess: Needed %d chars", needed);
1784  cmd = c_malloc(needed + 1);
1785  if (cmd == NULL) {
1786  ret = -1;
1787  goto QuitFunction;
1788  }
1789 
1790  sprintf(cmd, format, fname);
1791  logMsg(c_logger_Severity_DEBUG, "c_killProcess: cmd = '%s'", cmd);
1792 
1793  if ((fp = popen(cmd, "r")) == NULL) {
1794  logMsg(c_logger_Severity_WARNING, "c_killProcess: Error opening pipe!");
1795  ret = -1;
1796  goto QuitFunction;
1797  }
1798 
1799  while (fgets(buf, KILL_BUFSIZE, fp) != NULL) {
1800  int64_t pid;
1801  int64_t this_pid = c_getpid();
1802  if (sscanf(buf, "%" SCNd64, &pid) == 1) {
1803  if (pid != this_pid) {
1804  logMsg(c_logger_Severity_DEBUG, "c_killProcess: Killing PID %" PRIi64, pid);
1805  int32_t l_ret = kill((pid_t)pid, SIGKILL);
1806  if (ret == 0 && l_ret != 0)
1807  ret = l_ret;
1808  }
1809  else {
1810  logMsg(c_logger_Severity_DEBUG, "c_killProcess: Will not kill PID %" PRIi64 " (this process).", pid);
1811  }
1812  }
1813  else {
1814  logMsg(c_logger_Severity_WARNING, "c_killProcess: Can't parse PID from string %s", buf);
1815  }
1816  }
1817 
1818  if (pclose(fp)) {
1819  logMsg(c_logger_Severity_WARNING, "c_killProcess: Can't close pipe.");
1820  ret = -1;
1821  goto QuitFunction;
1822  }
1823 
1824 QuitFunction:
1825  c_free(cmd);
1826 #endif
1827  c_free(fname);
1828  return ret;
1829 }
1830 
1831 #undef _is_machine_x86_64
int32_t c_pause(void)
static const double twop60
static void _freePointersArray(char **arr, size_t size)
void c_freeProc(c_Process_t *proc)
DIR * c_opendir(const char *name)
Not marked CAEN_UTILITY_API (meaningful only on 32-bit Windows), likely an original sin more than a c...
Corresponds to FP_INFINITE defined by math.h.
static int32_t _endProcessLin(c_Process_t *proc)
int32_t c_dfload(c_libhandle_t hLib, const char *fcnName, c_fcnhandle_t *funcPtr)
int32_t c_dlload(const char *libName, c_libhandle_t *libHandle)
static void * c_memcpy(void *__restrict dest, const void *__restrict src, size_t size)
Wrapper to memcpy(dest, src, size), with check for NULL arguments.
#define c_mp_optind
int c_closedir(DIR *dir)
int c_access(const char *pathname, int mode)
#define INFINITE
unsigned short wMilliseconds
uint32_t c_clz(uint32_t value)
void c_free(void *ptr)
int c_kbhit(void)
void * c_calloc(size_t nmemb, size_t size)
bool c_checkFileExists(const char *fname)
static const double twom60
int32_t c_killProcess(const char *procName)
#define c_unused_parameter(P)
Platform independent macro to remove unreferenced parameter warning.
Definition: CAENUtility.h:117
int64_t c_getpid(void)
#define DIRSEP0
int32_t c_dlclose(c_libhandle_t hLib)
char * c_getcwd(char *buf, size_t size)
#define _getch()
int32_t c_mkdirp(const char *dir)
Same of c_mkdir() but creates also intermediate directories.
int32_t c_mkdir(const char *path)
#define c_mp_opterr
#define c_map_next(m, iter)
Definition: CAENMap.h:117
static void _systemtimeToTm(const c_systemtime_t *src, struct tm *dest)
void * c_fcnhandle_t
#define max(a, b)
uint32_t c_ctz(uint32_t value)
int32_t c_getCurrentEnvironment(c_environment_t *dest)
uint64_t c_get_time(void)
Get time in milliseconds since 00:00:00 UTC, January 1, 1970.
double c_DiffSystemTime_sec(const c_systemtime_t *s1, const c_systemtime_t *s2)
#define _gcc_builtin_64(function, value)
static void _tmToSystemtime(const struct tm *src, c_systemtime_t *dest, uint16_t millis)
int c_rmdir(const char *path)
#define c_likely(x)
Definition: CAENUtility.h:292
void c_GetSystemTime(c_systemtime_t *t)
uint32_t c_popcount64(uint64_t value)
#define _gcc_builtin(function, value)
int32_t c_endProcess(c_Process_t *proc)
int32_t c_getBaseName(char *dest, const char *source)
char * c_getExpandedString(const char *filename)
int32_t c_addProcEnv(const char *varname, const char *varvalue, c_Process_t *proc)
void c_rewinddir(DIR *dir)
Corresponds to FP_SUBNORMAL defined by math.h.
#define _kbhit()
int32_t c_setProcExec(const char *exec, c_Process_t *proc)
size_t c_malloc_size(void *ptr)
#define DIRSEPS_CHAR
#define c_attribute_nonnull(...)
Definition: CAENUtility.h:213
static c_libhandle_t libHandle
Definition: CAENXMLParser.c:50
int c_strncasecmp(const char *s1, const char *s2, size_t n)
Corresponds to FP_NORMAL defined by math.h.
Hash table. Implementation from rxi&#39;s "map" project.
unsigned short wSecond
c_environment_t environment
#define c_unlikely(x)
Definition: CAENUtility.h:293
#define c_map_set(m, key, value)
Definition: CAENMap.h:85
#define TRUE
#define c_map_size(m)
Definition: CAENMap.h:101
Iterator.
Definition: CAENMapTypes.h:58
#define c_map_iter()
Definition: CAENMap.h:108
#define FALSE
static int32_t _getNCPULin(void)
void * c_malloc(size_t size)
double c_ldexp(int64_t mantissa, int16_t exponent, int8_t classification)
int32_t c_addProcArg(const char *arg, c_Process_t *proc)
#define KILL_BUFSIZE
Generic wrappers to platform-dependent functions.
static void _decodeError(const char *functName, int32_t resCode)
#define INIT_C_LOGGER(fname, mname)
Definition: CAENLogger.h:139
#define c_map_get(m, key)
Definition: CAENMap.h:75
#define c_mp_optarg
#define c_map_deinit(m)
Definition: CAENMap.h:66
c_Process_t * c_newProc(void)
void c_GetLocalTime(c_systemtime_t *now)
#define c_mp_optopt
int c_getch(void)
unsigned short wMinute
int32_t c_startProcess(c_Process_t *proc)
Logger implemented in C.
#define c_mp_getopt
int c_chdir(const char *path)
#define logMsg(s,...)
Definition: CAENLogger.h:157
static char * _strndup(const char *str, size_t size)
#define c_map_init(m, nreserved)
Definition: CAENMap.h:51
#define DIRSEPS_STR
#define c_use_decl_annotations
Definition: CAENUtility.h:278
int c_strcasecmp(const char *s1, const char *s2)
Corresponds to FP_ZERO defined by math.h.
int64_t c_frexp(double value, int16_t *exponent, int8_t *classification)
char * argv[100+1]
char * c_strndup(const char *str, size_t size)
Wrapper to strndup(str, size), with check for NULL arguments. To be freed with c_free().
struct dirent * c_readdir(DIR *dir)
Not marked CAEN_UTILITY_API (meaningful only on 32-bit Windows), likely an original sin more than a c...
void * c_realloc(void *ptr, size_t size)
Corresponds to FP_NAN defined by math.h.
static int32_t _mapToStringArray(c_environment_t *map, char ***dest, size_t *count)
char * c_strdup(const char *str)
Wrapper to strdup(str), with check for NULL arguments. To be freed with c_free(). ...
void * c_libhandle_t
int32_t c_getNCPU(void)
uint32_t c_clz64(uint64_t value)
unsigned short wDayOfWeek
uint64_t c_getCurrentTimeRepresentation(void)
uint32_t c_ctz64(uint64_t value)
int32_t c_remAllFiles(const char *dir)
#define MAX_PROC_NARGS
static bool _isProcValid(c_Process_t *proc)
int32_t c_getDirName(char *dest, const char *source)
static int32_t _startProcessLin(c_Process_t *proc)
uint32_t c_popcount(uint32_t value)