Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ results

npm-debug.log
node_modules
build/
.vscode
49 changes: 43 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,46 @@
node-dup2
=========
There isn't any way for us to redirect #1(fd) to any other file, before I born.
There wasn't any way for us to redirect #1(fd) to any other file, before I was born.

好吧,我是说,node里面没有dup2函数(当然你可以先关掉一个文件描述符,然后立即打开一个文件),我的出现就是用来提供dup2支持的。
Demo
require("node-dup2").invoke(oldfd, newfd);
此句会先关掉newfd文件描述符,而克隆oldfd的文件打开状态到newfd上。
失败时返回-1
Example
-------

```js
const dup = require('dup')
const fs = require('fs')

let test = async () => {
try {
let oldfd = fs.openSync('myFile.dat', 'a')
let newfd = await dup.change(oldfd, 1)
} catch (error) {
throw error
}

console.log('New FileDescriptor:', newfd);
}

test() // --> New FileDescriptor: 1
```

API
-------------

```js
const dup = require('dup')
```

### .change(oldfd, newfd)

- oldfd: The old file descriptor
- newfd: The new file descriptor
- Returns a resolved promise with the new file descriptor
- Returns a rejected promise with an Error when the File descriptor cant be changed and it dont match with the new File descriptor


Testing
-------

```bash
$ npm test
```
161 changes: 141 additions & 20 deletions dup2.cc
Original file line number Diff line number Diff line change
@@ -1,32 +1,153 @@
#include <v8.h>
#include <node.h>
#include <node_api.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <string.h>

#ifdef WIN32
#include <io.h>
#else
#include <unistd.h>
#endif

using namespace v8;

Handle<Value> InvokeMethod(const Arguments& args) {
HandleScope scope;
int oldfd = 0, newfd = 0;
int ret = 0;
if(!(args[0]->IsNumber() && args[1]->IsNumber())){
ThrowException(Exception::TypeError(String::New("FileDescriptor must be number")));
}else{
oldfd = args[0]->Uint32Value();
newfd = args[1]->Uint32Value();
/**
* Changes a old file descriptor to a new one (rather duplicates it)
*/
napi_value change (napi_env env, napi_callback_info info) {
napi_status status = napi_generic_failure;
napi_deferred deferred; // this will be either resolved or rejected and freed after
napi_value promise; // promise which will be returned
napi_value argv[2]; // arg value
napi_value ret; // refers to the number that was returnd by fcntl
napi_value This; // refers to the js this

napi_valuetype argType1;
napi_valuetype argType2;

uint32_t oldfd = 0;
uint32_t newfd = 0;
size_t argc = 2;

// create promise
status = napi_create_promise(env, &deferred, &promise);
assert(status == napi_ok);

// get args
status = napi_get_cb_info(env, info, &argc, argv, &This, nullptr);
assert(status == napi_ok);

// argc is an "in-out" argument of "napi_get_cb_info" you pass the
// expected arg count in and get the actual count so you can compare it
// against something
if (argc != 2) {
napi_value error;
napi_value nReason;
const char* reason = "The function needs two arguments";

status = napi_create_string_utf8(env, reason, 32, &nReason);
assert(status == napi_ok);

status = napi_create_error(env, nullptr, nReason, &error);
assert(status == napi_ok);

status = napi_reject_deferred(env, deferred, error);
assert(status == napi_ok);

deferred = NULL;

return promise;
}

// type checking
status = napi_typeof(env, argv[0], &argType1);
assert(status == napi_ok);

status = napi_typeof(env, argv[1], &argType2);
assert(status == napi_ok);

if (argType1 != napi_number || argType2 != napi_number) {
napi_value error;
napi_value nReason;
const char* reason = "The FileDescriptor must be a Number";

status = napi_create_string_utf8(env, reason, 35, &nReason);
assert(status == napi_ok);

status = napi_create_error(env, nullptr, nReason, &error);
assert(status == napi_ok);

status = napi_reject_deferred(env, deferred, error);
assert(status == napi_ok);

deferred = NULL;

return promise;
}
close(newfd);
ret = fcntl(oldfd, F_DUPFD, newfd);
return scope.Close(Number::New(ret));

// get the actual values and convert them to C types
status = napi_get_value_uint32(env, argv[0], &oldfd);
assert(status == napi_ok);

status = napi_get_value_uint32(env, argv[1], &newfd);
assert(status == napi_ok);

// all the duplicating magic
close(newfd); // close the new file descriptor first if its occupied

int retfd = dup2(oldfd, newfd);

// either resolve or reject with an error
if (newfd == (uint32_t)retfd) {
status = napi_create_uint32(env, retfd, &ret);
assert(status == napi_ok);

status = napi_resolve_deferred(env, deferred, ret);
assert(status == napi_ok);
} else if (retfd == -1 && errno) {
napi_value error;
napi_value nErrno;
napi_value nReason;
char* reason = strerror(errno);

status = napi_create_int32(env, (int32_t)errno, &nErrno);
assert(status == napi_ok);

status = napi_create_string_utf8(env, (const char*)reason, sizeof(reason), &nReason);
assert(status == napi_ok);

status = napi_create_error(env, nErrno, nReason, &error);
assert(status == napi_ok);

status = napi_reject_deferred(env, deferred, error);
assert(status == napi_ok);
}

// the deferred object was freed by "napi_resolve_deferred" or
// "napi_reject_deferred" so we can set it to NULL
deferred = NULL;

// return the promise to the js land so we can do async stuff with it
return promise;
}

void init(Handle<Object> exports) {
exports->Set(String::NewSymbol("invoke"),
FunctionTemplate::New(InvokeMethod)->GetFunction());
// helper to create a function on the exports value object
#define DECLARE_NAPI_METHOD(name, func) \
{ name, 0, func, 0, 0, 0, napi_default, 0 }

/**
* Initializes the module and sets up the change property on the exports object
*/
napi_value init (napi_env env, napi_value exports) {
napi_status status;

napi_property_descriptor changeDescriptor = DECLARE_NAPI_METHOD("change", change);

status = napi_define_properties(env, exports, 1, &changeDescriptor);
assert(status == napi_ok);

return exports;

}

NODE_MODULE(dup2, init)
// NODE_GYP_MODULE_NAME refers to the name of the addon in the binding.gyp file
NAPI_MODULE(NODE_GYP_MODULE_NAME, init)
2 changes: 1 addition & 1 deletion dup2.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = require("./build/Release/dup2");
module.exports = require('bindings')("dup2");
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 15 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
{
"name": "node-dup2",
"version": "0.0.1",
"version": "1.0.1",
"description": "I achieve dup2 of C",
"main": "dup2.js",
"scripts": {
"test": "testdup2.js",
"test": "node testdup2.js",
"install": "node-gyp rebuild"
},
"keywords": [
"dup2", "stdout"
"dup2",
"stdout",
"napi"
],
"repository": {
"url": "https://github.com/solidco2/node-dup2"
},
"author": "Solid",
"contributors": [
{
"name": "Philipp 'luii' Czarnetzki",
"url": "https://github.com/luii"
}
],
"license": "ISC",
"gypfile": true
"gypfile": true,
"dependencies": {
"bindings": "^1.3.0"
}
}
25 changes: 20 additions & 5 deletions testdup2.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
#!/usr/bin/env node
var dup2 = require("./build/Release/dup2");
var fs = require("fs");
var fd = fs.openSync("test.log", "a");
dup2.invoke(fd, 1);
console.log("hello, file");

let fs = require('fs');
let dup2 = require('bindings')('dup2');

async function test () {

let fd = fs.openSync('test.log', 'a');
let newfd;

try {
newfd = await dup2.change(fd, 2);
} catch (error) {
throw error;
}

console.log('hello, file');
console.log('New FileDescriptor:', newfd);
}

test()