shim.c 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208
  1. /* m2compiler-V3 runtime shim (C).
  2. *
  3. * Bridges Modula-2 external procedures to the host. Memory layout
  4. * matches the compiler's: a STRING (ARRAY OF CHAR) value is a
  5. * descriptor [0] = LONGCARD element count, then the bytes; the
  6. * compiler passes the descriptor's address as an `l`.
  7. *
  8. * Linked with every V3 program image.
  9. */
  10. #define _GNU_SOURCE
  11. #include <unistd.h>
  12. #include <stdlib.h>
  13. #include <stdio.h>
  14. #include <string.h>
  15. #include <time.h>
  16. #include <ucontext.h>
  17. /* Write the contents of a string descriptor to fd 1. Returns the
  18. number of bytes written. */
  19. long m2write(long *desc)
  20. {
  21. /* desc[0] is the capacity; the string is NUL-terminated. */
  22. char *s = (char *)(desc + 1);
  23. size_t n = strnlen(s, (size_t)desc[0] + 1);
  24. if (n > 0)
  25. write(1, s, n);
  26. return (long)n;
  27. }
  28. /* Write a newline to fd 1. */
  29. long m2writeln(void)
  30. {
  31. write(1, "\n", 1);
  32. return 0;
  33. }
  34. /* Read one character from stdin (0 at EOF). */
  35. long m2readchar(void)
  36. {
  37. int c = getchar();
  38. return (c == EOF) ? 0 : c;
  39. }
  40. /* Read a signed decimal integer from stdin, skipping leading
  41. whitespace. The first non-digit after the number is consumed. */
  42. long m2readint(void)
  43. {
  44. long v = 0;
  45. int c;
  46. int neg = 0;
  47. do { c = getchar(); } while (c == ' ' || c == '\t' ||
  48. c == '\n' || c == '\r');
  49. if (c == '-') { neg = 1; c = getchar(); }
  50. while (c >= '0' && c <= '9') {
  51. v = v * 10 + (c - '0');
  52. c = getchar();
  53. }
  54. return neg ? -v : v;
  55. }
  56. /* ---------------- sequential files (stdio) ---------------- */
  57. /* Copy a descriptor string into a NUL-terminated buffer. */
  58. static void m2cstr(long *desc, char *buf, size_t cap)
  59. {
  60. char *s = (char *)(desc + 1);
  61. size_t n = strnlen(s, (size_t)desc[0] + 1);
  62. if (n >= cap) n = cap - 1;
  63. if (n > 0) memcpy(buf, s, n);
  64. buf[n] = 0;
  65. }
  66. long m2fopenread(long *desc)
  67. {
  68. char buf[4096];
  69. m2cstr(desc, buf, sizeof buf);
  70. return (long)fopen(buf, "r");
  71. }
  72. long m2fopenwrite(long *desc)
  73. {
  74. char buf[4096];
  75. m2cstr(desc, buf, sizeof buf);
  76. return (long)fopen(buf, "w");
  77. }
  78. long m2fclose(long h)
  79. {
  80. if (h != 0) fclose((FILE *)h);
  81. return 0;
  82. }
  83. /* A handle of 0 means the open failed (Modula-2 NIL). Every file
  84. operation degrades to a safe no-op instead of dereferencing NULL. */
  85. long m2fgetc(long h)
  86. {
  87. int c;
  88. if (h == 0) return 0;
  89. c = fgetc((FILE *)h);
  90. return (c == EOF) ? 0 : c;
  91. }
  92. long m2fputc(long h, long c)
  93. {
  94. if (h != 0) fputc((int)c, (FILE *)h);
  95. return 0;
  96. }
  97. long m2fputs(long h, long *desc)
  98. {
  99. char *s = (char *)(desc + 1);
  100. size_t n = strnlen(s, (size_t)desc[0] + 1);
  101. if (h == 0) return 0;
  102. if (n > 0) fwrite(s, 1, n, (FILE *)h);
  103. return (long)n;
  104. }
  105. long m2freadint(long h)
  106. {
  107. long v = 0;
  108. int c, neg = 0;
  109. if (h == 0) return 0;
  110. do { c = fgetc((FILE *)h); } while (c == ' ' || c == '\t' ||
  111. c == '\n' || c == '\r');
  112. if (c == '-') { neg = 1; c = fgetc((FILE *)h); }
  113. while (c >= '0' && c <= '9') {
  114. v = v * 10 + (c - '0');
  115. c = fgetc((FILE *)h);
  116. }
  117. return neg ? -v : v;
  118. }
  119. long m2fwriteint(long h, long v)
  120. {
  121. char buf[32];
  122. int i = 0, neg = 0;
  123. unsigned long u;
  124. if (h == 0) return 0;
  125. if (v < 0) { neg = 1; u = (unsigned long)(-v); }
  126. else u = (unsigned long)v;
  127. do { buf[i++] = (char)('0' + u % 10); u /= 10; } while (u != 0);
  128. if (neg) buf[i++] = '-';
  129. while (i > 0) fputc(buf[--i], (FILE *)h);
  130. return 0;
  131. }
  132. long m2fwriteln(long h)
  133. {
  134. if (h != 0) fputc('\n', (FILE *)h);
  135. return 0;
  136. }
  137. /* Read one line (up to and including '\n', or EOF) into the
  138. descriptor's data area, dropping the newline; NUL-terminated.
  139. The descriptor's capacity header is left intact (so a caller may
  140. keep using HIGH), and the number of bytes stored is returned.
  141. `max` is declared `int`: Modula-2 CARDINAL arguments arrive in a
  142. 32-bit register (QBE `w`), so only the low 32 bits are defined. */
  143. long m2freadline(long h, long *desc, int max)
  144. {
  145. char *dst = (char *)(desc + 1);
  146. long n = 0;
  147. int c;
  148. if (max < 0) max = 0;
  149. if (h == 0) { if (max > 0) dst[0] = 0; return 0; }
  150. while (n < max) {
  151. c = fgetc((FILE *)h);
  152. if (c == EOF) break;
  153. if (c == '\n') break;
  154. if (c == '\r') continue;
  155. dst[n++] = (char)c;
  156. }
  157. dst[n] = 0;
  158. return n;
  159. }
  160. /* Bulk read: up to max bytes into the descriptor's data area; the
  161. descriptor's header is set to the number actually read. */
  162. long m2fread(long h, long *desc, long max)
  163. {
  164. long n;
  165. if (max < 0) max = 0;
  166. if (h == 0) { desc[0] = 0; return 0; }
  167. n = (long)fread((char *)(desc + 1), 1, (size_t)max, (FILE *)h);
  168. desc[0] = n;
  169. return n;
  170. }
  171. /* Write a signed integer right-justified in a field of width `wid`.
  172. wid = 0 means exactly one leading space (FileIO convention). */
  173. long m2fwriteintw(long h, long v, long wid)
  174. {
  175. char buf[64];
  176. int n;
  177. if (h == 0) return 0;
  178. if (wid == 0)
  179. n = snprintf(buf, sizeof buf, " %ld", v);
  180. else
  181. n = snprintf(buf, sizeof buf, "%*ld", (int)wid, v);
  182. if (n > 0) fwrite(buf, 1, (size_t)n, (FILE *)h);
  183. return 0;
  184. }
  185. /* Byte offset of the current position in h (-1 on error). */
  186. long m2ftell(long h)
  187. {
  188. if (h == 0) return -1;
  189. return (long)ftell((FILE *)h);
  190. }
  191. /* Seek h to absolute byte offset pos; 1 on success, 0 on failure.
  192. Rewinding also clears the stdio error/EOF state. */
  193. long m2fseek(long h, long pos)
  194. {
  195. if (h == 0) return 0;
  196. if (fseek((FILE *)h, (long)pos, SEEK_SET) != 0) return 0;
  197. clearerr((FILE *)h);
  198. return 1;
  199. }
  200. /* Standard streams as file handles. */
  201. long m2stdout(void) { return (long)stdout; }
  202. long m2stderr(void) { return (long)stderr; }
  203. long m2stdin(void) { return (long)stdin; }
  204. /* Command-line arguments, read from /proc/self/cmdline (Linux). */
  205. static char *m2_args[1024];
  206. static long m2_nargs = -1;
  207. static void m2_argv_init(void)
  208. {
  209. static char cmd[65536];
  210. FILE *f;
  211. long i, n;
  212. int c;
  213. if (m2_nargs >= 0) return;
  214. m2_nargs = 0;
  215. f = fopen("/proc/self/cmdline", "rb");
  216. if (f == NULL) return;
  217. n = (long)fread(cmd, 1, sizeof cmd - 1, f);
  218. fclose(f);
  219. if (n <= 0) return;
  220. cmd[n] = 0;
  221. i = 0;
  222. while (i < n && m2_nargs < 1024) {
  223. while (i < n && cmd[i] == 0) i++; /* skip separators */
  224. if (i >= n) break;
  225. m2_args[m2_nargs++] = &cmd[i];
  226. while (i < n && cmd[i] != 0) i++; /* to end of arg */
  227. }
  228. }
  229. long m2argc(void)
  230. {
  231. m2_argv_init();
  232. return m2_nargs;
  233. }
  234. long m2arg(long i, long *desc, long cap)
  235. {
  236. const char *a;
  237. long n;
  238. m2_argv_init();
  239. if (i < 0 || i >= m2_nargs) { ((char *)(desc + 1))[0] = 0; return 0; }
  240. a = m2_args[i];
  241. n = (long)strlen(a);
  242. if (cap > 0 && n >= cap) n = cap - 1;
  243. if (n < 0) n = 0;
  244. memcpy((char *)(desc + 1), a, (size_t)n);
  245. if (n < cap) ((char *)(desc + 1))[n] = 0; /* NUL terminator */
  246. /* desc[0] (capacity) is left intact: length is derived by scanning */
  247. return 1;
  248. }
  249. /* Set a string descriptor's logical length (header). */
  250. long m2setlen(long *desc, long n)
  251. {
  252. desc[0] = n;
  253. return n;
  254. }
  255. /* Write a single character (byte) to fd 1. */
  256. long m2writechar(long c)
  257. {
  258. char ch = (char)c;
  259. write(1, &ch, 1);
  260. return 0;
  261. }
  262. /* Write a signed decimal integer to fd 1. */
  263. long m2writeint(long v)
  264. {
  265. char buf[32];
  266. int i = 0;
  267. int neg = 0;
  268. unsigned long u;
  269. if (v < 0) { neg = 1; u = (unsigned long)(-v); }
  270. else u = (unsigned long)v;
  271. do { buf[i++] = (char)('0' + (u % 10)); u /= 10; } while (u != 0);
  272. if (neg) buf[i++] = '-';
  273. while (i > 0)
  274. write(1, &buf[--i], 1);
  275. return 0;
  276. }
  277. /* Terminate the process. */
  278. long m2exit(long code)
  279. {
  280. exit((int)code);
  281. return 0;
  282. }
  283. /* ---------------- clock (SysClock) ---------------- */
  284. /* Wall-clock time in seconds since the Unix epoch. */
  285. long m2time(void)
  286. {
  287. return (long)time(NULL);
  288. }
  289. /* Processor time used so far, in clock ticks (CLOCKS_PER_SEC per
  290. second). The scaling is exposed as a constant by SysClock. */
  291. long m2clock(void)
  292. {
  293. return (long)clock();
  294. }
  295. /* String content equality: 1 when the NUL-terminated contents of the
  296. two descriptors match. */
  297. long m2streq(long *a, long *b)
  298. {
  299. return (strcmp((char *)(a + 1), (char *)(b + 1)) == 0) ? 1 : 0;
  300. }
  301. /* Number of characters before the NUL. */
  302. long m2strlen(long *s)
  303. {
  304. return (long)strlen((char *)(s + 1));
  305. }
  306. /* UString length: the codepoint count is the descriptor's header. */
  307. long m2ustrlen(long *s)
  308. {
  309. return s[0];
  310. }
  311. /* Build a 1-codepoint UString descriptor from a UCHAR value; returns
  312. its address. A small rotating pool lets an expression supply both
  313. operands to m2ustrcat before it reads them. */
  314. long m2ustrfrom(long cp)
  315. {
  316. static struct { long count; int cp; } pool[16];
  317. static int next = 0;
  318. long *d = (long *)&pool[next];
  319. pool[next].count = 1;
  320. pool[next].cp = (int)cp;
  321. next = (next + 1) & 15;
  322. return (long)d;
  323. }
  324. /* Concatenate two UString descriptors into a static buffer; returns
  325. the result descriptor address. The value is valid until the next
  326. m2ustrcat call. */
  327. long m2ustrcat(long *a, long *b)
  328. {
  329. static struct { long count; int cp[4096]; } buf;
  330. int *pa = (int *)(a + 1);
  331. int *pb = (int *)(b + 1);
  332. long na = a[0], nb = b[0], i;
  333. if (na < 0) na = 0;
  334. if (nb < 0) nb = 0;
  335. if (na > 4096) na = 4096;
  336. if (nb > 4096 - na) nb = 4096 - na;
  337. for (i = 0; i < na; i++) buf.cp[i] = pa[i];
  338. for (i = 0; i < nb; i++) buf.cp[na + i] = pb[i];
  339. buf.count = na + nb;
  340. return (long)&buf;
  341. }
  342. /* Copy a UString descriptor (count + 4-byte codepoints) into the
  343. UCHAR array at dst (capacity dst[0]): min(count, capacity)
  344. codepoints, followed by a 0 codepoint terminator. Returns the
  345. number of codepoints copied. */
  346. long m2uassign(long *dst, long *src)
  347. {
  348. /* Both descriptors are { l count, w codepoints... }: the header is
  349. 64-bit, each codepoint is 32-bit. */
  350. int *d = (int *)(dst + 1);
  351. int *s = (int *)(src + 1);
  352. long cap = dst[0];
  353. long n = src[0];
  354. long i;
  355. if (cap < 0) cap = 0;
  356. if (n > cap) n = cap;
  357. for (i = 0; i < n; i++) d[i] = s[i];
  358. d[n] = 0;
  359. return n;
  360. }
  361. /* Copy the content of src into the CHAR array at dst (capacity
  362. dst[0]); truncated to fit and NUL-terminated. A CHAR array has
  363. `cap` content slots plus a reserved terminator slot at index `cap`,
  364. so up to `cap` characters fit. Returns the length copied. */
  365. long m2strassign(long *dst, long *src)
  366. {
  367. char *d = (char *)(dst + 1);
  368. const char *s = (const char *)(src + 1);
  369. long cap = dst[0];
  370. size_t n;
  371. if (cap < 0) return 0;
  372. n = strlen(s);
  373. if (n > (size_t)cap) n = (size_t)cap;
  374. memmove(d, s, n);
  375. d[n] = 0;
  376. return (long)n;
  377. }
  378. /* Concatenate two string descriptors into a static buffer; returns the
  379. result descriptor address. The value is valid until the next
  380. m2strcat call (so copy it before concatenating again). */
  381. long m2strcat(long *a, long *b)
  382. {
  383. static struct { long count; char data[8192]; } buf;
  384. const char *sa = (const char *)(a + 1);
  385. const char *sb = (const char *)(b + 1);
  386. size_t na = strlen(sa), nb = strlen(sb);
  387. if (na > sizeof buf.data - 1) na = sizeof buf.data - 1;
  388. if (nb > sizeof buf.data - 1 - na)
  389. nb = sizeof buf.data - 1 - na;
  390. memmove(buf.data, sa, na);
  391. memmove(buf.data + na, sb, nb);
  392. buf.data[na + nb] = 0;
  393. buf.count = (long)(na + nb);
  394. return (long)&buf;
  395. }
  396. /* ---------------- reals (Conversions / RealIO) ---------------- */
  397. /* Format a REAL into a descriptor string (6 significant digits,
  398. scientific). desc[0] is the capacity; on return it holds the
  399. length. Returns the number of characters written. */
  400. long m2realstr(double x, long *desc)
  401. {
  402. char buf[64];
  403. char *dst = (char *)(desc + 1);
  404. long cap = desc[0];
  405. int n = snprintf(buf, sizeof buf, "%.6E", x);
  406. if (n < 0) n = 0;
  407. if (cap > 0 && n >= (int)cap) n = (int)cap - 1;
  408. if (n > 0) memcpy(dst, buf, (size_t)n);
  409. if (cap > 0) { dst[n] = 0; desc[0] = n; }
  410. return (long)n;
  411. }
  412. /* Parse a REAL from a descriptor string; *ok := 1 on success. */
  413. double m2strreal(long *desc, long *ok)
  414. {
  415. char buf[4096];
  416. char *end;
  417. double v;
  418. m2cstr(desc, buf, sizeof buf);
  419. v = strtod(buf, &end);
  420. while (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r') end++;
  421. *ok = (end != buf && *end == 0) ? 1 : 0;
  422. return v;
  423. }
  424. /* Write a REAL right-justified in a field of width `wid` (0 => one
  425. leading space), matching the integer convention. */
  426. long m2writerealf(double x, long wid)
  427. {
  428. char buf[128];
  429. int n;
  430. if (wid == 0)
  431. n = snprintf(buf, sizeof buf, "%.6E", x);
  432. else
  433. n = snprintf(buf, sizeof buf, "%*.*E", (int)wid, 6, x);
  434. if (n > 0) write(1, buf, (size_t)n);
  435. return 0;
  436. }
  437. long m2writereal(double x)
  438. {
  439. return m2writerealf(x, 0);
  440. }
  441. /* Write a REAL right-justified in `wid` with `dec` digits after the
  442. point (FpuIO.WriteReal/WriteLongReal). */
  443. long m2writefix(double x, long wid, long dec)
  444. {
  445. char buf[256];
  446. int n;
  447. if (wid <= 0) wid = 1;
  448. if (dec < 0) dec = 0;
  449. if (dec > 30) dec = 30;
  450. n = snprintf(buf, sizeof buf, "%*.*f", (int)wid, (int)dec, x);
  451. if (n > 0) write(1, buf, (size_t)n);
  452. return 0;
  453. }
  454. /* Read a REAL from stdin. */
  455. double m2readreal(void)
  456. {
  457. double v = 0.0;
  458. if (scanf("%lf", &v) != 1) v = 0.0;
  459. return v;
  460. }
  461. /* ---------------- PIM coroutines (SYSTEM.NEWPROCESS/TRANSFER/...) --------
  462. *
  463. * A PROCESS is a pointer to a ucontext_t. NEWPROCESS builds a context
  464. * over the caller-supplied workspace; TRANSFER saves the running
  465. * context into *a and resumes *b. Coroutine bodies are compiled with a
  466. * leading static-link argument; module-level bodies (the usual case for
  467. * NEWPROCESS) take 0. */
  468. void m2_newprocess(void *p, void *workspace, long size, void **c)
  469. {
  470. ucontext_t *uc;
  471. if (c == NULL) return;
  472. uc = (ucontext_t *)malloc(sizeof(ucontext_t));
  473. if (uc == NULL) { *c = NULL; return; }
  474. if (getcontext(uc) != 0) { free(uc); *c = NULL; return; }
  475. uc->uc_stack.ss_sp = workspace;
  476. uc->uc_stack.ss_size = (size_t)size;
  477. uc->uc_link = NULL;
  478. makecontext(uc, (void (*)(void))p, 1, (int)0);
  479. *c = uc;
  480. }
  481. void m2_transfer(void **a, void **b)
  482. {
  483. ucontext_t *save;
  484. ucontext_t *go;
  485. if ((a == NULL) || (b == NULL)) return;
  486. save = (ucontext_t *)*a;
  487. go = (ucontext_t *)*b;
  488. if (go == NULL) return;
  489. if (save == NULL) {
  490. save = (ucontext_t *)malloc(sizeof(ucontext_t));
  491. if (save == NULL) return;
  492. *a = save;
  493. }
  494. swapcontext(save, go);
  495. }
  496. /* IOTRANSFER: without a device-interrupt subsystem this degrades to a
  497. plain transfer to b (classic PIM resumes a when the interrupt
  498. fires). The interrupt number is accepted and ignored. */
  499. void m2_iotransfer(void **a, void **b, long interruptNo)
  500. {
  501. (void)interruptNo;
  502. m2_transfer(a, b);
  503. }
  504. /* ---------------- text-IO library support ---------------- */
  505. /* Read one line from stdin (newline dropped) into the string
  506. descriptor's data area; NUL-terminated. Returns the length. */
  507. long m2readline(long *desc, int max)
  508. {
  509. char *dst = (char *)(desc + 1);
  510. long n = 0;
  511. int c;
  512. if (max < 0) max = 0;
  513. while (n < max) {
  514. c = getchar();
  515. if (c == EOF) break;
  516. if (c == '\n') break;
  517. if (c == '\r') continue;
  518. dst[n++] = (char)c;
  519. }
  520. dst[n] = 0;
  521. return n;
  522. }
  523. /* Write a signed integer right-justified in a field of width `wid`
  524. to fd 1 (wid = 0 => exactly one leading space). */
  525. long m2writeintwidth(long v, int wid)
  526. {
  527. char buf[64];
  528. int n;
  529. if (wid == 0)
  530. n = snprintf(buf, sizeof buf, " %ld", v);
  531. else
  532. n = snprintf(buf, sizeof buf, "%*ld", wid, v);
  533. if (n > 0) write(1, buf, (size_t)n);
  534. return 0;
  535. }
  536. /* Format a REAL into a descriptor: mode 0 = fixed (prec = decimal
  537. places), 1 = floating (prec = significant figures), 2 = engineering
  538. (prec = significant figures, exponent a multiple of three).
  539. desc[0] is the capacity; on return it holds the length. */
  540. static long m2putstr(char *dst, long cap, const char *src)
  541. {
  542. long n = (long)strlen(src);
  543. if (cap <= 0) return 0;
  544. if (n > cap - 1) n = cap - 1;
  545. memcpy(dst, src, (size_t)n);
  546. dst[n] = 0;
  547. return n;
  548. }
  549. long m2realconv(double x, int mode, int prec, long *desc)
  550. {
  551. char buf[256];
  552. char *dst = (char *)(desc + 1);
  553. long cap = desc[0];
  554. int n = 0;
  555. if (prec < 0) prec = 0;
  556. if (prec > 40) prec = 40;
  557. if (mode == 0) {
  558. n = snprintf(buf, sizeof buf, "%.*f", prec, x);
  559. } else if (mode == 1) {
  560. n = snprintf(buf, sizeof buf, "%.*E",
  561. prec > 0 ? prec - 1 : 0, x);
  562. } else {
  563. /* engineering: mantissa in [1,1000), exponent multiple of 3 */
  564. int e;
  565. double m = x;
  566. if (m != 0.0) {
  567. e = 0;
  568. while (m >= 1000.0) { m /= 1000.0; e += 3; }
  569. while (m < 1.0) { m *= 1000.0; e -= 3; }
  570. } else {
  571. e = 0;
  572. }
  573. n = snprintf(buf, sizeof buf, "%.*fE%+d",
  574. prec > 0 ? prec - 1 : 0, m, e);
  575. }
  576. if (n < 0) n = 0;
  577. return m2putstr(dst, cap, buf);
  578. }
  579. /* Termination flags: HALT aborts the image, so neither is ever
  580. observably TRUE; provided so TERMINATION links. */
  581. long m2terminating(void) { return 0; }
  582. long m2hashalted(void) { return 0; }
  583. /* ---------------- DynamicStrings (heap C strings) ----------------
  584. *
  585. * A DynamicStrings.String is a malloc'd NUL-terminated char buffer.
  586. * Procedures that return a String return the buffer address; those
  587. * that take a String receive it in the first `long` argument. */
  588. static char *m2dsdup(const char *s)
  589. {
  590. size_t n;
  591. char *p;
  592. if (s == NULL) s = "";
  593. n = strlen(s);
  594. p = (char *)malloc(n + 1);
  595. if (p == NULL) return NULL;
  596. memcpy(p, s, n + 1);
  597. return p;
  598. }
  599. long m2dsinit(long *a) { return (long)m2dsdup((const char *)(a + 1)); }
  600. long m2dskill(char *s) { free(s); return 0; }
  601. long m2dslength(const char *s) { return s ? (long)strlen(s) : 0; }
  602. long m2dsdupstr(const char *s) { return (long)m2dsdup(s); }
  603. long m2dsconcat(char *a, const char *b)
  604. {
  605. size_t na, nb;
  606. char *p;
  607. if (b == NULL) b = "";
  608. na = a ? strlen(a) : 0;
  609. nb = strlen(b);
  610. p = (char *)realloc(a, na + nb + 1);
  611. if (p == NULL) return (long)a;
  612. memcpy(p + na, b, nb + 1);
  613. return (long)p;
  614. }
  615. long m2dsconcatchar(char *a, int ch)
  616. {
  617. size_t na = a ? strlen(a) : 0;
  618. char *p = (char *)realloc(a, na + 2);
  619. if (p == NULL) return (long)a;
  620. p[na] = (char)ch;
  621. p[na + 1] = 0;
  622. return (long)p;
  623. }
  624. long m2dsassign(char *a, const char *b)
  625. {
  626. size_t nb;
  627. char *p;
  628. if (b == NULL) b = "";
  629. nb = strlen(b);
  630. p = (char *)realloc(a, nb + 1);
  631. if (p == NULL) return (long)a;
  632. memcpy(p, b, nb + 1);
  633. return (long)p;
  634. }
  635. long m2dseq(const char *a, const char *b)
  636. {
  637. if (a == NULL) a = "";
  638. if (b == NULL) b = "";
  639. return strcmp(a, b) == 0;
  640. }
  641. /* EqualArray: the second operand arrives as the *descriptor* of a V3
  642. CHAR-array actual (V3 passes array actuals as their descriptor). */
  643. long m2dseqarr(const char *s, long *a)
  644. {
  645. return m2dseq(s, (const char *)(a + 1));
  646. }
  647. long m2dschar(const char *s, int i)
  648. {
  649. int n;
  650. if (s == NULL) return 0;
  651. n = (int)strlen(s);
  652. if (i < 0) i = n + i;
  653. if (i < 0 || i >= n) return 0;
  654. return (unsigned char)s[i];
  655. }
  656. long m2dscopyout(long *dst, const char *s)
  657. {
  658. char *d = (char *)(dst + 1);
  659. long cap = dst[0];
  660. size_t n;
  661. if (s == NULL) s = "";
  662. n = strlen(s);
  663. if (cap < 0) cap = 0;
  664. if (n > (size_t)cap) n = (size_t)cap;
  665. memcpy(d, s, n);
  666. d[n] = 0;
  667. return (long)n;
  668. }
  669. long m2dsslice(const char *s, int low, int high)
  670. {
  671. int n, lo, hi;
  672. char *p;
  673. if (s == NULL) s = "";
  674. n = (int)strlen(s);
  675. lo = low;
  676. hi = high;
  677. if (lo < 0) lo = n + lo;
  678. if (lo < 0) lo = 0;
  679. if (hi == 0) hi = n;
  680. else if (hi < 0) hi = n + hi;
  681. if (hi > n) hi = n;
  682. if (hi < lo) hi = lo;
  683. p = (char *)malloc((size_t)(hi - lo) + 1);
  684. if (p == NULL) return 0;
  685. memcpy(p, s + lo, (size_t)(hi - lo));
  686. p[hi - lo] = 0;
  687. return (long)p;
  688. }
  689. long m2dsindex(const char *s, int ch, int o)
  690. {
  691. int i;
  692. if (s == NULL) return -1;
  693. for (i = o; s[i] != 0; i++)
  694. if ((unsigned char)s[i] == (unsigned char)ch) return i;
  695. return -1;
  696. }
  697. long m2dsrindex(const char *s, int ch, int o)
  698. {
  699. int n, i;
  700. if (s == NULL) return -1;
  701. n = (int)strlen(s);
  702. if (o >= n) o = n - 1;
  703. for (i = o; i >= 0; i--)
  704. if ((unsigned char)s[i] == (unsigned char)ch) return i;
  705. return -1;
  706. }
  707. long m2dsmult(const char *s, int n)
  708. {
  709. size_t len, i;
  710. char *p;
  711. if (s == NULL) s = "";
  712. if (n <= 0) return (long)m2dsdup("");
  713. len = strlen(s);
  714. p = (char *)malloc(len * (size_t)n + 1);
  715. if (p == NULL) return 0;
  716. for (i = 0; i < (size_t)n; i++) memcpy(p + i * len, s, len);
  717. p[len * (size_t)n] = 0;
  718. return (long)p;
  719. }
  720. long m2dsreplacechar(char *s, int from, int to)
  721. {
  722. int i;
  723. if (s == NULL) return 0;
  724. for (i = 0; s[i] != 0; i++)
  725. if ((unsigned char)s[i] == (unsigned char)from) s[i] = (char)to;
  726. return (long)s;
  727. }
  728. long m2dsupper(char *s)
  729. {
  730. int i;
  731. if (s == NULL) return 0;
  732. for (i = 0; s[i] != 0; i++)
  733. if (s[i] >= 'a' && s[i] <= 'z') s[i] = (char)(s[i] - 32);
  734. return (long)s;
  735. }
  736. long m2dslower(char *s)
  737. {
  738. int i;
  739. if (s == NULL) return 0;
  740. for (i = 0; s[i] != 0; i++)
  741. if (s[i] >= 'A' && s[i] <= 'Z') s[i] = (char)(s[i] + 32);
  742. return (long)s;
  743. }
  744. long m2dstrimprefix(char *s)
  745. {
  746. char *p;
  747. if (s == NULL) return 0;
  748. p = s;
  749. while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++;
  750. if (p != s) memmove(s, p, strlen(p) + 1);
  751. return (long)s;
  752. }
  753. long m2dstrimpostfix(char *s)
  754. {
  755. size_t n;
  756. if (s == NULL) return 0;
  757. n = strlen(s);
  758. while (n > 0 && (s[n-1] == ' ' || s[n-1] == '\t' ||
  759. s[n-1] == '\n' || s[n-1] == '\r')) {
  760. s[--n] = 0;
  761. }
  762. return (long)s;
  763. }
  764. /* ---------------- TimeString / wrapclock / wraptime / wrapc -------- */
  765. #include <sys/stat.h>
  766. #include <sys/time.h>
  767. #include <pwd.h>
  768. #include <math.h>
  769. #include <fcntl.h>
  770. long m2timestring(long *desc)
  771. {
  772. char buf[128];
  773. char *dst = (char *)(desc + 1);
  774. long cap = desc[0], n;
  775. time_t t = time(NULL);
  776. struct tm tmv;
  777. localtime_r(&t, &tmv);
  778. if (strftime(buf, sizeof buf, "%c", &tmv) == 0) buf[0] = 0;
  779. n = (long)strlen(buf);
  780. if (cap < 0) cap = 0;
  781. if (n > cap) n = cap;
  782. memcpy(dst, buf, (size_t)n);
  783. dst[n] = 0;
  784. return n;
  785. }
  786. long m2wctimezone(void) { return (long)timezone; }
  787. long m2wcistimezone(void) { return 1; }
  788. long m2wcdaylight(void) { return (long)daylight; }
  789. long m2wcisdst(void) { return 1; }
  790. long m2wctzname(long d) { return (long)(d ? tzname[1] : tzname[0]); }
  791. void *m2wctsnew(void) { return calloc(1, sizeof(struct timespec)); }
  792. void *m2wctskill(void *ts){ free(ts); return NULL; }
  793. long m2wctsget(void *ts, long *sec, long *nano)
  794. {
  795. struct timespec *t = (struct timespec *)ts;
  796. if (t == NULL) return -1;
  797. *sec = (long)t->tv_sec; *nano = (long)t->tv_nsec; return 0;
  798. }
  799. long m2wctsset(void *ts, long sec, long nano)
  800. {
  801. struct timespec *t = (struct timespec *)ts;
  802. if (t == NULL) return -1;
  803. t->tv_sec = (time_t)sec; t->tv_nsec = (long)nano; return 0;
  804. }
  805. long m2wctsrealtime(void *ts)
  806. { return clock_gettime(CLOCK_REALTIME, (struct timespec *)ts); }
  807. long m2wctssetrealtime(void *ts)
  808. { return clock_settime(CLOCK_REALTIME, (struct timespec *)ts); }
  809. void *m2wttvnew(void) { return calloc(1, sizeof(struct timeval)); }
  810. void *m2wttvkill(void *p) { free(p); return NULL; }
  811. void *m2wttznew(void) { return calloc(1, sizeof(struct timezone)); }
  812. void *m2wttzkill(void *p) { free(p); return NULL; }
  813. void *m2wttmnew(void) { return calloc(1, sizeof(struct tm)); }
  814. void *m2wttmkill(void *p) { free(p); return NULL; }
  815. long m2wtgettimeofday(void *tv, void *tz)
  816. { return gettimeofday((struct timeval *)tv, (struct timezone *)tz); }
  817. long m2wtsettimeofday(void *tv, void *tz)
  818. { return settimeofday((struct timeval *)tv, (struct timezone *)tz); }
  819. long m2wtfractions(void *tv)
  820. { struct timeval *t = (struct timeval *)tv; return t ? (long)t->tv_usec : 0; }
  821. void *m2wtlocaltime(void *tv, void *tm)
  822. { struct timeval *t = (struct timeval *)tv; return localtime_r(&t->tv_sec, (struct tm *)tm); }
  823. long m2wtyear(void *m) { return ((struct tm *)m)->tm_year + 1900; }
  824. long m2wtmonth(void *m) { return ((struct tm *)m)->tm_mon + 1; }
  825. long m2wtday(void *m) { return ((struct tm *)m)->tm_mday; }
  826. long m2wthour(void *m) { return ((struct tm *)m)->tm_hour; }
  827. long m2wtminute(void *m) { return ((struct tm *)m)->tm_min; }
  828. long m2wtsecond(void *m) { return ((struct tm *)m)->tm_sec; }
  829. void m2wttvset(void *tv, long sec, long usec)
  830. { struct timeval *t = (struct timeval *)tv; t->tv_sec = (time_t)sec; t->tv_usec = (long)usec; }
  831. void m2wttzset(void *tz, long minuteswest, long dsttime)
  832. { struct timezone *z = (struct timezone *)tz; z->tz_minuteswest = (int)minuteswest; z->tz_dsttime = (int)dsttime; }
  833. long m2cstrtime(void) { time_t t = time(NULL); return (long)ctime(&t); }
  834. long m2cfilesize(long f, long *low, long *high)
  835. {
  836. struct stat st;
  837. if (fstat((int)f, &st) != 0) { *low = 0; *high = 0; return -1; }
  838. *low = (long)(st.st_size & 0xffffffffL);
  839. *high = (long)((unsigned long long)st.st_size >> 32);
  840. return 0;
  841. }
  842. long m2cfileinode(long f, long *low, long *high)
  843. {
  844. struct stat st;
  845. if (fstat((int)f, &st) != 0) { *low = 0; *high = 0; return -1; }
  846. *low = (long)(st.st_ino & 0xffffffffL);
  847. *high = (long)((unsigned long long)st.st_ino >> 32);
  848. return 0;
  849. }
  850. long m2cfilemtime(long f)
  851. { struct stat st; if (fstat((int)f, &st) != 0) return -1; return (long)st.st_mtime; }
  852. long m2cgetrand(long n) { return n <= 0 ? 0 : (long)(random() % n); }
  853. long m2cgetusername(void)
  854. { struct passwd *pw = getpwuid(getuid()); return pw ? (long)pw->pw_name : (long)""; }
  855. void m2cgetnameuidgid(long name, long *uid, long *gid)
  856. { struct passwd *pw = getpwnam((const char *)name);
  857. if (pw == NULL) { *uid = -1; *gid = -1; } else { *uid = (long)pw->pw_uid; *gid = (long)pw->pw_gid; } }
  858. long m2csignbit(double r) { return signbit(r) ? 1 : 0; }
  859. long m2cisfinite(double x) { return isfinite(x) ? 1 : 0; }
  860. long m2cisnan(double x) { return isnan(x) ? 1 : 0; }
  861. long m2cseekset(void) { return (long)SEEK_SET; }
  862. long m2cseekend(void) { return (long)SEEK_END; }
  863. long m2creadonly(void) { return (long)O_RDONLY; }
  864. long m2cwriteonly(void) { return (long)O_WRONLY; }
  865. /* ---------------- ISO exceptions ---------------- */
  866. #include <setjmp.h>
  867. #include <signal.h>
  868. #include <stdint.h>
  869. #define M2_EXC_MAX 1024
  870. #define M2_EXC_MSG_MAX 1024
  871. #define M2_EXC_ENV_SIZE 512
  872. _Static_assert(sizeof(sigjmp_buf) <= M2_EXC_ENV_SIZE,
  873. "sigjmp_buf does not fit the QBE exception environment");
  874. typedef struct {
  875. int active;
  876. int language;
  877. void *source;
  878. unsigned number;
  879. char message[M2_EXC_MSG_MAX];
  880. } m2_exc_state;
  881. typedef struct {
  882. void *env;
  883. int in_handler;
  884. m2_exc_state saved;
  885. } m2_exc_handler;
  886. static m2_exc_state m2_cur;
  887. static m2_exc_handler m2_handlers[M2_EXC_MAX];
  888. static int m2_top;
  889. static int m2_exc_inited;
  890. static unsigned m2_source_seq;
  891. static char m2_language_source;
  892. void m2_exc_raise_lang(int code);
  893. static const char *m2_exc_text[15] = {
  894. "array index out of range",
  895. "range check failed",
  896. "no matching CASE label",
  897. "invalid memory location",
  898. "function did not return a value",
  899. "integer overflow",
  900. "integer divide by zero",
  901. "real value error",
  902. "real divide by zero",
  903. "complex value error",
  904. "complex divide by zero",
  905. "protection error",
  906. "system error",
  907. "coroutine error",
  908. "exception handling error"
  909. };
  910. static void m2_exc_clear(void)
  911. {
  912. m2_cur.active = 0;
  913. m2_cur.language = 0;
  914. m2_cur.source = NULL;
  915. m2_cur.number = 0;
  916. m2_cur.message[0] = 0;
  917. }
  918. static void m2_exc_copy_desc(char *dst, size_t cap, const long *desc)
  919. {
  920. size_t n;
  921. if (cap == 0) return;
  922. dst[0] = 0;
  923. if (desc == NULL) return;
  924. n = (desc[0] < 0) ? 0 : (size_t)desc[0];
  925. if (n >= cap) n = cap - 1;
  926. if (n > 0) memcpy(dst, (const char *)(desc + 1), n);
  927. dst[n] = 0;
  928. }
  929. static void m2_exc_unhandled(void)
  930. {
  931. const char *msg = m2_cur.message[0] ? m2_cur.message : "unhandled exception";
  932. fprintf(stderr, "unhandled Modula-2 exception %u: %s\n",
  933. m2_cur.number, msg);
  934. fflush(stderr);
  935. abort();
  936. }
  937. static void m2_exc_raise_state(const m2_exc_state *st)
  938. {
  939. int i;
  940. for (i = m2_top - 1; i >= 0; --i) {
  941. if (!m2_handlers[i].in_handler) {
  942. m2_top = i + 1;
  943. m2_handlers[i].in_handler = 1;
  944. m2_cur = *st;
  945. siglongjmp(*(sigjmp_buf *)m2_handlers[i].env, 1);
  946. }
  947. }
  948. m2_cur = *st;
  949. m2_exc_unhandled();
  950. }
  951. static void m2_exc_fault(int sig, siginfo_t *info, void *ctx)
  952. {
  953. int code;
  954. (void)ctx;
  955. if (sig == SIGSEGV || sig == SIGBUS) {
  956. code = 3; /* invalidLocation */
  957. } else if (sig == SIGFPE) {
  958. switch (info ? info->si_code : 0) {
  959. case FPE_INTDIV: code = 6; break; /* wholeDivException */
  960. case FPE_INTOVF: code = 5; break; /* wholeValueException */
  961. case FPE_FLTDIV: code = 8; break; /* realDivException */
  962. case FPE_FLTOVF:
  963. case FPE_FLTUND:
  964. case FPE_FLTRES:
  965. case FPE_FLTINV: code = 7; break; /* realValueException */
  966. #ifdef FPE_FLTSUB
  967. case FPE_FLTSUB: code = 0; break; /* indexException */
  968. #endif
  969. default: code = 12; break; /* sysException */
  970. }
  971. } else {
  972. code = 12;
  973. }
  974. m2_exc_raise_lang(code);
  975. }
  976. void m2_exc_init(void)
  977. {
  978. struct sigaction sa;
  979. if (m2_exc_inited) return;
  980. m2_exc_inited = 1;
  981. memset(&sa, 0, sizeof sa);
  982. sa.sa_sigaction = m2_exc_fault;
  983. sa.sa_flags = SA_SIGINFO;
  984. sigemptyset(&sa.sa_mask);
  985. sigaction(SIGSEGV, &sa, NULL);
  986. #ifdef SIGBUS
  987. sigaction(SIGBUS, &sa, NULL);
  988. #endif
  989. sigaction(SIGFPE, &sa, NULL);
  990. }
  991. void m2_exc_push(void *env)
  992. {
  993. if (m2_top >= M2_EXC_MAX) {
  994. fprintf(stderr, "Modula-2 exception handler stack overflow\n");
  995. abort();
  996. }
  997. m2_handlers[m2_top].env = env;
  998. m2_handlers[m2_top].in_handler = 0;
  999. m2_handlers[m2_top].saved = m2_cur;
  1000. m2_top++;
  1001. m2_exc_clear();
  1002. }
  1003. void m2_exc_leave(void)
  1004. {
  1005. if (m2_top <= 0) return;
  1006. m2_top--;
  1007. m2_cur = m2_handlers[m2_top].saved;
  1008. }
  1009. void m2_exc_retry(void)
  1010. {
  1011. if (m2_top <= 0) {
  1012. fprintf(stderr, "RETRY without an exception handler\n");
  1013. abort();
  1014. }
  1015. m2_handlers[m2_top - 1].in_handler = 0;
  1016. m2_exc_clear();
  1017. siglongjmp(*(sigjmp_buf *)m2_handlers[m2_top - 1].env, 2);
  1018. }
  1019. void m2_exc_raise_lang(int code)
  1020. {
  1021. m2_exc_state st;
  1022. if (code < 0 || code > 14) code = 14; /* exException */
  1023. memset(&st, 0, sizeof st);
  1024. st.active = 1;
  1025. st.language = 1;
  1026. st.source = &m2_language_source;
  1027. st.number = (unsigned)code;
  1028. strncpy(st.message, m2_exc_text[code], M2_EXC_MSG_MAX - 1);
  1029. st.message[M2_EXC_MSG_MAX - 1] = 0;
  1030. m2_exc_raise_state(&st);
  1031. }
  1032. void m2_exc_raise_user(void *source, unsigned number, const long *msg_desc)
  1033. {
  1034. m2_exc_state st;
  1035. memset(&st, 0, sizeof st);
  1036. st.active = 1;
  1037. st.language = 0;
  1038. st.source = source;
  1039. st.number = number;
  1040. m2_exc_copy_desc(st.message, sizeof st.message, msg_desc);
  1041. m2_exc_raise_state(&st);
  1042. }
  1043. void m2_exc_throw(long code)
  1044. {
  1045. if (code < 0 || code > 14) code = 14;
  1046. m2_exc_raise_lang((int)code);
  1047. }
  1048. int m2_exc_active(void)
  1049. {
  1050. return m2_cur.active;
  1051. }
  1052. int m2_exc_language(void)
  1053. {
  1054. return m2_cur.active && m2_cur.language;
  1055. }
  1056. int m2_exc_is_source(void *source)
  1057. {
  1058. return m2_cur.active && (m2_cur.source == source);
  1059. }
  1060. unsigned m2_exc_number(void)
  1061. {
  1062. return m2_cur.number;
  1063. }
  1064. void m2_exc_copy_message(long *text_desc)
  1065. {
  1066. size_t cap, n;
  1067. if (text_desc == NULL) return;
  1068. cap = (text_desc[0] < 0) ? 0 : (size_t)text_desc[0];
  1069. if (cap == 0) return;
  1070. {
  1071. char *dst = (char *)(text_desc + 1);
  1072. if (!m2_cur.active) {
  1073. dst[0] = 0;
  1074. return;
  1075. }
  1076. n = strlen(m2_cur.message);
  1077. if (n >= cap) n = cap - 1;
  1078. memcpy(dst, m2_cur.message, n);
  1079. dst[n] = 0;
  1080. }
  1081. }
  1082. void *m2_exc_new_source(void)
  1083. {
  1084. return (void *)(uintptr_t)(++m2_source_seq);
  1085. }