swisseph_wasm/
lib.rs

1#![no_std]
2extern crate alloc;
3
4use wasm_bindgen::prelude::*;
5use alloc::string::{String, ToString};
6use core::ffi::CStr;
7use js_sys::{Object, Reflect};
8
9// Include generated Swiss Ephemeris bindings
10pub mod bindings;
11
12#[allow(dead_code, non_upper_case_globals, non_camel_case_types, non_snake_case)]
13pub mod swe_bindings {
14    pub use crate::bindings::*;
15
16    // Constants (Manually replicated from swephexp.h)
17    pub const SE_SUN: i32 = 0;
18    pub const SE_MOON: i32 = 1;
19    pub const SE_GREG_CAL: i32 = 1;
20    pub const SEFLG_SWIEPH: i32 = 2;
21    pub const SEFLG_SPEED: i32 = 256;
22    pub const SE_SIDM_LAHIRI: i32 = 1;
23    pub const SE_SIDM_RAMAN: i32 = 3;
24    pub const SE_SIDM_KRISHNAMURTI: i32 = 5;
25    pub const SE_SIDM_TRUE_CITRA: i32 = 27;
26}
27
28// --- EXPORTS (Renamed to swe_*) ---
29
30#[wasm_bindgen(js_name = swe_calc_ut)]
31pub fn js_swe_calc_ut(tjd_ut: f64, ipl: i32, iflag: i32) -> Result<JsValue, JsValue> {
32    let mut xx = [0.0; 6];
33    let mut serr = [0i8; 256];
34    
35    let ret = unsafe {
36        swe_bindings::swe_calc_ut(tjd_ut, ipl, iflag, xx.as_mut_ptr(), serr.as_mut_ptr())
37    };
38    
39    if ret < 0 {
40        let err_msg = unsafe { CStr::from_ptr(serr.as_ptr()).to_str().unwrap_or("Unknown error") };
41        return Err(JsValue::from_str(err_msg));
42    }
43    
44    let obj = Object::new();
45    Reflect::set(&obj, &"longitude".into(), &xx[0].into())?;
46    Reflect::set(&obj, &"latitude".into(), &xx[1].into())?;
47    Reflect::set(&obj, &"distance".into(), &xx[2].into())?;
48    Reflect::set(&obj, &"speed_long".into(), &xx[3].into())?;
49    Reflect::set(&obj, &"speed_lat".into(), &xx[4].into())?;
50    Reflect::set(&obj, &"speed_dist".into(), &xx[5].into())?;
51    Reflect::set(&obj, &"rc_flags".into(), &ret.into())?;
52    
53    Ok(obj.into())
54}
55
56#[wasm_bindgen(js_name = swe_julday)]
57pub fn js_swe_julday(year: i32, month: i32, day: i32, hour: f64, gregflag: i32) -> f64 {
58    unsafe {
59        swe_bindings::swe_julday(year, month, day, hour, gregflag)
60    }
61}
62
63#[wasm_bindgen(js_name = swe_revjul)]
64pub fn js_swe_revjul(tjd: f64, gregflag: i32) -> JsValue {
65    let mut year = 0;
66    let mut month = 0;
67    let mut day = 0;
68    let mut hour = 0.0;
69    
70    unsafe {
71        swe_bindings::swe_revjul(tjd, gregflag, &mut year, &mut month, &mut day, &mut hour);
72    }
73    
74    let obj = Object::new();
75    Reflect::set(&obj, &"year".into(), &year.into()).unwrap();
76    Reflect::set(&obj, &"month".into(), &month.into()).unwrap();
77    Reflect::set(&obj, &"day".into(), &day.into()).unwrap();
78    Reflect::set(&obj, &"hour".into(), &hour.into()).unwrap();
79    obj.into()
80}
81
82#[wasm_bindgen(js_name = swe_fixstar_ut)]
83pub fn js_swe_fixstar_ut(star: &str, tjd_ut: f64, iflag: i32) -> Result<JsValue, JsValue> {
84    
85    // Manual CString construction for no_std
86    let mut star_bytes = star.as_bytes().to_vec();
87    star_bytes.push(0);
88    let star_ptr = star_bytes.as_mut_ptr() as *mut i8;
89    
90    let mut xx = [0.0; 6];
91    let mut serr = [0i8; 256];
92    
93    let ret = unsafe {
94        swe_bindings::swe_fixstar_ut(star_ptr, tjd_ut, iflag, xx.as_mut_ptr(), serr.as_mut_ptr())
95    };
96
97    if ret < 0 {
98        let err_msg = unsafe { CStr::from_ptr(serr.as_ptr()).to_str().unwrap_or("Unknown error") };
99        return Err(JsValue::from_str(err_msg));
100    }
101    
102    let obj = Object::new();
103    Reflect::set(&obj, &"name".into(), &JsValue::from_str(star))?;
104    Reflect::set(&obj, &"longitude".into(), &xx[0].into())?;
105    Reflect::set(&obj, &"latitude".into(), &xx[1].into())?;
106    Reflect::set(&obj, &"distance".into(), &xx[2].into())?;
107    Reflect::set(&obj, &"rc_flags".into(), &ret.into())?;
108    
109    Ok(obj.into())
110}
111
112#[wasm_bindgen(js_name = swe_pheno_ut)]
113pub fn js_swe_pheno_ut(tjd_ut: f64, ipl: i32, iflag: i32) -> Result<JsValue, JsValue> {
114    let mut attr = [0.0; 20];
115    let mut serr = [0i8; 256];
116    
117    let ret = unsafe {
118        swe_bindings::swe_pheno_ut(tjd_ut, ipl, iflag, attr.as_mut_ptr(), serr.as_mut_ptr())
119    };
120    
121    if ret < 0 {
122        let err_msg = unsafe { CStr::from_ptr(serr.as_ptr()).to_str().unwrap_or("Unknown error") };
123        return Err(JsValue::from_str(err_msg));
124    }
125    
126    let obj = Object::new();
127    Reflect::set(&obj, &"phase_angle".into(), &attr[0].into())?;
128    Reflect::set(&obj, &"phase".into(), &attr[1].into())?;
129    Reflect::set(&obj, &"elongation".into(), &attr[2].into())?;
130    Reflect::set(&obj, &"diameter_app".into(), &attr[3].into())?;
131    Reflect::set(&obj, &"magnitude".into(), &attr[4].into())?;
132    
133    Ok(obj.into())
134}
135
136#[wasm_bindgen(js_name = swe_set_topo)]
137pub fn js_swe_set_topo(geolon: f64, geolat: f64, geoalt: f64) {
138    unsafe {
139        swe_bindings::swe_set_topo(geolon, geolat, geoalt);
140    }
141}
142
143#[wasm_bindgen(js_name = swe_set_sid_mode)]
144pub fn js_swe_set_sid_mode(sid_mode: i32, t0: f64, ayan_t0: f64) {
145    unsafe {
146        swe_bindings::swe_set_sid_mode(sid_mode, t0, ayan_t0);
147    }
148}
149
150#[wasm_bindgen(js_name = swe_get_ayanamsa_ut)]
151pub fn js_swe_get_ayanamsa_ut(tjd_ut: f64) -> f64 {
152    unsafe {
153        swe_bindings::swe_get_ayanamsa_ut(tjd_ut)
154    }
155}
156
157#[wasm_bindgen(js_name = swe_get_planet_name)]
158pub fn js_swe_get_planet_name(ipl: i32) -> String {
159    let mut spname = [0i8; 256];
160    unsafe {
161        swe_bindings::swe_get_planet_name(ipl, spname.as_mut_ptr());
162        CStr::from_ptr(spname.as_ptr()).to_str().unwrap_or("").to_string()
163    }
164}
165
166#[wasm_bindgen(js_name = swe_sidtime)]
167pub fn js_swe_sidtime(tjd_ut: f64) -> f64 {
168    unsafe {
169        swe_bindings::swe_sidtime(tjd_ut)
170    }
171}
172
173
174// --- REQUIRED C SHIMS FOR WASM ---
175// These are needed because compiling C code often requires stdlib functions not present in pure Wasm environment.
176#[cfg(target_arch = "wasm32")]
177mod shims {
178    use super::*;
179
180    #[unsafe(no_mangle)]
181    pub unsafe extern "C" fn sin(x: f64) -> f64 { libm::sin(x) }
182    #[unsafe(no_mangle)]
183    pub unsafe extern "C" fn cos(x: f64) -> f64 { libm::cos(x) }
184    #[unsafe(no_mangle)]
185    pub unsafe extern "C" fn tan(x: f64) -> f64 { libm::tan(x) }
186    #[unsafe(no_mangle)]
187    pub unsafe extern "C" fn asin(x: f64) -> f64 { libm::asin(x) }
188    #[unsafe(no_mangle)]
189    pub unsafe extern "C" fn acos(x: f64) -> f64 { libm::acos(x) }
190    #[unsafe(no_mangle)]
191    pub unsafe extern "C" fn atan(x: f64) -> f64 { libm::atan(x) }
192    #[unsafe(no_mangle)]
193    pub unsafe extern "C" fn atan2(y: f64, x: f64) -> f64 { libm::atan2(y, x) }
194    #[unsafe(no_mangle)]
195    pub unsafe extern "C" fn sqrt(x: f64) -> f64 { libm::sqrt(x) }
196    #[unsafe(no_mangle)]
197    pub unsafe extern "C" fn log(x: f64) -> f64 { libm::log(x) }
198    #[unsafe(no_mangle)]
199    pub unsafe extern "C" fn exp(x: f64) -> f64 { libm::exp(x) }
200    #[unsafe(no_mangle)]
201    pub unsafe extern "C" fn pow(x: f64, y: f64) -> f64 { libm::pow(x, y) }
202    #[unsafe(no_mangle)]
203    pub unsafe extern "C" fn fabs(x: f64) -> f64 { libm::fabs(x) }
204    #[unsafe(no_mangle)]
205    pub unsafe extern "C" fn ceil(x: f64) -> f64 { libm::ceil(x) }
206    #[unsafe(no_mangle)]
207    pub unsafe extern "C" fn floor(x: f64) -> f64 { libm::floor(x) }
208    #[unsafe(no_mangle)]
209    pub unsafe extern "C" fn fmod(x: f64, y: f64) -> f64 { libm::fmod(x, y) }
210    #[unsafe(no_mangle)]
211    pub unsafe extern "C" fn log10(x: f64) -> f64 { libm::log10(x) }
212    
213    const MAGIC: usize = 0xDEADBEEF;
214
215    #[unsafe(no_mangle)]
216    pub unsafe extern "C" fn malloc(size: usize) -> *mut u8 {
217        unsafe {
218            let header_size = 8;
219            let total_size = size + header_size;
220            let layout = alloc::alloc::Layout::from_size_align_unchecked(total_size, 8);
221            let ptr = alloc::alloc::alloc(layout);
222            
223            if ptr.is_null() { return core::ptr::null_mut(); }
224            
225            // Header: [Magic (4 bytes) | Size (4 bytes)] (on 32-bit wasm)
226            // Actually usize is u32. 
227            // We use 2 * usize = 8 bytes.
228            let header_ptr = ptr as *mut usize;
229            *header_ptr = MAGIC;
230            *header_ptr.add(1) = total_size;
231            
232            // Return pointer after header
233            ptr.add(header_size)
234        }
235    }
236    
237    #[unsafe(no_mangle)]
238    pub unsafe extern "C" fn free(ptr: *mut u8) {
239        unsafe {
240            if ptr.is_null() { return; }
241            
242            let header_size = 8;
243            let real_ptr = ptr.sub(header_size);
244            let header_ptr = real_ptr as *mut usize;
245            
246            // Safety check: Verify magic number
247            if *header_ptr != MAGIC {
248                // Not our memory or corrupted. Do nothing to avoid crash.
249                return;
250            }
251
252            let total_size = *header_ptr.add(1);
253            let layout = alloc::alloc::Layout::from_size_align_unchecked(total_size, 8);
254            
255            alloc::alloc::dealloc(real_ptr, layout);
256        }
257    }
258    
259    #[unsafe(no_mangle)]
260    pub unsafe extern "C" fn calloc(nmemb: usize, size: usize) -> *mut u8 {
261        unsafe {
262            let total_size = nmemb * size;
263            let ptr = malloc(total_size); // Uses our malloc with header
264            if !ptr.is_null() {
265                core::ptr::write_bytes(ptr, 0, total_size);
266            }
267            ptr
268        }
269    }
270    
271    #[unsafe(no_mangle)]
272    pub unsafe extern "C" fn realloc(ptr: *mut u8, new_size: usize) -> *mut u8 {
273        unsafe {
274            if ptr.is_null() {
275                return malloc(new_size);
276            }
277            if new_size == 0 {
278                free(ptr);
279                return core::ptr::null_mut();
280            }
281            
282            let header_size = 8;
283            let real_ptr = ptr.sub(header_size);
284            let header_ptr = real_ptr as *mut usize;
285            
286            // Safety check
287            if *header_ptr != MAGIC {
288                 // Can't realloc something we didn't alloc. 
289                 // Best effort: malloc new, copy nothing (unsafe to read), return new.
290                 // Or fail. Failing is safer.
291                 return core::ptr::null_mut();
292            }
293
294            let old_total_size = *header_ptr.add(1);
295            let old_user_size = old_total_size - header_size;
296            
297            let new_ptr = malloc(new_size);
298            if !new_ptr.is_null() {
299                let copy_size = if old_user_size < new_size { old_user_size } else { new_size };
300                core::ptr::copy_nonoverlapping(ptr, new_ptr, copy_size);
301                free(ptr);
302            }
303            new_ptr
304        }
305    }
306    
307    #[unsafe(no_mangle)]
308    pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 {
309        unsafe {
310            core::ptr::copy_nonoverlapping(src, dest, n);
311            dest
312        }
313    }
314    
315    #[unsafe(no_mangle)]
316    pub unsafe extern "C" fn memset(s: *mut u8, c: i32, n: usize) -> *mut u8 {
317        unsafe {
318            core::ptr::write_bytes(s, c as u8, n);
319            s
320        }
321    }
322    
323    #[unsafe(no_mangle)]
324    pub unsafe extern "C" fn strcpy(dest: *mut u8, src: *const u8) -> *mut u8 {
325        unsafe {
326            let mut i = 0;
327            loop {
328                let c = *src.add(i);
329                *dest.add(i) = c;
330                if c == 0 { break; }
331                i += 1;
332            }
333            dest
334        }
335    }
336    
337    #[unsafe(no_mangle)]
338    pub unsafe extern "C" fn strncpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 {
339        unsafe {
340            let mut i = 0;
341            while i < n {
342                let c = *src.add(i);
343                *dest.add(i) = c;
344                if c == 0 {
345                    while i < n {
346                        *dest.add(i) = 0;
347                        i += 1;
348                    }
349                    break;
350                }
351                i += 1;
352            }
353            dest
354        }
355    }
356    
357    #[unsafe(no_mangle)]
358    pub unsafe extern "C" fn strlen(s: *const i8) -> usize {
359        unsafe {
360            let mut len = 0;
361            while *s.add(len) != 0 {
362                len += 1;
363            }
364            len
365        }
366    }
367    
368    #[unsafe(no_mangle)]
369    pub unsafe extern "C" fn strcmp(s1: *const i8, s2: *const i8) -> i32 {
370        unsafe {
371            let mut i = 0;
372            loop {
373                let c1 = *s1.add(i);
374                let c2 = *s2.add(i);
375                if c1 != c2 { return (c1 - c2) as i32; }
376                if c1 == 0 { return 0; }
377                i += 1;
378            }
379        }
380    }
381    
382    #[unsafe(no_mangle)]
383    pub unsafe extern "C" fn strncmp(s1: *const i8, s2: *const i8, n: usize) -> i32 {
384        unsafe {
385            let mut i = 0;
386            while i < n {
387                let c1 = *s1.add(i);
388                let c2 = *s2.add(i);
389                if c1 != c2 { return (c1 - c2) as i32; }
390                if c1 == 0 { return 0; }
391                i += 1;
392            }
393            0
394        }
395    }
396    
397    #[unsafe(no_mangle)]
398    pub unsafe extern "C" fn strstr(haystack: *const i8, needle: *const i8) -> *const i8 {
399        unsafe {
400            let needle_len = strlen(needle);
401            if needle_len == 0 { return haystack; }
402            let mut h = haystack;
403            while *h != 0 {
404                if strncmp(h, needle, needle_len) == 0 {
405                    return h;
406                }
407                h = h.add(1);
408            }
409            core::ptr::null()
410        }
411    }
412    
413    #[unsafe(no_mangle)]
414    pub unsafe extern "C" fn abs(j: i32) -> i32 { j.abs() }
415    #[unsafe(no_mangle)]
416    pub unsafe extern "C" fn labs(j: i64) -> i64 { j.abs() }
417    
418    #[unsafe(no_mangle)]
419    pub unsafe extern "C" fn atof(_str: *const i8) -> f64 { 0.0 } 
420    #[unsafe(no_mangle)]
421    pub unsafe extern "C" fn atoi(_str: *const i8) -> i32 { 0 } 
422    #[unsafe(no_mangle)]
423    pub unsafe extern "C" fn atol(_str: *const i8) -> i64 { 0 }
424    
425    #[unsafe(no_mangle)]
426    pub unsafe extern "C" fn fopen(_filename: *const i8, _mode: *const i8) -> *mut u8 { core::ptr::null_mut() }
427    #[unsafe(no_mangle)]
428    pub unsafe extern "C" fn fclose(_stream: *mut u8) -> i32 { 0 }
429    #[unsafe(no_mangle)]
430    pub unsafe extern "C" fn fseek(_stream: *mut u8, _offset: i64, _whence: i32) -> i32 { 0 }
431    #[unsafe(no_mangle)]
432    pub unsafe extern "C" fn ftell(_stream: *mut u8) -> i64 { 0 }
433    #[unsafe(no_mangle)]
434    pub unsafe extern "C" fn fread(_ptr: *mut u8, _size: usize, _nmemb: usize, _stream: *mut u8) -> usize { 0 }
435    #[unsafe(no_mangle)]
436    pub unsafe extern "C" fn fwrite(_ptr: *const u8, _size: usize, nmemb: usize, _stream: *mut u8) -> usize { nmemb }
437    
438    #[unsafe(no_mangle)]
439    pub unsafe extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 {
440        unsafe {
441            core::ptr::copy(src, dest, n);
442            dest
443        }
444    }
445    #[unsafe(no_mangle)]
446    pub unsafe extern "C" fn fgets(_str: *mut i8, _n: i32, _stream: *mut u8) -> *mut i8 { core::ptr::null_mut() }
447    #[unsafe(no_mangle)]
448    pub unsafe extern "C" fn fflush(_stream: *mut u8) -> i32 { 0 }
449    #[unsafe(no_mangle)]
450    pub unsafe extern "C" fn exit(_status: i32) { panic!("exit called") }
451    
452    #[unsafe(no_mangle)]
453    pub unsafe extern "C" fn isspace(c: i32) -> i32 {
454        if (c as u8 as char).is_whitespace() { 1 } else { 0 }
455    }
456    #[unsafe(no_mangle)]
457    pub unsafe extern "C" fn isdigit(c: i32) -> i32 {
458        if (c as u8 as char).is_ascii_digit() { 1 } else { 0 }
459    }
460    #[unsafe(no_mangle)]
461    pub unsafe extern "C" fn isalpha(c: i32) -> i32 {
462        if (c as u8 as char).is_ascii_alphabetic() { 1 } else { 0 }
463    }
464    #[unsafe(no_mangle)]
465    pub unsafe extern "C" fn isalnum(c: i32) -> i32 {
466        if (c as u8 as char).is_ascii_alphanumeric() { 1 } else { 0 }
467    }
468    #[unsafe(no_mangle)]
469    pub unsafe extern "C" fn isupper(c: i32) -> i32 {
470        if (c as u8 as char).is_ascii_uppercase() { 1 } else { 0 }
471    }
472    
473    #[unsafe(no_mangle)]
474    pub unsafe extern "C" fn fseeko(_stream: *mut u8, _offset: i64, _whence: i32) -> i32 { 0 }
475    #[unsafe(no_mangle)]
476    pub unsafe extern "C" fn ftello(_stream: *mut u8) -> i64 { 0 }
477    
478    #[unsafe(no_mangle)]
479    pub unsafe extern "C" fn strpbrk(s: *const i8, accept: *const i8) -> *mut i8 {
480        unsafe {
481            let mut s_ptr = s;
482            while *s_ptr != 0 {
483                let mut a_ptr = accept;
484                while *a_ptr != 0 {
485                    if *s_ptr == *a_ptr {
486                        return s_ptr as *mut i8;
487                    }
488                    a_ptr = a_ptr.add(1);
489                }
490                s_ptr = s_ptr.add(1);
491            }
492            core::ptr::null_mut()
493        }
494    }
495    
496    #[unsafe(no_mangle)]
497    pub unsafe extern "C" fn memchr(s: *const u8, c: i32, n: usize) -> *mut u8 {
498        unsafe {
499            let mut i = 0;
500            while i < n {
501                if *s.add(i) == c as u8 {
502                    return s.add(i) as *mut u8;
503                }
504                i += 1;
505            }
506            core::ptr::null_mut()
507        }
508    }
509    
510    #[unsafe(no_mangle)]
511    pub unsafe extern "C" fn strdup(s: *const i8) -> *mut i8 {
512        unsafe {
513            let len = strlen(s);
514            let layout = alloc::alloc::Layout::from_size_align_unchecked(len + 1, 8);
515            let ptr = alloc::alloc::alloc(layout) as *mut i8;
516            if !ptr.is_null() {
517                strcpy(ptr as *mut u8, s as *const u8);
518            }
519            ptr
520        }
521    }
522    
523    
524    #[unsafe(no_mangle)]
525    pub unsafe extern "C" fn strcat(dest: *mut i8, src: *const i8) -> *mut i8 {
526        unsafe {
527            let len = strlen(dest);
528            strcpy(dest.add(len) as *mut u8, src as *const u8);
529            dest
530        }
531    }
532    
533    #[unsafe(no_mangle)]
534    pub unsafe extern "C" fn strchr(s: *const i8, c: i32) -> *mut i8 {
535        unsafe {
536            let mut s_ptr = s;
537            loop {
538                if *s_ptr == c as i8 { return s_ptr as *mut i8; }
539                if *s_ptr == 0 { return core::ptr::null_mut(); }
540                s_ptr = s_ptr.add(1);
541            }
542        }
543    }
544    
545    #[unsafe(no_mangle)]
546    pub unsafe extern "C" fn strrchr(s: *const i8, c: i32) -> *mut i8 {
547        unsafe {
548            let mut last = core::ptr::null_mut();
549            let mut s_ptr = s;
550            loop {
551                if *s_ptr == c as i8 { last = s_ptr as *mut i8; }
552                if *s_ptr == 0 { return last; }
553                s_ptr = s_ptr.add(1);
554            }
555        }
556    }
557    
558    #[unsafe(no_mangle)]
559    pub unsafe extern "C" fn tolower(c: i32) -> i32 {
560        (c as u8).to_ascii_lowercase() as i32
561    }
562    
563    // dlfcn stubs
564    #[unsafe(no_mangle)]
565    pub unsafe extern "C" fn dlopen(_filename: *const i8, _flag: i32) -> *mut u8 { core::ptr::null_mut() }
566    #[unsafe(no_mangle)]
567    pub unsafe extern "C" fn dlerror() -> *mut i8 { core::ptr::null_mut() }
568    #[unsafe(no_mangle)]
569    pub unsafe extern "C" fn dlsym(_handle: *mut u8, _symbol: *const i8) -> *mut u8 { core::ptr::null_mut() }
570    #[unsafe(no_mangle)]
571    pub unsafe extern "C" fn dlclose(_handle: *mut u8) -> i32 { 0 }
572    #[unsafe(no_mangle)]
573    pub unsafe extern "C" fn dladdr(_addr: *const u8, _info: *mut u8) -> i32 { 0 }
574    
575    // stdlib stubs
576    #[unsafe(no_mangle)]
577    pub unsafe extern "C" fn getenv(_name: *const i8) -> *mut i8 { core::ptr::null_mut() }
578    #[unsafe(no_mangle)]
579    pub unsafe extern "C" fn qsort(_base: *mut u8, _nmemb: usize, _size: usize, _compar: *const u8) {}
580    #[unsafe(no_mangle)]
581    pub unsafe extern "C" fn bsearch(_key: *const u8, _base: *const u8, _nmemb: usize, _size: usize, _compar: *const u8) -> *mut u8 {
582        core::ptr::null_mut()
583    }
584    
585    // stdio stubs
586    #[unsafe(no_mangle)]
587    pub unsafe extern "C" fn rewind(_stream: *mut u8) { }
588    
589    // unistd stubs
590    #[unsafe(no_mangle)]
591    pub unsafe extern "C" fn readlink(_path: *const i8, _buf: *mut i8, _bufsiz: usize) -> isize { -1 }
592    
593    // sys/stat stubs
594    #[unsafe(no_mangle)]
595    pub unsafe extern "C" fn stat(_path: *const i8, _buf: *mut u8) -> i32 { -1 }
596}