add cross-platform open()