#include "config.h" #if defined(__FreeBSD__) #include #include #include #include #endif #ifdef HAVE_STRPTIME #define _XOPEN_SOURCE 500 #ifdef HAVE_FEATURES_H #include #endif #endif #include #include #include #include #include #include #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_RESOURCE_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_SYSMACROS_H #include #endif #ifdef HAVE_FCNTL_H #include #endif /* Standard headers */ #include #include #include #include #include #include #include #ifdef HAVE_SYS_SENDFILE_H #include #endif #define NONE Val_int(0) #define SOME 0 #define Nothing 0 /* From the unix lib */ #define UNIX_BUFFER_SIZE 16384 extern void uerror(char *, value) Noreturn; static char* str_option(value v) { if (v == NONE) return (char*) NULL; else return String_val(Field(v,SOME)); } struct locale_conversion { value hash; int ltype; const char *name; }; struct locale_conversion contable[] = { {0, LC_ALL, "LC_ALL"}, {0, LC_COLLATE, "LC_COLLATE"}, {0, LC_CTYPE, "LC_CTYPE"}, {0, LC_MONETARY, "LC_MONETARY"}, {0, LC_MESSAGES, "LC_MESSAGES"}, {0, LC_NUMERIC, "LC_NUMERIC"}, {0, LC_TIME, "LC_TIME"}, {0, 0, 0} }; int contable_init = 0; value signpos_conversion[6]; int signpos_set = 0; value stew_set_locale(value whatopt, value type) { CAMLparam2(type, whatopt); CAMLlocal2(ret, nl); char *newlocale; struct locale_conversion *c; char *locale = str_option(whatopt); int whattype = 0; if (contable_init == 0) { contable_init = 1; for (c = contable; c && c->name; c++) c->hash = hash_variant((char *)c->name); } for (c = contable; c && c->name; c++) { if (c->hash == type) { whattype = c->ltype; break; } } if (!(c && c->name)) failwith("Locale.set: Unknown locale type"); newlocale = setlocale(whattype, locale); if (!newlocale) CAMLreturn(NONE); nl = copy_string(newlocale); ret = alloc_small(1, SOME); Field(ret, 0) = nl; CAMLreturn(ret); } /* The numeric bits of a struct lconv */ value stew_localeconv_n (void) { CAMLparam0(); CAMLlocal4(dp, ts, g, res); struct lconv *l; l = localeconv(); dp = copy_string(l->decimal_point); ts = copy_string(l->thousands_sep); g = copy_string(l->grouping); res = alloc_tuple(3); Field(res, 0) = dp; Field(res, 1) = ts; Field(res, 2) = g; CAMLreturn(res); } /* The monetary bits of a struct lconv */ value stew_localeconv_m (void) { CAMLparam0(); CAMLlocal5(ics, cs, dp, ts, g); CAMLlocal3(ps, ns, res); struct lconv *l; if (!signpos_set) { signpos_set = 1; signpos_conversion[0] = hash_variant("SurroundBoth"); signpos_conversion[1] = hash_variant("SignPrecedesBoth"); signpos_conversion[2] = hash_variant("SignSucceedsBoth"); signpos_conversion[3] = hash_variant("SignPrecedesCS"); signpos_conversion[4] = hash_variant("SignSucceedsCS"); signpos_conversion[5] = hash_variant("UnknownOrder"); } l = localeconv(); ics = copy_string(l->int_curr_symbol); cs = copy_string(l->currency_symbol); dp = copy_string(l->mon_decimal_point); ts = copy_string(l->mon_thousands_sep); g = copy_string(l->mon_grouping); ps = copy_string(l->positive_sign); ns = copy_string(l->negative_sign); res = alloc_tuple(15); Field(res, 0) = ics; Field(res, 1) = cs; Field(res, 2) = dp; Field(res, 3) = ts; Field(res, 4) = g; Field(res, 5) = ps; Field(res, 6) = ns; Field(res, 7) = Val_int(l->int_frac_digits); Field(res, 8) = Val_int(l->int_frac_digits); Field(res, 9) = Val_bool(l->p_cs_precedes); Field(res, 10) = Val_bool(l->p_sep_by_space); Field(res, 11) = Val_bool(l->n_cs_precedes); Field(res, 12) = Val_bool(l->n_sep_by_space); if (l->p_sign_posn > 4) Field(res, 13) = signpos_conversion[5]; else Field(res, 13) = signpos_conversion[(int)l->p_sign_posn]; if (l->n_sign_posn > 4) Field(res, 14) = signpos_conversion[5]; else Field(res, 14) = signpos_conversion[(int)l->n_sign_posn]; CAMLreturn(res); } value stew_is_alpha(value c) { if (isalpha(Int_val(c))) return Val_true; else return Val_false; } value stew_is_space(value c) { if (isspace(Int_val(c))) return Val_true; else return Val_false; } value stew_is_number(value c) { if (isdigit(Int_val(c))) return Val_true; else return Val_false; } value stew_is_lower(value c) { if (islower(Int_val(c))) return Val_true; else return Val_false; } value stew_is_upper(value c) { if (isupper(Int_val(c))) return Val_true; else return Val_false; } value stew_is_punct(value c) { if (ispunct(Int_val(c))) return Val_true; else return Val_false; } value stew_is_print(value c) { if (isprint(Int_val(c))) return Val_true; else return Val_false; } value stew_is_graph(value c) { if (isgraph(Int_val(c))) return Val_true; else return Val_false; } value stew_is_xdigit(value c) { if (isxdigit(Int_val(c))) return Val_true; else return Val_false; } value stew_to_lower(value c) { return Val_int(tolower(Int_val(c))); } value stew_to_upper(value c) { return Val_int(toupper(Int_val(c))); } static value alloc_tm(struct tm *tm) { value res; res = alloc_tuple(9); Field(res,0) = Val_int(tm->tm_sec); Field(res,1) = Val_int(tm->tm_min); Field(res,2) = Val_int(tm->tm_hour); Field(res,3) = Val_int(tm->tm_mday); Field(res,4) = Val_int(tm->tm_mon); Field(res,5) = Val_int(tm->tm_year); Field(res,6) = Val_int(tm->tm_wday); Field(res,7) = Val_int(tm->tm_yday); Field(res,8) = tm->tm_isdst ? Val_true : Val_false; return res; } static struct tm* tm_val(value t, struct tm *tm) { if (!tm) return NULL; tm->tm_sec = Int_val(Field(t, 0)); tm->tm_min = Int_val(Field(t, 1)); tm->tm_hour = Int_val(Field(t, 2)); tm->tm_mday = Int_val(Field(t, 3)); tm->tm_mon = Int_val(Field(t, 4)); tm->tm_year = Int_val(Field(t, 5)); tm->tm_wday = Int_val(Field(t, 6)); tm->tm_yday = Int_val(Field(t, 7)); tm->tm_isdst = -1; /* tm.tm_isdst = Bool_val(Field(t, 8)); */ return tm; } value stew_time_int32(value unit) { time_t t; time(&t); return copy_int32(t); } value stew_ctime(value for_time) { CAMLparam1(for_time); CAMLlocal1(ret); time_t t1 = (time_t)Int32_val(for_time); char buf[50]; ctime_r(&t1, buf); ret = copy_string(buf); CAMLreturn(ret); } value stew_asctime(value for_time) { CAMLparam1(for_time); CAMLlocal1(ret); struct tm tm; char buf[50]; tm_val(for_time, &tm); asctime_r(&tm, buf); ret = copy_string(buf); CAMLreturn(ret); } /* value stew_strftime_time(value fmt, value for_time) { CAMLparam2(fmt, for_time); CAMLlocal1(ret); time_t t1 = (time_t)Int32_val(for_time); struct tm t2; char buf[8192]; localtime_r(&t1, &t2); strftime(buf, sizeof buf, String_val(fmt), &t2); ret = copy_string(buf); CAMLreturn(ret); } */ value stew_strftime_tm(value fmt, value for_time) { CAMLparam2(fmt, for_time); CAMLlocal1(ret); struct tm tm = {0}; char buf[8192]; tm_val(for_time, &tm); strftime(buf, sizeof buf, String_val(fmt), &tm); ret = copy_string(buf); CAMLreturn(ret); } value stew_strptime_tm(value time_str, value fmt) { #ifdef HAVE_STRPTIME CAMLparam2(time_str, fmt); CAMLlocal1(ret); struct tm tm = {0}; char *s; s = strptime(String_val(time_str), String_val(fmt), &tm); if (s == NULL || *s != '\0') { failwith("extern C function stew_strptime_tm failed"); } ret = alloc_tm(&tm); CAMLreturn(ret); #else failwith("stew_strptime_tm not implemented"); #endif } value stew_gmtime(value t) { time_t tyme; struct tm tm; tyme = (time_t) Int32_val(t); gmtime_r(&tyme, &tm); return alloc_tm(&tm); } value stew_localtime(value t) { time_t tyme; struct tm tm; tyme = (time_t) Int32_val(t); localtime_r(&tyme, &tm); return alloc_tm(&tm); } value stew_mktime(value t) { CAMLparam1(t); CAMLlocal3(res, clockval, tmval); struct tm tm; time_t tyme; tm_val(t, &tm); tyme = mktime(&tm); tmval = alloc_tm(&tm); clockval = copy_int32(tyme); res = alloc_tuple(2); Field(res, 0) = clockval; Field(res, 1) = tmval; CAMLreturn(res); } value stew_difftime(value a, value b) { return copy_double(difftime((time_t)Int32_val(a), (time_t)Int32_val(b))); } value stew_sendfile(value o, value i, value off, value count) { size_t len; off_t offset, wrote; int infd, outfd; infd = Int_val(o); outfd = Int_val(i); offset = Int_val(off); len = Int_val(count); #if defined(linux) enter_blocking_section(); len = sendfile(outfd, infd, &offset, len); leave_blocking_section(); if (len >= 0) return Val_int(len); else uerror("sendfile", Nothing); #elif defined(__FreeBSD__) enter_blocking_section(); len = sendfile(outfd, infd, offset, len, NULL, &wrote, 0); leave_blocking_section(); if (len == 0) return Val_int(wrote); else uerror("sendfile", Nothing); #else failwith("sendfile unimplemented!"); #endif } /* From getserv.c */ #ifdef HAVE_GETSERVENT static value alloc_service_entry(struct servent *entry) { value res; value name = Val_unit, aliases = Val_unit, proto = Val_unit; Begin_roots3 (name, aliases, proto); name = copy_string(entry->s_name); aliases = copy_string_array((const char**)entry->s_aliases); proto = copy_string(entry->s_proto); res = alloc_tuple(4); Field(res,0) = name; Field(res,1) = aliases; Field(res,2) = Val_int(ntohs(entry->s_port)); Field(res,3) = proto; End_roots(); return res; } #endif value stew_getservent(value unit) { #ifdef HAVE_GETSERVENT struct servent *s = getservent(); if (s == NULL) raise_not_found(); return alloc_service_entry(s); #else failwith("getservent unimplemented"); #endif } value stew_setservent(value stay) { #ifdef HAVE_SETSERVENT setservent(Bool_val(stay)); return Val_unit; #else failwith("setservent unimplemented"); #endif } value stew_endservent(value unit) { #ifdef HAVE_ENDSERVENT endservent(); return Val_unit; #else failwith("endservent unimplemented"); #endif } value stew_hash_variant(value s) { return hash_variant(String_val(s)); } value stew_strcoll(value a, value b) { int r; r = strcoll(String_val(a), String_val(b)); if (r < 0) return Val_int(-1); else if (r == 0) return Val_int(0); else return Val_int(1); } value stew_pread(value fd, value buf, value vofs, value len) { #ifdef HAVE_PREAD long numbytes, ofs; int ret; char iobuf[UNIX_BUFFER_SIZE]; Begin_root(buf); numbytes = Long_val(len); ofs = Long_val(vofs); if (numbytes > UNIX_BUFFER_SIZE) numbytes = UNIX_BUFFER_SIZE; enter_blocking_section(); ret = pread(Int_val(fd), iobuf, ofs, (int) numbytes); leave_blocking_section(); if (ret == -1) uerror("pread", Nothing); memmove(&Byte(buf, ofs), iobuf, ret); End_roots(); return Val_int(ret); #else failwith("pread unimplemented"); #endif } value stew_pwrite(value fd, value buf, value vofs, value vlen) { #ifdef HAVE_PWRITE long ofs, len, written; int numbytes, ret; char iobuf[UNIX_BUFFER_SIZE]; Begin_root(buf); ofs = Long_val(vofs); len = Long_val(vlen); written = 0; while (len > 0) { numbytes = len > UNIX_BUFFER_SIZE ? UNIX_BUFFER_SIZE : len; memmove(iobuf, &Byte(buf, ofs), numbytes); enter_blocking_section(); ret = pwrite(Int_val(fd), iobuf, ofs, numbytes); leave_blocking_section(); if (ret == -1) { if ((errno == EAGAIN || errno == EWOULDBLOCK) && written > 0) break; uerror("pwrite", Nothing); } written += ret; ofs += ret; len -= ret; } End_roots(); return Val_long(written); #else failwith("pwrite unimplemented"); #endif } value stew_gettimeofday(value u) { #ifdef HAVE_GETTIMEOFDAY struct timeval t; CAMLparam1(u); CAMLlocal3(res, sec, usec); if (gettimeofday(&t, NULL)) uerror("gettimeofday", Nothing); sec = copy_int32(t.tv_sec); usec = copy_int32(t.tv_usec); res = alloc_tuple(2); Field(res, 0) = sec; Field(res, 1) = usec; CAMLreturn(res); #else failwith("gettimeofday unimplemented"); #endif } value stew_getrusage(value whov) { #ifdef HAVE_GETRUSAGE struct rusage r; int who; CAMLparam1(whov); CAMLlocal5(res, utime, stime, sec, usec); if (Int_val(whov) == 0) who = RUSAGE_SELF; else who = RUSAGE_CHILDREN; if (getrusage(who, &r)) uerror("getrusage", Nothing); sec = copy_int32(r.ru_utime.tv_sec); usec = copy_int32(r.ru_utime.tv_usec); utime = alloc_tuple(2); Field(utime, 0) = sec; Field(utime, 1) = usec; sec = copy_int32(r.ru_stime.tv_sec); usec = copy_int32(r.ru_stime.tv_usec); stime = alloc_tuple(2); Field(stime, 0) = sec; Field(stime, 1) = usec; res = alloc_tuple(16); Field(res, 0) = utime; Field(res, 1) = stime; #ifdef __APPLE__ /* For some reason, the OS X headers only define these structures if _POSIX_C_SOURCE is undefined. However, since the headers seem to always define this, #undefing it doesn't do any good. *sigh* */ { int n; for (n = 2; n < 16; n++) Field(res, n) = Val_long(0); } #else Field(res, 2) = Val_long(r.ru_maxrss); Field(res, 3) = Val_long(r.ru_ixrss); Field(res, 4) = Val_long(r.ru_idrss); Field(res, 5) = Val_long(r.ru_isrss); Field(res, 6) = Val_long(r.ru_minflt); Field(res, 7) = Val_long(r.ru_majflt); Field(res, 8) = Val_long(r.ru_nswap); Field(res, 9) = Val_long(r.ru_inblock); Field(res, 10) = Val_long(r.ru_oublock); Field(res, 11) = Val_long(r.ru_msgsnd); Field(res, 12) = Val_long(r.ru_msgrcv); Field(res, 13) = Val_long(r.ru_nsignals); Field(res, 14) = Val_long(r.ru_nvcsw); Field(res, 15) = Val_long(r.ru_nivcsw); #endif CAMLreturn(res); #else failwith("getrusage unimplemented"); #endif } value stew_getrlimit(value resource) { #ifdef HAVE_GETRLIMIT CAMLparam1(resource); CAMLlocal1(retval); int r; struct rlimit lim; switch (Int_val(resource)) { case 0: r = RLIMIT_CPU; break; case 1: r = RLIMIT_FSIZE; break; case 2: r = RLIMIT_DATA; break; case 3: r = RLIMIT_STACK; break; case 4: r = RLIMIT_CORE; break; case 5: #ifdef RLIMIT_RSS r = RLIMIT_RSS; #else failwith("RSS is not defined by your os"); #endif break; case 6: #ifdef RLIMIT_NPROC r = RLIMIT_NPROC; #else failwith("NPROC not implimented by your OS"); #endif break; case 7: #ifdef RLIMIT_NOFILE r = RLIMIT_NOFILE; #else r = RLIMIT_OFILE: #endif break; case 8: #ifdef RLIMIT_MEMLOCK r = RLIMIT_MEMLOCK; #else failwith("MEMLOCK is not defined by your os"); #endif break; #if defined(linux) case 9: r = RLIMIT_AS; break; #endif default: errno = EINVAL; uerror("getrlimit", Nothing); } if (getrlimit(r, &lim) < 0) uerror("getrlimit", Nothing); retval = alloc_tuple(2); Field(retval, 0) = Val_int(lim.rlim_cur); Field(retval, 1) = Val_int(lim.rlim_max); CAMLreturn(retval); #else failwith("getrlimit unimplemented"); #endif } value stew_setrlimit(value resource, value rlimit) { #ifdef HAVE_SETRLIMIT int r; struct rlimit lim; switch (Int_val(resource)) { case 0: r = RLIMIT_CPU; break; case 1: r = RLIMIT_FSIZE; break; case 2: r = RLIMIT_DATA; break; case 3: r = RLIMIT_STACK; break; case 4: r = RLIMIT_CORE; break; case 5: #ifdef RLIMIT_RSS r = RLIMIT_RSS; #else failwith("RSS is not defined by your os"); #endif break; case 6: #ifdef RLIMIT_NPROC r = RLIMIT_NPROC; #else failwith("NPROC is not defined by your os"); #endif break; case 7: #ifdef RLIMIT_NOFILE r = RLIMIT_NOFILE; #else r = RLIMIT_OFILE: #endif break; case 8: #ifdef RLIMIT_MEMLOCK r = RLIMIT_MEMLOCK; #else failwith("MEMLOCK is not defined by your os"); #endif break; #if defined(linux) case 9: r = RLIMIT_AS; break; #endif default: errno = EINVAL; uerror("getrlimit", Nothing); } lim.rlim_cur = Int_val(Field(rlimit, 0)); lim.rlim_max = Int_val(Field(rlimit, 1)); if (setrlimit(r, &lim) < 0) uerror("setrlimit", Nothing); return Val_unit; #else failwith("getrlimit unimplemented"); #endif } value stew_setpgid(value pid, value pgid) { #ifdef HAVE_SETPGID if (setpgid(Int_val(pid), Int_val(pgid)) < 0) uerror("setpgid", Nothing); return Val_unit; #else failwith("setgpid unimplemented"); #endif } value stew_getpgid(value pid) { #ifdef HAVE_GETPGID int ret = getpgid(Int_val(pid)); if (ret < 0) uerror("getpgid", Nothing); return Val_int(ret); #else failwith("getgpid unimplemented"); #endif } value stew_getpgrp(value unit) { #ifdef HAVE_GETPGRP int ret = getpgrp(); if (ret < 0) uerror("getpgrp", Nothing); return Val_int(ret); #else failwith("getpgrp unimplemented"); #endif } value stew_setpgrp(value unit) { #if defined(HAVE_SETPGRP) /* This needs a better test to tell if we have 0, 1, or 2 argument forms of setpgrp() */ #if defined(__FreeBSD__) || defined(__APPLE__) if (setpgid(0,0) < 0) #else if (setpgrp() < 0) #endif uerror("setpgrp", Nothing); return Val_unit; #else /* !HAVE_SETPGRP */ failwith("setpgrp unimplemented"); #endif } #if defined(__APPLE__) && !defined(makedev) #define major(x) ((int32_t)(((u_int32_t)(x) >> 24) & 0xff)) #define minor(x) ((int32_t)((x) & 0xffffff)) #define makedev(x,y) ((dev_t)(((x) << 24) | (y))) #endif value stew_mknod(value fname, value mod, value type, value major, value minor) { #ifdef HAVE_MKNOD dev_t dev; mode_t mode = Int_val(mod); dev = makedev(Int_val(major), Int_val(minor)); if (Int_val(type) == 0) mode |= S_IFBLK; else mode |= S_IFCHR; if (mknod(String_val(fname), mode, dev) < 0) uerror("mknod", fname); return Val_unit; #else failwith("mknod unimplemented"); #endif }