shim.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  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. #include <unistd.h>
  11. #include <stdlib.h>
  12. #include <stdio.h>
  13. #include <string.h>
  14. /* Write the contents of a string descriptor to fd 1. Returns the
  15. number of bytes written. */
  16. long m2write(long *desc)
  17. {
  18. /* desc[0] is the capacity; the string is NUL-terminated. */
  19. char *s = (char *)(desc + 1);
  20. size_t n = strnlen(s, (size_t)desc[0] + 1);
  21. if (n > 0)
  22. write(1, s, n);
  23. return (long)n;
  24. }
  25. /* Write a newline to fd 1. */
  26. long m2writeln(void)
  27. {
  28. write(1, "\n", 1);
  29. return 0;
  30. }
  31. /* Read one character from stdin (0 at EOF). */
  32. long m2readchar(void)
  33. {
  34. int c = getchar();
  35. return (c == EOF) ? 0 : c;
  36. }
  37. /* Read a signed decimal integer from stdin, skipping leading
  38. whitespace. The first non-digit after the number is consumed. */
  39. long m2readint(void)
  40. {
  41. long v = 0;
  42. int c;
  43. int neg = 0;
  44. do { c = getchar(); } while (c == ' ' || c == '\t' ||
  45. c == '\n' || c == '\r');
  46. if (c == '-') { neg = 1; c = getchar(); }
  47. while (c >= '0' && c <= '9') {
  48. v = v * 10 + (c - '0');
  49. c = getchar();
  50. }
  51. return neg ? -v : v;
  52. }
  53. /* ---------------- sequential files (stdio) ---------------- */
  54. /* Copy a descriptor string into a NUL-terminated buffer. */
  55. static void m2cstr(long *desc, char *buf, size_t cap)
  56. {
  57. char *s = (char *)(desc + 1);
  58. size_t n = strnlen(s, (size_t)desc[0] + 1);
  59. if (n >= cap) n = cap - 1;
  60. if (n > 0) memcpy(buf, s, n);
  61. buf[n] = 0;
  62. }
  63. long m2fopenread(long *desc)
  64. {
  65. char buf[4096];
  66. m2cstr(desc, buf, sizeof buf);
  67. return (long)fopen(buf, "r");
  68. }
  69. long m2fopenwrite(long *desc)
  70. {
  71. char buf[4096];
  72. m2cstr(desc, buf, sizeof buf);
  73. return (long)fopen(buf, "w");
  74. }
  75. long m2fclose(long h)
  76. {
  77. if (h != 0) fclose((FILE *)h);
  78. return 0;
  79. }
  80. /* A handle of 0 means the open failed (Modula-2 NIL). Every file
  81. operation degrades to a safe no-op instead of dereferencing NULL. */
  82. long m2fgetc(long h)
  83. {
  84. int c;
  85. if (h == 0) return 0;
  86. c = fgetc((FILE *)h);
  87. return (c == EOF) ? 0 : c;
  88. }
  89. long m2fputc(long h, long c)
  90. {
  91. if (h != 0) fputc((int)c, (FILE *)h);
  92. return 0;
  93. }
  94. long m2fputs(long h, long *desc)
  95. {
  96. char *s = (char *)(desc + 1);
  97. size_t n = strnlen(s, (size_t)desc[0] + 1);
  98. if (h == 0) return 0;
  99. if (n > 0) fwrite(s, 1, n, (FILE *)h);
  100. return (long)n;
  101. }
  102. long m2freadint(long h)
  103. {
  104. long v = 0;
  105. int c, neg = 0;
  106. if (h == 0) return 0;
  107. do { c = fgetc((FILE *)h); } while (c == ' ' || c == '\t' ||
  108. c == '\n' || c == '\r');
  109. if (c == '-') { neg = 1; c = fgetc((FILE *)h); }
  110. while (c >= '0' && c <= '9') {
  111. v = v * 10 + (c - '0');
  112. c = fgetc((FILE *)h);
  113. }
  114. return neg ? -v : v;
  115. }
  116. long m2fwriteint(long h, long v)
  117. {
  118. char buf[32];
  119. int i = 0, neg = 0;
  120. unsigned long u;
  121. if (h == 0) return 0;
  122. if (v < 0) { neg = 1; u = (unsigned long)(-v); }
  123. else u = (unsigned long)v;
  124. do { buf[i++] = (char)('0' + u % 10); u /= 10; } while (u != 0);
  125. if (neg) buf[i++] = '-';
  126. while (i > 0) fputc(buf[--i], (FILE *)h);
  127. return 0;
  128. }
  129. long m2fwriteln(long h)
  130. {
  131. if (h != 0) fputc('\n', (FILE *)h);
  132. return 0;
  133. }
  134. /* Read one line (up to and including '\n', or EOF) into the
  135. descriptor's data area, dropping the newline. The descriptor's
  136. header is set to the number of bytes stored; NUL-terminated. */
  137. long m2freadline(long h, long *desc, long max)
  138. {
  139. char *dst = (char *)(desc + 1);
  140. long n = 0;
  141. int c;
  142. if (max < 0) max = 0;
  143. if (h == 0) { if (max > 0) dst[0] = 0; desc[0] = 0; return 0; }
  144. while (n < max) {
  145. c = fgetc((FILE *)h);
  146. if (c == EOF) break;
  147. if (c == '\n') break;
  148. if (c == '\r') continue;
  149. dst[n++] = (char)c;
  150. }
  151. dst[n] = 0;
  152. desc[0] = n;
  153. return n;
  154. }
  155. /* Bulk read: up to max bytes into the descriptor's data area; the
  156. descriptor's header is set to the number actually read. */
  157. long m2fread(long h, long *desc, long max)
  158. {
  159. long n;
  160. if (max < 0) max = 0;
  161. if (h == 0) { desc[0] = 0; return 0; }
  162. n = (long)fread((char *)(desc + 1), 1, (size_t)max, (FILE *)h);
  163. desc[0] = n;
  164. return n;
  165. }
  166. /* Write a signed integer right-justified in a field of width `wid`.
  167. wid = 0 means exactly one leading space (FileIO convention). */
  168. long m2fwriteintw(long h, long v, long wid)
  169. {
  170. char buf[64];
  171. int n;
  172. if (h == 0) return 0;
  173. if (wid == 0)
  174. n = snprintf(buf, sizeof buf, " %ld", v);
  175. else
  176. n = snprintf(buf, sizeof buf, "%*ld", (int)wid, v);
  177. if (n > 0) fwrite(buf, 1, (size_t)n, (FILE *)h);
  178. return 0;
  179. }
  180. /* Standard streams as file handles. */
  181. long m2stdout(void) { return (long)stdout; }
  182. long m2stderr(void) { return (long)stderr; }
  183. long m2stdin(void) { return (long)stdin; }
  184. /* Command-line arguments, read from /proc/self/cmdline (Linux). */
  185. static char *m2_args[1024];
  186. static long m2_nargs = -1;
  187. static void m2_argv_init(void)
  188. {
  189. static char cmd[65536];
  190. FILE *f;
  191. long i, n;
  192. int c;
  193. if (m2_nargs >= 0) return;
  194. m2_nargs = 0;
  195. f = fopen("/proc/self/cmdline", "rb");
  196. if (f == NULL) return;
  197. n = (long)fread(cmd, 1, sizeof cmd - 1, f);
  198. fclose(f);
  199. if (n <= 0) return;
  200. cmd[n] = 0;
  201. i = 0;
  202. while (i < n && m2_nargs < 1024) {
  203. while (i < n && cmd[i] == 0) i++; /* skip separators */
  204. if (i >= n) break;
  205. m2_args[m2_nargs++] = &cmd[i];
  206. while (i < n && cmd[i] != 0) i++; /* to end of arg */
  207. }
  208. }
  209. long m2argc(void)
  210. {
  211. m2_argv_init();
  212. return m2_nargs;
  213. }
  214. long m2arg(long i, long *desc, long cap)
  215. {
  216. const char *a;
  217. long n;
  218. m2_argv_init();
  219. if (i < 0 || i >= m2_nargs) { ((char *)(desc + 1))[0] = 0; return 0; }
  220. a = m2_args[i];
  221. n = (long)strlen(a);
  222. if (cap > 0 && n >= cap) n = cap - 1;
  223. if (n < 0) n = 0;
  224. memcpy((char *)(desc + 1), a, (size_t)n);
  225. if (n < cap) ((char *)(desc + 1))[n] = 0; /* NUL terminator */
  226. /* desc[0] (capacity) is left intact: length is derived by scanning */
  227. return 1;
  228. }
  229. /* Set a string descriptor's logical length (header). */
  230. long m2setlen(long *desc, long n)
  231. {
  232. desc[0] = n;
  233. return n;
  234. }
  235. /* Write a single character (byte) to fd 1. */
  236. long m2writechar(long c)
  237. {
  238. char ch = (char)c;
  239. write(1, &ch, 1);
  240. return 0;
  241. }
  242. /* Write a signed decimal integer to fd 1. */
  243. long m2writeint(long v)
  244. {
  245. char buf[32];
  246. int i = 0;
  247. int neg = 0;
  248. unsigned long u;
  249. if (v < 0) { neg = 1; u = (unsigned long)(-v); }
  250. else u = (unsigned long)v;
  251. do { buf[i++] = (char)('0' + (u % 10)); u /= 10; } while (u != 0);
  252. if (neg) buf[i++] = '-';
  253. while (i > 0)
  254. write(1, &buf[--i], 1);
  255. return 0;
  256. }
  257. /* Terminate the process. */
  258. long m2exit(long code)
  259. {
  260. exit((int)code);
  261. return 0;
  262. }
  263. /* String content equality: 1 when the NUL-terminated contents of the
  264. two descriptors match. */
  265. long m2streq(long *a, long *b)
  266. {
  267. return (strcmp((char *)(a + 1), (char *)(b + 1)) == 0) ? 1 : 0;
  268. }
  269. /* Number of characters before the NUL. */
  270. long m2strlen(long *s)
  271. {
  272. return (long)strlen((char *)(s + 1));
  273. }
  274. /* Copy the content of src into the CHAR array at dst (capacity
  275. dst[0]); truncated to fit and NUL-terminated. A CHAR array has
  276. `cap` content slots plus a reserved terminator slot at index `cap`,
  277. so up to `cap` characters fit. Returns the length copied. */
  278. long m2strassign(long *dst, long *src)
  279. {
  280. char *d = (char *)(dst + 1);
  281. const char *s = (const char *)(src + 1);
  282. long cap = dst[0];
  283. size_t n;
  284. if (cap < 0) return 0;
  285. n = strlen(s);
  286. if (n > (size_t)cap) n = (size_t)cap;
  287. memmove(d, s, n);
  288. d[n] = 0;
  289. return (long)n;
  290. }
  291. /* Concatenate two string descriptors into a static buffer; returns the
  292. result descriptor address. The value is valid until the next
  293. m2strcat call (so copy it before concatenating again). */
  294. long m2strcat(long *a, long *b)
  295. {
  296. static struct { long count; char data[8192]; } buf;
  297. const char *sa = (const char *)(a + 1);
  298. const char *sb = (const char *)(b + 1);
  299. size_t na = strlen(sa), nb = strlen(sb);
  300. if (na > sizeof buf.data - 1) na = sizeof buf.data - 1;
  301. if (nb > sizeof buf.data - 1 - na)
  302. nb = sizeof buf.data - 1 - na;
  303. memmove(buf.data, sa, na);
  304. memmove(buf.data + na, sb, nb);
  305. buf.data[na + nb] = 0;
  306. buf.count = (long)(na + nb);
  307. return (long)&buf;
  308. }
  309. /* ---------------- reals (Conversions / RealIO) ---------------- */
  310. /* Format a REAL into a descriptor string (6 significant digits,
  311. scientific). desc[0] is the capacity; on return it holds the
  312. length. Returns the number of characters written. */
  313. long m2realstr(double x, long *desc)
  314. {
  315. char buf[64];
  316. char *dst = (char *)(desc + 1);
  317. long cap = desc[0];
  318. int n = snprintf(buf, sizeof buf, "%.6E", x);
  319. if (n < 0) n = 0;
  320. if (cap > 0 && n >= (int)cap) n = (int)cap - 1;
  321. if (n > 0) memcpy(dst, buf, (size_t)n);
  322. if (cap > 0) { dst[n] = 0; desc[0] = n; }
  323. return (long)n;
  324. }
  325. /* Parse a REAL from a descriptor string; *ok := 1 on success. */
  326. double m2strreal(long *desc, long *ok)
  327. {
  328. char buf[4096];
  329. char *end;
  330. double v;
  331. m2cstr(desc, buf, sizeof buf);
  332. v = strtod(buf, &end);
  333. while (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r') end++;
  334. *ok = (end != buf && *end == 0) ? 1 : 0;
  335. return v;
  336. }
  337. /* Write a REAL right-justified in a field of width `wid` (0 => one
  338. leading space), matching the integer convention. */
  339. long m2writerealf(double x, long wid)
  340. {
  341. char buf[128];
  342. int n;
  343. if (wid == 0)
  344. n = snprintf(buf, sizeof buf, "%.6E", x);
  345. else
  346. n = snprintf(buf, sizeof buf, "%*.*E", (int)wid, 6, x);
  347. if (n > 0) write(1, buf, (size_t)n);
  348. return 0;
  349. }
  350. long m2writereal(double x)
  351. {
  352. return m2writerealf(x, 0);
  353. }
  354. /* Read a REAL from stdin. */
  355. double m2readreal(void)
  356. {
  357. double v = 0.0;
  358. if (scanf("%lf", &v) != 1) v = 0.0;
  359. return v;
  360. }