P99
p99_constraint.h
Go to the documentation of this file.
1 /* This may look like nonsense, but it really is -*- mode: C; coding: utf-8 -*- */
2 /* */
3 /* Except for parts copied from previous work and as explicitly stated below, */
4 /* the author and copyright holder for this work is */
5 /* (C) copyright 2012-2014, 2018 Jens Gustedt, INRIA, France */
6 /* */
7 /* This file is free software; it is part of the P99 project. */
8 /* */
9 /* Licensed under the Apache License, Version 2.0 (the "License"); */
10 /* you may not use this file except in compliance with the License. */
11 /* You may obtain a copy of the License at */
12 /* */
13 /* http://www.apache.org/licenses/LICENSE-2.0 */
14 /* */
15 /* Unless required by applicable law or agreed to in writing, software */
16 /* distributed under the License is distributed on an "AS IS" BASIS, */
17 /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
18 /* See the License for the specific language governing permissions and */
19 /* limitations under the License. */
20 /* */
21 #ifndef P99_CONSTRAINT_H
22 #define P99_CONSTRAINT_H
23 #include "p99_bitset.h"
24 
35 p99_inline size_t strerrorlen_s(errno_t p00_errnum);
36 
40 p99_inline size_t strnlen_s(const char *p00_s, size_t p00_maxsize);
41 
42 
48 errno_t strerror_s(char *p00_s, rsize_t p00_maxsize, errno_t p00_errnum);
49 
50 P99_WEAK(exit_handler_s)
51 void exit_handler_s(const char * restrict p00_msg,
52  void * restrict p00_ptr,
53  errno_t p00_err);
54 
61 P99_WEAK(ignore_handler_s)
62 void ignore_handler_s(const char * restrict p00_msg,
63  void * restrict p00_ptr,
64  errno_t p00_err);
65 
78 P99_WEAK(abort_handler_s)
79 void abort_handler_s(const char * restrict p00_msg,
80  void * restrict p00_ptr,
81  errno_t p00_err);
82 
83 
84 typedef
85 void (*constraint_handler_t)(const char * restrict p00_msg,
86  void * restrict p00_ptr,
87  errno_t p00_err);
88 
93 constraint_handler_t set_constraint_handler_s(constraint_handler_t handler);
94 
95 
96 P99_DECLARE_THREAD_LOCAL(char_cptr, p00_jmp_buf_file);
97 P99_DECLARE_THREAD_LOCAL(char_cptr, p00_jmp_buf_context);
98 P99_DECLARE_THREAD_LOCAL(char_cptr, p00_jmp_buf_info);
99 
100 #define P00_JMP_BUF_FILE P99_THREAD_LOCAL(p00_jmp_buf_file)
101 #define P00_JMP_BUF_CONTEXT P99_THREAD_LOCAL(p00_jmp_buf_context)
102 #define P00_JMP_BUF_INFO P99_THREAD_LOCAL(p00_jmp_buf_info)
103 
104 P99_CONSTANT(int, p00_ilen10, 256);
105 
106 #if __STDC_WANT_LIB_EXT1__ && !defined(__STDC_LIB_EXT1__)
108 size_t strerrorlen_s(errno_t p00_errnum) {
109  return strlen(strerror(p00_errnum));
110 }
111 #endif
112 
113 #if (_XOPEN_SOURCE >= 600)
115 int p00_strerror(int p00_errname, size_t p00_maxsize, char p00_s[p00_maxsize]) {
116  /* This "feature" test macro for the bogus glibc version seems to be
117  the only possible. We get in trouble if we compile the library
118  against glibc, and then run the executable with another libc that
119  is POSIX compliant. All of a sudden we have an ABI breakage. We
120  try to get away with it by re-interpreting the return value as
121  integer. If it is small we suppose that we had an ABI breakage,
122  and return that small integer value. */
123 # if _GNU_SOURCE && __GLIBC__
124  char * p00_ret = strerror_r(p00_errname, p00_s, p00_maxsize);
125  if ((uintptr_t)p00_ret < 2048) return (intptr_t)p00_ret;
126  if (p00_ret != p00_s) {
127  if (strlen(p00_ret) < p00_maxsize) {
128  strcpy(p00_s, p00_ret);
129  } else {
130  errno = EDOM;
131  return -1;
132  }
133  }
134  return 0;
135 # else
136  return strerror_r(p00_errname, p00_s, p00_maxsize);
137 # endif
138 }
139 #elif __STDC_WANT_LIB_EXT1__ && defined(__STDC_LIB_EXT1__)
141 int p00_strerror(int p00_errname, size_t p00_maxsize, char p00_s[p00_maxsize]) {
142  return strerror_s(p00_s, p00_maxsize, p00_errname);
143 }
144 #else
146 int p00_strerror(int p00_errname, size_t p00_maxsize, char p00_s[p00_maxsize]) {
147  size_t p00_len = strerrorlen_s(p00_errname);
148  size_t p00_ret = 0;
149  if (p00_len > p00_maxsize - 1) {
150  p00_len = p00_maxsize - 1;
151  p00_ret = EDOM;
152  }
153  strncpy(p00_s, strerror(p00_errname), p00_len);
154  p00_s[p00_len] = 0;
155  return p00_ret;
156 }
157 #endif
158 
160 char const* p00_strerror_r(int p00_errname, size_t p00_maxsize, char p00_s[p00_maxsize]) {
161  p00_strerror(p00_errname, p00_maxsize, p00_s);
162  return p00_s;
163 }
164 
165 #define P00_STRERROR2(E, STR) p00_strerror(E, sizeof(STR), STR)
166 
167 #define P99_STRERROR_MAX 256
168 
169 #define p00_strerror(...) \
170 P99_IF_LT(P99_NARG(__VA_ARGS__), 2) \
171 (p00_strerror(__VA_ARGS__, P99_STRERROR_MAX, (char[P99_STRERROR_MAX]))) \
172 (P99_IF_LT(P99_NARG(__VA_ARGS__), 3) \
173  (P00_STRERROR2(__VA_ARGS__)) \
174  (p00_strerror(__VA_ARGS__)))
175 
176 #if __STDC_WANT_LIB_EXT1__ && !defined(__STDC_LIB_EXT1__)
177 P99_WEAK(p99_constraint_handler)
178 void p99_constraint_handler(const char * restrict p00_msg,
179  void * restrict p00_ptr,
180  errno_t p00_err);
181 
182 
185 size_t strnlen_s(const char *p00_s, size_t p00_maxsize) {
186  size_t p00_ret = p00_maxsize;
187  if (p00_s && p00_maxsize) {
188  char const* p00_pos = memchr(p00_s, 0, p00_maxsize);
189  if (p00_pos) p00_ret = p00_pos - p00_s;
190  }
191  return p00_ret;
192 }
193 
195 errno_t strerror_s(char *p00_s, rsize_t p00_maxsize, errno_t p00_errnum) {
196  if (!p00_maxsize || p00_maxsize > RSIZE_MAX)
197  p99_constraint_handler(
198  ", call to strerror_s, dynamic constraint violation",
199  0,
200  EINVAL);
201  // strerror_r may set errno
202  errno_t p00_back = errno;
203  errno = 0;
204  int p00_ret = p00_strerror(p00_errnum, p00_maxsize, p00_s);
205  switch (p00_ret) {
206  case 0: break;
207  case EDOM: {
208  /* The output string has been shortend to fit into p00_s. */
209  p00_ret = 0;
210  if (p00_maxsize > 3) memset(p00_s + (p00_maxsize - 4), '.', 3);
211  break;
212  }
213  case EINVAL: {
214  /* p00_errnum didn't correspond to a valid error number. */
215  p00_ret = snprintf(p00_s, p00_maxsize, "unknown <%d> error", p00_errnum);
216  if (!p00_ret || (p00_ret < p00_maxsize)) {
217  p00_ret = 0;
218  if (p00_maxsize > 3) memset(p00_s + (p00_maxsize - 4), '.', 3);
219  }
220  break;
221  }
222  default:
223  fprintf(stderr, "strerror_r returned %d\n", p00_ret);
224  break;
225  }
226  /* Unconditionally set errno to the previous value. */
227  errno = p00_back;
228  return p00_ret;
229 }
230 #endif
231 
233 char const* p00_strerror_s(char *p00_s, rsize_t p00_maxsize, errno_t p00_errnum) {
234  strerror_s(p00_s, p00_maxsize, p00_errnum);
235  return p00_s;
236 }
237 
238 # define P00_STRERROR(E, S, STR) p00_strerror_s(STR, S, E)
239 #else
240 # define P00_STRERROR(...) p00_strerror_r(__VA_ARGS__)
241 #endif
242 
243 #define P00_STRERROR02(E, STR) P00_STRERROR(E, sizeof(STR), STR)
244 
245 #define P99_STRERROR(...) \
246 P99_IF_LT(P99_NARG(__VA_ARGS__), 2) \
247 (P00_STRERROR(__VA_ARGS__, P99_STRERROR_MAX, (char[P99_STRERROR_MAX]){ 0 })) \
248 (P99_IF_LT(P99_NARG(__VA_ARGS__), 3) \
249  (P00_STRERROR02(__VA_ARGS__)) \
250  (P00_STRERROR(__VA_ARGS__)))
251 
253 void p00_constraint_report(errno_t p00_cond, char const* p00_file, char const* p00_context, char const* p00_info) {
254  char p00_str[p00_ilen10] = P99_INIT;
255  if (!p00_context) p00_context = P00_JMP_BUF_CONTEXT;
256  if (!p00_context) p00_context = "<unknown function>";
257  if (!p00_info) p00_info = P00_JMP_BUF_INFO;
258  if (!p00_file) p00_file = P00_JMP_BUF_FILE;
259  if (!p00_file) p00_file = "<unknown location>";
260  fputs(p00_context, stderr);
261  fputc(':', stderr);
262  fputs(p00_file, stderr);
263  fputs(": ", stderr);
264  if (p00_info) {
265  fputs(p00_info, stderr);
266  fputs(", ", stderr);
267  }
268  fputs("exception ", stderr);
269  {
270  char const*const p00_errname = p99_errno_getname(p00_cond);
271  if (p00_errname) {
272  fputs(p00_errname, stderr);
273  fputc('=', stderr);
274  }
275  }
276  {
277  sprintf(p00_str, "%d", p00_cond);
278  fputs(p00_str, stderr);
279  }
280  if (!p00_cond && errno) p00_cond = errno;
281  P99_STRERROR(p00_cond, p00_str);
282  if (p00_str[0]) {
283  fputs(", library error: ", stderr);
284  fputs(p00_str, stderr);
285  }
286  fputc('\n', stderr);
287 }
288 
290 void p99_report_handler(const char * restrict p00_msg,
291  void * restrict p00_ptr,
292  errno_t p00_err) {
293  P99_UNUSED(p00_ptr);
294  p00_constraint_report(p00_err, 0, 0, p00_msg);
295 }
296 
298 void p99_ignore_handler(const char * restrict p00_msg,
299  void * restrict p00_ptr,
300  errno_t p00_err) {
301  P99_UNUSED(p00_msg);
302  P99_UNUSED(p00_ptr);
303  P99_UNUSED(p00_err);
304 }
305 
306 noreturn
308 void p99_abort_handler(const char * restrict p00_msg,
309  void * restrict p00_ptr,
310  errno_t p00_err) {
311  P99_UNUSED(p00_ptr);
312  p00_constraint_report(p00_err, 0, 0, p00_msg);
313  fputs("runtime constraint violation: ", stderr);
314  abort();
315 }
316 
317 noreturn
319 void p99_exit_handler(const char * restrict p00_msg,
320  void * restrict p00_ptr,
321  errno_t p00_err) {
322  P99_UNUSED(p00_ptr);
323  P99_UNUSED(p00_err);
324  p00_constraint_report(p00_err, 0, 0, p00_msg);
325  fputs("runtime constraint violation: ", stderr);
326  exit(EXIT_FAILURE);
327 }
328 
329 #ifndef P99_CONSTRAINT_HANDLER
330 # define P99_CONSTRAINT_HANDLER exit_handler_s
331 #endif
332 
333 #if __STDC_WANT_LIB_EXT1__
334 
336 
337 P99_WEAK(report_handler_s)
338 void report_handler_s(const char * restrict p00_msg,
339  void * restrict p00_ptr,
340  errno_t p00_err);
341 
342 P99_WEAK(exit_handler_s)
343 void exit_handler_s(const char * restrict p00_msg,
344  void * restrict p00_ptr,
345  errno_t p00_err);
346 
347 P99_WEAK(p00_constraint_handler)
348 _Atomic(constraint_handler_t) p00_constraint_handler = ATOMIC_VAR_INIT(P99_CONSTRAINT_HANDLER);
349 
350 P99_WEAK(report_handler_s)
351 void report_handler_s(const char * restrict p00_msg,
352  void * restrict p00_ptr,
353  errno_t p00_err) {
354  p99_report_handler(p00_msg, p00_ptr, p00_err);
355 }
356 
357 P99_WEAK(exit_handler_s)
358 noreturn
359 void exit_handler_s(const char * restrict p00_msg,
360  void * restrict p00_ptr,
361  errno_t p00_err) {
362  p99_exit_handler(p00_msg, p00_ptr, p00_err);
363 }
364 
365 P99_WEAK(p99_constraint_handler)
366 void p99_constraint_handler(const char * restrict p00_msg,
367  void * restrict p00_ptr,
368  errno_t p00_err) {
369  constraint_handler_t p00_func = atomic_load_explicit(&p00_constraint_handler, memory_order_acquire);
370  if (p00_func) p00_func(p00_msg, p00_ptr, p00_err);
371 }
372 
373 
374 # ifndef __STDC_LIB_EXT1__
375 
376 P99_WEAK(ignore_handler_s)
377 void ignore_handler_s(const char * restrict p00_msg,
378  void * restrict p00_ptr,
379  errno_t p00_err) {
380  p99_ignore_handler(p00_msg, p00_ptr, p00_err);
381 }
382 
383 P99_WEAK(abort_handler_s)
384 noreturn
385 void abort_handler_s(const char * restrict p00_msg,
386  void * restrict p00_ptr,
387  errno_t p00_err) {
388  p99_abort_handler(p00_msg, p00_ptr, p00_err);
389 }
390 
392 constraint_handler_t set_constraint_handler_s(constraint_handler_t p00_hand) {
393  if (!p00_hand) p00_hand = P99_CONSTRAINT_HANDLER;
394  return atomic_exchange_explicit(&p00_constraint_handler, p00_hand, memory_order_acq_rel);
395 }
396 
397 # endif
398 
400 errno_t p00_constraint_call(errno_t p00_cond, char const* p00_file, char const* p00_context, char const* p00_info) {
401  if (p00_cond) {
402  if (p00_file) P00_JMP_BUF_FILE = p00_file;
403  if (p00_context) P00_JMP_BUF_CONTEXT = p00_context;
404  /* Ensure that all dependent data for this error has been */ \
405  /* synchronized. */ \
406  atomic_thread_fence(memory_order_seq_cst); \
407  p99_constraint_handler(p00_info, 0, p00_cond);
408  }
409  return p00_cond;
410 }
411 
412 #define P00_CONSTRAINT_INFO(F) ", call to " #F ", dynamic constraint violation"
413 
414 
415 #define P99_CONSTRAINT_TRIGGER(E, I) \
416 p00_constraint_call((E), P99_STRINGIFY(__LINE__), __func__, I)
417 
418 #define P00_CONSTRAINT_CALL3(F, I, C) \
419 P99_CONSTRAINT_TRIGGER(F C, I)
420 
421 #define P00_CONSTRAINT_CALL1(F) P00_CONSTRAINT_CALL3(F, P00_CONSTRAINT_INFO(F), ())
422 
423 #define P00_CONSTRAINT_CALL0(F, ...) P00_CONSTRAINT_CALL3(F, P00_CONSTRAINT_INFO(F), (__VA_ARGS__))
424 
425 #define P99_CONSTRAINT_CALL(...) \
426 P99_IF_LT(P99_NARG(__VA_ARGS__), 2) \
427 (P00_CONSTRAINT_CALL1(__VA_ARGS__)) \
428 (P00_CONSTRAINT_CALL0(__VA_ARGS__))
429 
430 
431 # if !defined(__STDC_LIB_EXT1__) || defined(P00_DOXYGEN)
432 
434 errno_t p00_memcpy_s(_Bool p00_overlap,
435  void * restrict p00_s1, rsize_t p00_s1max,
436  const void * restrict p00_s2, rsize_t p00_n) {
437  errno_t p00_ret = 0;
438  if (!p00_s1) { p00_ret = EINVAL; goto P00_SEVERE; }
439  if (p00_s1max > RSIZE_MAX) { p00_ret = EDOM; goto P00_SEVERE; }
440  if (!p00_s2) { p00_ret = EINVAL; goto P00_ERR; }
441  if (p00_n > RSIZE_MAX || p00_n > p00_s1max) { p00_ret = EDOM; goto P00_ERR; }
442  if (!p00_overlap) {
443  unsigned char const* p00_c1 = p00_s1;
444  unsigned char const* p00_c2 = p00_s2;
445  /* Strictly speaking all this range check is UB if the regions don't
446  overlap. */
447  if ((p00_c1 <= p00_c2 && p00_c2 < (p00_c1 + p00_s1max))
448  || (p00_c2 <= p00_c1 && p00_c1 < (p00_c2 + p00_n))) {
449  p00_ret = EINVAL; goto P00_ERR;
450  }
451  memcpy(p00_s1, p00_s2, p00_n);
452  } else {
453  memmove(p00_s1, p00_s2, p00_n);
454  }
455  return 0;
456 P00_ERR:
457  if (p00_s1max) memset(p00_s1, 0, p00_s1max);
458 P00_SEVERE:
459  return p00_ret;
460 }
461 
463 # define memcpy_s(S1, S1MAX, S2, N) P99_CONSTRAINT_TRIGGER(p00_memcpy_s(false, (S1), (S1MAX), (S2), (N)), "memcpy_s runtime constraint violation")
464 
465 # define memmove_s(S1, S1MAX, S2, N) P99_CONSTRAINT_TRIGGER(p00_memcpy_s(true, (S1), (S1MAX), (S2), (N)), "memmove_s runtime constraint violation")
466 
468 errno_t p00_strcpy_s(void * restrict p00_s1, rsize_t p00_s1max,
469  const void * restrict p00_s2) {
470  size_t p00_len = strnlen_s(p00_s2, p00_s1max) + 1;
471  return p00_memcpy_s(false, p00_s1, p00_s1max, p00_s2, p00_len);
472 }
473 
475 # define strcpy_s(S1, S1MAX, S2) P99_CONSTRAINT_TRIGGER(p00_strcpy_s((S1), (S1MAX), (S2)), "strcpy_s runtime constraint violation")
476 
478 errno_t p00_strncpy_s(char * restrict p00_s1,
479  rsize_t p00_s1max,
480  const char * restrict p00_s2,
481  rsize_t p00_n) {
482  if (!p00_s1 || !p00_s1max || p00_s1max > RSIZE_MAX) return memcpy_s(p00_s1, p00_s1max, p00_s2, p00_n);
483  if (!p00_s2 || p00_n > RSIZE_MAX) return memcpy_s(p00_s1, p00_s1max, p00_s2, p00_n);
484  /* The maximum string length that we can copy is p00_n - 1. */
485  size_t p00_len = strnlen_s(p00_s2, p00_n - 1) + 1;
486  /* If the target can't hold the string, the standard demands to
487  abort the copy operation. */
488  if (p00_s1max < p00_len) return memcpy_s(p00_s1, p00_s1max, 0, 0);
489  /* Now copy and force null termination. */
490  size_t p00_ret = memcpy_s(p00_s1, p00_s1max, p00_s2, p00_len - 1);
491  p00_s1[p00_len - 1] = 0;
492  return p00_ret;
493 }
494 
495 # define strncpy_s(S1, S1MAX, S2, N) P99_CONSTRAINT_TRIGGER(p00_strncpy_s((S1), (S1MAX), (S2), (N)), "strncpy_s runtime constraint violation")
496 
498 errno_t p00_strcat_s(char * restrict p00_s1,
499  rsize_t p00_s1max,
500  const char * restrict p00_s2) {
501  if (!p00_s1 || !p00_s1max || p00_s1max > RSIZE_MAX) return memcpy_s(p00_s1, p00_s1max, p00_s2, 0);
502  if (!p00_s2) return memcpy_s(p00_s1, p00_s1max, p00_s2, 0);
503  size_t p00_l1 = strnlen_s(p00_s1, p00_s1max);
504  /* Check if p00_s1 had been null terminated */
505  if (p00_l1 >= p00_s1max) return memcpy_s(p00_s1, 1, 0, 0);
506  else p00_s1max -= p00_l1;
507  /* Only look into the string that could fit after the current
508  contents of p00_s1. */
509  size_t p00_l2 = strnlen_s(p00_s2, p00_s1max) + 1;
510  if (p00_l2 > p00_s1max) return memcpy_s(p00_s1, 1, p00_s2, 0);
511  size_t p00_ret = strncpy_s(p00_s1 + p00_l1, p00_s1max, p00_s2, p00_l2);
512  if (p00_ret) p00_s1[0] = 0;
513  return p00_ret;
514 }
515 
517 # define strcat_s(S1, S1MAX, S2) P99_CONSTRAINT_TRIGGER(p00_strcat_s((S1), (S1MAX), (S2)), "strcat_s runtime constraint violation")
518 
520 errno_t p00_strncat_s(char * restrict p00_s1,
521  rsize_t p00_s1max,
522  const char * restrict p00_s2,
523  rsize_t p00_n) {
524  if (!p00_s1 || !p00_s1max || p00_s1max > RSIZE_MAX) return memcpy_s(p00_s1, p00_s1max, p00_s2, 0);
525  if (!p00_s2 || p00_n > RSIZE_MAX) return memcpy_s(p00_s1, p00_s1max, p00_s2, 0);
526  size_t p00_l1 = strnlen_s(p00_s1, p00_s1max);
527  /* Check if p00_s1 had been null terminated */
528  if (p00_l1 >= p00_s1max) return memcpy_s(p00_s1, 1, 0, 0);
529  else p00_s1max -= p00_l1;
530  /* Only look into the string that could fit after the current
531  contents of p00_s1. */
532  size_t p00_l2 = strnlen_s(p00_s2, p00_n) + 1;
533  if (p00_l2 > p00_s1max) return memcpy_s(p00_s1, 1, p00_s2, 0);
534  size_t p00_ret = strncpy_s(p00_s1 + p00_l1, p00_s1max, p00_s2, p00_l2);
535  if (p00_ret) p00_s1[0] = 0;
536  return p00_ret;
537 }
538 
540 # define strncat_s(S1, S1MAX, S2, N) P99_CONSTRAINT_TRIGGER(p00_strncat_s((S1), (S1MAX), (S2), (N)), "strncat_s runtime constraint violation")
541 
543 void *p00_bsearch_s(char const* p00_file, char const* p00_context,
544  const void *p00_key, const void *p00_base,
545  rsize_t p00_nmemb, rsize_t p00_size,
546  int (*p00_compar)(const void *, const void *, void *),
547  void *p00_ctx) {
548  if (p00_nmemb) {
549  if (p00_nmemb > RSIZE_MAX || p00_size > RSIZE_MAX) {
550  p00_constraint_call(EDOM, p00_file, p00_context, "bsearch_s runtime constraint violation");
551  } else if (p00_nmemb && (!p00_key || !p00_base || !p00_compar)) {
552  p00_constraint_call(EINVAL, p00_file, p00_context, "bsearch_s runtime constraint violation");
553  } else {
554  register unsigned char const (*p00_dbase)[p00_size] = (void*)p00_base;
555  /* bot and top will always be the maximal (minimal) index that
556  is known to be smaller (larger) than the search item. This
557  strategy here even works when p00_nmemb == (rsize_t)-1. The
558  wonders of unsigned arithmetic... */
559  for (register size_t p00_bot = -1, p00_top = p00_nmemb;
560  (p00_top - p00_bot) != 1;) {
561  /* unsigned arithmetic just wraps around, so this is ok */
562  register size_t p00_med = (p00_bot + p00_top) / 2;
563  /* We always have the assertions */
564  /* assert((p00_bot == p00_top && p00_top == (rsize_t)-1) */
565  /* || (p00_top - p00_bot > 1)); */
566  /* assert(p00_med < p00_top); */
567  /* assert(p00_bot < p00_med || (p00_bot == (rsize_t)-1 && p00_med != (rsize_t)-1); */
568  int co = p00_compar(p00_key, p00_dbase + p00_med, p00_ctx);
569  if (!co) return (void*)(p00_dbase + p00_med);
570  else if (co < 0) p00_top = p00_med;
571  else p00_bot = p00_med;
572  }
573  }
574  }
575  return 0;
576 }
577 
579 #define bsearch_s(...) p00_bsearch_s(P99_STRINGIFY(__LINE__), __func__, __VA_ARGS__)
580 
582 errno_t p00_getenv_s(char const* p00_file, char const* p00_context,
583  size_t * restrict p00_len,
584  char * restrict p00_value, rsize_t p00_maxsize,
585  const char * restrict p00_name) {
586  errno_t p00_ret = 0;
587  size_t p00_le = 0;
588  if (p00_maxsize > RSIZE_MAX) {
589  p00_ret = EDOM;
590  p00_constraint_call(p00_ret, p00_file, p00_context, "getenv_s runtime constraint violation");
591  } else if (!p00_name || (p00_maxsize && !p00_value)) {
592  p00_ret = EINVAL;
593  p00_constraint_call(p00_ret, p00_file, p00_context, "getenv_s runtime constraint violation");
594  } else {
595  char const*const p00_found = getenv(p00_name);
596  if (p00_found) {
597  p00_le = strlen(p00_found);
598  if (p00_le < p00_maxsize) strcpy(p00_value, p00_found);
599  } else {
600  if (p00_maxsize) p00_value[0] = '\0';
601  }
602  }
603  if (p00_len) *p00_len = p00_le;
604  return p00_ret;
605 }
606 
608 #define getenv_s(...) p00_getenv_s(P99_STRINGIFY(__LINE__), __func__, __VA_ARGS__)
609 
611 errno_t p00_tmpfile_s(char const* p00_file, char const* p00_context,
612  FILE * restrict * restrict p00_streamptr) {
613  errno_t p00_ret = 0;
614  if (!p00_streamptr) {
615  p00_ret = EINVAL;
616  } else {
617  *p00_streamptr = tmpfile();
618  if (!*p00_streamptr) {
619  p00_ret = errno;
620  errno = 0;
621  }
622  }
623  if (p00_ret) p00_constraint_call(p00_ret, p00_file, p00_context, "tmpfile_s runtime constraint violation");
624  return p00_ret;
625 }
626 
628 #define tmpfile_s(...) p00_tmpfile_s(P99_STRINGIFY(__LINE__), __func__, __VA_ARGS__)
629 
631 errno_t p00_tmpnam_s(char const* p00_file, char const* p00_context,
632  char *p00_s, rsize_t p00_maxsize) {
633  errno_t p00_ret = 0;
634  if (p00_maxsize > RSIZE_MAX) {
635  p00_ret = EDOM;
636  } else if (!p00_s) {
637  p00_ret = EINVAL;
638  } else {
639  char p00_b[L_tmpnam] = P99_INIT;
640  char * p00_r = tmpnam(p00_b);
641  char p00_p = p00_b[L_tmpnam - 1];
642  p00_b[L_tmpnam - 1] = 0;
643  size_t p00_l = strlen(p00_b);
644  if (!p00_r || ((p00_l == (L_tmpnam - 1)) && p00_p) || (p00_l >= p00_maxsize)) {
645  p00_ret = EDOM;
646  /* no name could be created or the result was too long */
647  p00_s[0] = 0;
648  } else {
649  strcpy(p00_s, p00_r);
650  }
651  }
652  if (p00_ret) p00_constraint_call(p00_ret, p00_file, p00_context, "tmpnam_s runtime constraint violation");
653  return p00_ret;
654 }
655 
657 #define tmpnam_s(...) p00_tmpnam_s(P99_STRINGIFY(__LINE__), __func__, __VA_ARGS__)
658 
660 char *p00_gets_s(char const* p00_file, char const* p00_context,
661  char *p00_s, rsize_t p00_n) {
662  errno_t p00_err = 0;
663  char* p00_ret = 0;
664  if (!p00_s) {
665  p00_err = EINVAL;
666  } else if (!p00_n) {
667  p00_s[0] = '\0';
668  p00_err = EINVAL;
669  } else if (p00_n > RSIZE_MAX) {
670  p00_s[0] = '\0';
671  p00_err = EDOM;
672  } else {
673  p00_ret = fgets(p00_s, p00_n - 1, stdin);
674  if (p00_ret) {
675  size_t p00_l = strlen(p00_s);
676  if (p00_s[p00_l - 1] == '\n') {
677  /* all went well, just zero out the EOL character */
678  p00_s[p00_l - 1] = '\0';
679  } else {
680  /* If the last character has not been a newline the only
681  possibility of valid input that the end of the stream has been
682  encountered. */
683  char p00_b[2];
684  char* p00_r = fgets(p00_b, 2, stdin);
685  if (p00_r) {
686  p00_s[0] = '\0';
687  p00_err = ENOBUFS;
688  }
689  }
690  }
691  }
692  if (p00_err) {
693  p00_constraint_call(p00_err, p00_file, p00_context, "gets_s runtime constraint violation");
694  return 0;
695  } else {
696  return p00_ret;
697  }
698 }
699 
701 #define gets_s(...) p00_gets_s(P99_STRINGIFY(__LINE__), __func__, __VA_ARGS__)
702 
705 bool p00_isin0(char p00_c,
706  rsize_t p00_s2l, uint8_t const p00_s2[const restrict p00_s2l]) {
707  if (p00_c)
708  for (rsize_t p00_i = 0; p00_i < (p00_s2l - 1); ++p00_i)
709  if (P99_UNLIKELY(p00_c == p00_s2[p00_i]))
710  return true;
711  return false;
712 }
713 
714 
717 rsize_t p99_span(rsize_t p00_s1l, uint8_t p00_s1[const restrict p00_s1l],
718  rsize_t p00_s2l, uint8_t const p00_s2[const restrict p00_s2l]) {
719  for (rsize_t p00_ret = 0; p00_ret < p00_s1l; ++p00_ret) {
720  if (!p00_s1[p00_ret] || !P00_ISIN(p00_s1[p00_ret], p00_s2l, p00_s2))
721  return p00_ret;
722  }
723  return p00_s1l;
724 }
725 
727 rsize_t p00_cskip(rsize_t p00_s1l, uint8_t p00_s1[const restrict p00_s1l],
728  rsize_t p00_s2l, uint8_t const p00_s2[const restrict p00_s2l]) {
729  rsize_t p00_ret = 0;
730  for (; p00_ret < p00_s1l && p00_s1[p00_ret]; ++p00_ret) {
731  if (P00_ISIN(p00_s1[p00_ret], p00_s2l, p00_s2)) {
732  p00_s1[p00_ret] = '\0';
733  return p00_ret + 1;
734  }
735  }
736  return p00_ret;
737 }
738 
740 uint8_t *p00_strtok_inner(
741  rsize_t * restrict p00_s1max,
742  uint8_t p00_ret0[restrict (*p00_s1max)],
743  rsize_t p00_s2max,
744  const uint8_t p00_s2[const restrict p00_s2max],
745  uint8_t ** restrict p00_ptr) {
746  /* We have to copy this to a "veritable" pointer because of a bug in
747  clang 3.0 that tells us "read-only variable is not assignable"
748  for the function parameter. */
749  register uint8_t * p00_ret = p00_ret0;
750  /* Skip delimiters at the beginning of the string. */
751  register size_t const p00_l = p99_span(*p00_s1max, p00_ret, p00_s2max, p00_s2);
752  if (p00_l < *p00_s1max) {
753  *p00_s1max -= p00_l;
754  p00_ret += p00_l;
755 
756  register size_t const p00_k = p00_cskip(*p00_s1max, p00_ret, p00_s2max, p00_s2);
757  /* The empty token is always returned as null pointer. */
758  if (p00_k && ((p00_k > 1) || (*p00_s1max == 1) || !p00_ret[1])) {
759  *p00_ptr = p00_ret + p00_k;
760  *p00_s1max -= p00_k;
761  return p00_ret;
762  }
763  }
764  return 0;
765 }
766 
768 char *p00_strtok_s(char const* p00_file, char const* p00_context,
769  rsize_t * restrict p00_s1max,
770  uint8_t p00_s1[restrict (*p00_s1max)],
771  rsize_t p00_s2max,
772  const uint8_t p00_s2[const restrict p00_s2max],
773  uint8_t ** restrict p00_ptr) {
774  errno_t p00_err = 0;
775  uint8_t *p00_ret = 0;
776  if (P99_UNLIKELY(!p00_s1max || !p00_s2 || !p00_ptr)) {
777  p00_err = EINVAL;
778  } else if (P99_UNLIKELY(*p00_s1max > RSIZE_MAX)) {
779  p00_err = EDOM;
780  } else if (P99_UNLIKELY(!p00_s1 && !*p00_ptr)) {
781  p00_err = EINVAL;
782  }
783  if (P99_UNLIKELY(p00_err)) {
784  p00_constraint_call(p00_err, p00_file, p00_context, "strtok_s runtime constraint violation");
785  } else {
786  if (P99_LIKELY(*p00_s1max)) {
787  /* If this is a new scan, initialize the machinery */
788  if (p00_s1)
789  p00_ret = p00_strtok_inner(p00_s1max, p00_s1, p00_s2max, p00_s2, p00_ptr);
790  else
791  p00_ret = p00_strtok_inner(p00_s1max, *p00_ptr, p00_s2max, p00_s2, p00_ptr);
792  }
793  }
794  return (char*)p00_ret;
795 }
796 
797 #define P00_STRSIZE(S) \
798 P99_GENERIC(S, \
799  sizeof(S), \
800  (char *, strlen(S)+1), \
801  (char const*, strlen(S)+1), \
802  (char volatile*, strlen(S)+1), \
803  (char volatile const*, strlen(S)+1), \
804  (char *restrict, strlen(S)+1), \
805  (char const*restrict, strlen(S)+1), \
806  (char volatile*restrict, strlen(S)+1), \
807  (char volatile const*restrict, strlen(S)+1) \
808  )
809 
810 
812 #define strtok_s(S1, S1MAX, S2, PTR) \
813 p00_strtok_s(P99_STRINGIFY(__LINE__), __func__, \
814  S1MAX, (uint8_t*restrict)(S1), \
815  P00_STRSIZE(S2), (uint8_t const*restrict)(S2), \
816  (uint8_t**)PTR)
817 
818 #define P00_SPAN_DECLARE(NAME, SET) \
819 P99_PURE_FUNCTION \
820 p99_inline \
821 rsize_t P99_PASTE2(p99_span_, NAME)(rsize_t p00_s1l, uint8_t p00_s1[const restrict p00_s1l]) { \
822  return p99_span(p00_s1l, p00_s1, sizeof(SET)-1, (uint8_t const*)(char const[]){ SET }); \
823 } \
824 p99_inline \
825 char* P99_PASTE2(p99_strtok_, NAME)(rsize_t * restrict p00_s1max, \
826  char p00_s1[restrict (*p00_s1max)], \
827  char ** restrict p00_ptr) { \
828  return strtok_s(p00_s1, p00_s1max, SET, p00_ptr); \
829 } \
830 P99_MACRO_END(P00_SPAN_DECLARE, NAME)
831 
832 
833 
834 P00_SPAN_DECLARE(blank, P00_BLANK);
835 P00_SPAN_DECLARE(space, P00_SPACE);
836 P00_SPAN_DECLARE(lower, P00_LOWER);
837 P00_SPAN_DECLARE(upper, P00_UPPER);
838 P00_SPAN_DECLARE(digit, P00_DIGIT);
839 P00_SPAN_DECLARE(xdigit, P00_XDIGIT);
840 P00_SPAN_DECLARE(alpha, P00_ALPHA);
841 P00_SPAN_DECLARE(alnum, P00_ALNUM);
842 
843 /* This only checks for conformance with the bounds specified in the
844  standard, not if this presents a valid date. */
846 bool p00_tm_valid(struct tm const* p00_tm) {
847  /* int to unsigned conversion is always well defined and arithmetic
848  for it wraps around nicely, So, in general the way that these
849  comparisons are done here will avoid checking for the lower
850  bound. */
851  return
852  ((61u > (unsigned)p00_tm->tm_sec) // seconds after the minute — [0, 60]
853  // may have leap second with value 60
854  + (60u > (unsigned)p00_tm->tm_min) // minutes after the hour — [0, 59]
855  + (24u > (unsigned)p00_tm->tm_hour) // hours since midnight — [0, 23]
856  + (32u > ((unsigned)p00_tm->tm_mday - 1u)) // day of the month — [1, 31]
857  + (12u > (unsigned)p00_tm->tm_mon) // months since January — [0, 11]
858  + (10000u > ((unsigned)p00_tm->tm_year + 1900u)) // years since 1900
859  + (7u > (unsigned)p00_tm->tm_wday) // days since Sunday — [0, 6]
860  + (366u > (unsigned)p00_tm->tm_yday)) // days since January 1 — [0, 365]
861  // may have leap day in leap years
862  == 8;
863 }
864 
866 errno_t p00_asctime_s(char const* p00_file, char const* p00_context,
867  char *p00_s, rsize_t p00_maxsize,
868  const struct tm *p00_tptr) {
869  errno_t p00_ret = 0;
870  if (P99_UNLIKELY(p00_maxsize < 26 || p00_maxsize > RSIZE_MAX)) {
871  p00_ret = EDOM;
872  } else if (P99_UNLIKELY(!p00_s || !p00_tptr)) {
873  p00_ret = EINVAL;
874  } else if (P99_UNLIKELY(!p00_tm_valid(p00_tptr))) {
875  p00_ret = EDOM;
876  } else {
877  /* Watch that the year string has exactly 4 characters. The
878  * standard lists this condition only for asctime (and makes it
879  * UB), but we should watch not to have buffer overflow, here.
880  */
881  unsigned short p00_val = 1900u + p00_tptr->tm_year;
882  if (1000 <= p00_val && p00_val <= 9999) {
883  char p00_year[6] = "XXXX";
884  snprintf(p00_year, 6, "%4hu", p00_val);
885  char const p00_wday[7][3] = {
886  "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
887  };
888  char const p00_mon[12][3] = {
889  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
890  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
891  };
892  snprintf(p00_s,
893  p00_maxsize,
894  "%.3s %.3s %2d %.2d:%.2d:%.2d %.4s\n",
895  p00_wday[p00_tptr->tm_wday],
896  p00_mon[p00_tptr->tm_mon],
897  p00_tptr->tm_mday,
898  p00_tptr->tm_hour,
899  p00_tptr->tm_min,
900  p00_tptr->tm_sec,
901  p00_year);
902  } else {
903  p00_ret = EDOM;
904  }
905  }
906  if (p00_ret) {
907  if (p00_s && p00_maxsize) p00_s[0] = 0;
908  p00_constraint_call(p00_ret, p00_file, p00_context, "asctime_s runtime constraint violation");
909  }
910  return p00_ret;
911 }
912 
914 #define asctime_s(...) p00_asctime_s(P99_STRINGIFY(__LINE__), __func__, __VA_ARGS__)
915 
917 struct tm* p00_localtime_s(char const* p00_file, char const* p00_context,
918  time_t const * restrict p00_t,
919  struct tm * p00_tptr) {
920  struct tm * p00_ret = 0;
921  errno_t p00_err = 0;
922  if (P99_UNLIKELY(!p00_t || !p00_tptr)) {
923  p00_err = EINVAL;
924  } else {
925  int byear = 99; // 1999 was a good year
926  *p00_tptr = (struct tm) {
927  .tm_mday = 1,
928  .tm_year = byear,
929  .tm_isdst = -1,
930  };
931  double p00_diff = difftime(*p00_t, mktime(p00_tptr));
932  int64_t p00_sec = p00_diff;
933  int64_t p00_min = p00_sec/60u;
934  p00_sec -= 60u * p00_min;
935  int64_t p00_hour = p00_min/60u;
936  p00_min -= 60u * p00_hour;
937  int64_t p00_day = p00_hour/24u;
938  p00_hour -= 24u * p00_day;
939  *p00_tptr = (struct tm) {
940  .tm_sec = p00_sec,
941  .tm_min = p00_min,
942  .tm_hour = p00_hour,
943  .tm_mday = p00_day + 1,
944  .tm_year = byear,
945  .tm_isdst = 0,
946  };
947  if (mktime(p00_tptr) == (time_t)-1) {
948  char date[26];
949  asctime_s(date, sizeof date, p00_tptr);
950  printf("warning, time might be before epoch:\t%s", date);
951  }
952  p00_ret = p00_tptr;
953  }
954  if (p00_err) {
955  p00_constraint_call(p00_err, p00_file, p00_context, "localtime_s runtime constraint violation");
956  }
957  return p00_ret;
958 }
959 
961 #define localtime_s(...) p00_localtime_s(P99_STRINGIFY(__LINE__), __func__, __VA_ARGS__)
962 
964 struct tm* p00_gmtime_s(char const* p00_file, char const* p00_context,
965  time_t const * restrict p00_t,
966  struct tm * p00_tptr) {
967  struct tm * p00_ret = 0;
968  errno_t p00_err = 0;
969  if (P99_UNLIKELY(!p00_t || !p00_tptr)) {
970  p00_err = EINVAL;
971  } else {
972  localtime_s(&(time_t) { *p00_t }, p00_tptr);
973  char p00_off[6];
974  /* Obtain the local offset from UTC in ISO format, such as
975  +0430. */
976  strftime(p00_off, sizeof p00_off, "%z", p00_tptr);
977  /* See if a time zone can be determined. */
978  if (p00_off[0]) {
979  /* First read the minutes from that. */
980  long p00_min = strtol(&p00_off[3], 0, 10);
981  /* Then the hours, including sign. */
982  p00_off[3] = 0;
983  long p00_hour = strtol(p00_off, 0, 10);
984  /* If the sign was - minutes must be accounted negative, too. */
985  if (p00_hour < 0L) p00_min = -p00_min;
986  p00_tptr->tm_hour -= p00_hour;
987  p00_tptr->tm_min -= p00_min;
988  mktime(p00_tptr);
989  }
990  p00_ret = p00_tptr;
991  }
992  if (p00_err) {
993  p00_constraint_call(p00_err, p00_file, p00_context, "gmtime_s runtime constraint violation");
994  }
995  return p00_ret;
996 }
997 
999 #define gmtime_s(...) p00_gmtime_s(P99_STRINGIFY(__LINE__), __func__, __VA_ARGS__)
1000 
1002 #define ctime_s(S, M, T) asctime_s((S), (M), localtime_s((T), &(struct tm){ 0 }))
1003 
1004 # endif
1005 
1012 #endif
char_cptr
char const * char_cptr
a const pointer to char
Definition: p99_typenames.h:91
void
void void
Definition: p99_bitset.h:84
rsize_t
size_t rsize_t
Definition: p99_libc.h:164
strerror
#define strerror(...)
Default arguments for C99 function strerror
Definition: p99_c99_default.h:35
P99_LIKELY
#define P99_LIKELY(...)
Mark the conditional expression as being likely.
Definition: p99_compiler.h:1010
P99_DECLARE_ATOMIC
#define P99_DECLARE_ATOMIC(...)
Definition: p99_atomic.h:56
strtol
#define strtol(...)
Default arguments for C99 function strtol
Definition: p99_c99_default.h:177
noreturn
#define noreturn
Declare a function that doesn't return to the caller.
Definition: p99_compiler.h:842
P99_CONSTANT
#define P99_CONSTANT(T, NAME, INIT)
define a compile time constant NAME of type T with value INIT
Definition: p99_enum.h:258
P99_STRERROR
#define P99_STRERROR(...)
Definition: p99_constraint.h:241
strerror_s
P00_CLAUSE2 call to strerror_s
Definition: p99_constraint.h:194
asctime_s
#define asctime_s(...)
p99_errno_getname
char const * p99_errno_getname(errno_t p00_err)
Return the name of an error condition.
Definition: p99_errno.h:198
EINVAL
P00_CLAUSE2 call to dynamic constraint EINVAL
Definition: p99_constraint.h:195
p99_inline
#define p99_inline
Try to force a function always to be inlined.
Definition: p99_compiler.h:496
P99_UNUSED
#define P99_UNUSED(...)
check if the list of expressions is syntactically valid but don't evaluate it
Definition: p99_enum.h:215
strnlen_s
size_t strnlen_s(const char *p00_s, size_t p00_maxsize)
computes the length of the string pointed to by s.
P99_WEAK
#define P99_WEAK(...)
Declare a symbol to be weak such that it can be provided several times without error.
Definition: p99_compiler.h:561
p99_exit_handler
void p99_exit_handler(const char *restrict p00_msg, void *restrict p00_ptr, errno_t p00_err)
Definition: p99_constraint.h:315
date
P00_CLAUSE2 time might be before date
Definition: p99_constraint.h:938
P99_INIT
#define P99_INIT
A catch all 0-initializer.
Definition: p99_int.h:1065
strerrorlen_s
size_t strerrorlen_s(errno_t p00_errnum)
calculates the length of the (untruncated) locale-specific message string that the strerror_s functio...
RSIZE_MAX
#define RSIZE_MAX
Definition: p99_libc.h:169
P99_DECLARE_THREAD_LOCAL
#define P99_DECLARE_THREAD_LOCAL
localtime_s
#define localtime_s(...)
Definition: p99_constraint.h:949
P99_CONSTRAINT_HANDLER
#define P99_CONSTRAINT_HANDLER
Definition: p99_constraint.h:326
p99_ignore_handler
void p99_ignore_handler(const char *restrict p00_msg, void *restrict p00_ptr, errno_t p00_err)
Definition: p99_constraint.h:294
p99_report_handler
void p99_report_handler(const char *restrict p00_msg, void *restrict p00_ptr, errno_t p00_err)
Definition: p99_constraint.h:286
P99_PURE_FUNCTION
#define P99_PURE_FUNCTION
On architectures that support this, assert that a function is "pure", i.e only depends on parameters ...
Definition: p99_compiler.h:612
P99_UNLIKELY
#define P99_UNLIKELY(...)
Mark the conditional expression as being unlikely.
Definition: p99_compiler.h:994
p99_abort_handler
void p99_abort_handler(const char *restrict p00_msg, void *restrict p00_ptr, errno_t p00_err)
Definition: p99_constraint.h:304
errno
errno
Definition: p99_constraint.h:199
p99_bitset.h
errno_t
int errno_t
Definition: p99_type.h:38
constraint_handler_t
struct atomic_constraint_handler_t constraint_handler_t
Definition: p99_constraint.h:331
strncpy_s
#define strncpy_s(S1, S1MAX, S2, N)
memcpy_s
#define memcpy_s(S1, S1MAX, S2, N)