Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
  • 11 commits
  • 20 files changed
  • 0 commit comments
  • 1 contributor
View
1  builtin/all.js
@@ -8,3 +8,4 @@ include('builtin/coffee-script.js');
include('builtin/include.js');
include('builtin/require.js');
include('builtin/xhr.js');
+
View
22 builtin/interpreter.js
@@ -0,0 +1,22 @@
+var ReadLine = require('ReadLine'),
+ console = require('console');
+
+function main() {
+ var stdin = new ReadLine('silkjs');
+ stdin.prompt('SilkJS> ');
+ while (1) {
+ try {
+ var line = stdin.gets();
+ console.log(eval(line));
+ }
+ catch (e) {
+ if (e === 'SIGQUIT') {
+ break;
+ }
+ else if (e === 'SIGTERM') {
+ break;
+ }
+ console.dir(e);
+ }
+ }
+}
View
387 examples/GitHub.js
@@ -0,0 +1,387 @@
+#!/usr/local/bin/silkjs
+
+var GitHub = require('GitHub').GitHub,
+ ReadLine = require('ReadLine'),
+ process = require('builtin/process'),
+ fs = require('fs'),
+ console = require('console');
+
+var gh,
+ stdin,
+ alive = true;
+
+function defaultPrompt() {
+ stdin.prompt('GitHub:' + gh.user.login + '> ');
+}
+
+var commands = {
+ user: {
+ help: 'user [username] - get information about user.',
+ fn: function(username) {
+ console.dir(gh.getUser(username));
+ }
+ },
+ editUser: {
+ help: 'editUser - edit your user information.',
+ fn: function(username) {
+ var o = {};
+ console.log('Enter your information below. Hit ^C to abort.');
+ try {
+ ['name', 'email', 'blog', 'company', 'location', 'hireable', 'bio'].each(function(s) {
+ stdin.prompt(s + ' (' + gh.user[s] + '): ');
+ var inp = stdin.gets(false);
+ if (inp === '') {
+ inp = gh.user[s];
+ }
+ else if (!inp) {
+ alive = false;
+ return false;
+ }
+ o[s] = inp;
+ });
+ }
+ catch (e) {
+ defaultPrompt();
+ console.log('*** Aborted');
+ return;
+ }
+ defaultPrompt();
+ gh.editUser(o);
+ }
+ },
+ listEmails: {
+ help: 'listEmails - list your email addresses.',
+ fn: function() {
+ console.dir(gh.getEmails());
+ }
+ },
+ addEmails: {
+ help: 'addEmail email_address[ email_address...] - add email_addresses to your list of email addresses.',
+ fn: function(args) {
+ var addresses = args.split(/\s+/);
+ console.dir(gh.addEmails(addresses));
+ }
+ },
+ deleteEmails: {
+ help: 'deleteEmail email_address[ email_address...] - delete email_addresses from your list of email addresses.',
+ fn: function(args) {
+ var addresses = args.split(/\s+/);
+ console.dir(gh.deleteEmails(addresses));
+ }
+ },
+ listFollowers: {
+ help: 'listFollowers [username] - list followers for user.',
+ fn: function(args) {
+ console.dir(gh.listFollowers(args));
+ }
+ },
+ listFollowing: {
+ help: 'listFollowing [username] - list who a user is following.',
+ fn: function(args) {
+ console.dir(gh.listFollowing(args));
+ }
+ },
+ amFollowing: {
+ help: 'amFollowing username - check if you are following a user',
+ fn: function(args) {
+ if (!args || !args.length) {
+ console.log('*** command error - you must supply a username');
+ return;
+ }
+ console.dir(gh.amFollowing(args));
+ }
+ },
+ follow: {
+ help: 'follow username - follow a user',
+ fn: function(args) {
+ if (!args || !args.length) {
+ console.log('*** command error - you must supply a username');
+ return;
+ }
+ console.dir(gh.follow(args));
+ }
+ },
+ unfollow: {
+ help: 'unfollow username - unfollow a user',
+ fn: function(args) {
+ if (!args || !args.length) {
+ console.log('*** command error - you must supply a username');
+ return;
+ }
+ console.dir(gh.unfollow(args));
+ }
+ },
+ listKeys: {
+ help: 'listKeys - list public keys.',
+ fn: function() {
+ console.dir(gh.listKeys());
+ }
+ },
+ getKey: {
+ help: 'getKey id [filename] - get public key by id [to file]',
+ fn: function(args) {
+ var parts = args.split(/\s+/);
+ var id = parts.shift();
+ var fn = false;
+ if (parts.length) {
+ fn = parts.join(' ');
+ }
+ var key = gh.getKey(id);
+ console.dir(key);
+ if (fn) {
+ fs.writeFile(fn, key.key);
+ console.log('>>> written to ' + fn);
+ }
+ }
+ },
+ createKey: {
+ help: 'createKey name filename - create a key from file containing RSA/DSA key.',
+ fn: function(args) {
+ var parts = args.split(/\s+/);
+ var name = parts.shift();
+ var filename = parts.join(' ');
+ var key = fs.readFile(filename);
+ if (!key) {
+ console.log('*** error: ' + filename + ' not found');
+ return;
+ }
+ console.dir(gh.createKey(name, key));
+ }
+ },
+ deleteKey: {
+ help: 'deleteKey id - delete key identifeid by id.',
+ fn: function(args) {
+ console.dir(gh.deleteKey(args));
+ }
+ },
+ listRepos: {
+ help: 'listRepos [user [type]] - list [a user\'s] repositories of [type].\n\ttype may be "all", "public", "member", or "all" (default),',
+ fn: function(args) {
+ var parts = args.split(/\s+/);
+ var name = parts.shift();
+ var type = parts.join(' ');
+ name = name || gh.username;
+ gh.listRepos(name, type).each(function(repo) {
+ console.log(new Date(repo.updated_at).toString('MM/dd/yyyy HH:mm:ss') + ' ' + name + '/' + repo.name);
+ });
+ }
+ },
+ listOrgRepos: {
+ help: 'listOrgRepos org - list an organization\'s repos; you must be a member of the organization.',
+ fn: function(args) {
+ gh.listOrgRepos(args).each(function(repo) {
+ console.log(new Date(repo.updated_at).toString('MM/dd/yyyy HH:mm:ss') + ' ' + args + '/' + repo.name);
+ });
+ }
+ },
+ createRepo: {
+ help: 'createRepo name - create a repository',
+ fn: function(args) {
+ var o = {
+ name: args,
+ description: '',
+ homepage: '',
+ private: false,
+ has_issues: true,
+ has_wiki: true,
+ has_downloads: true
+ };
+ console.log('Enter repository information below. Hit ^C to abort.');
+ try {
+ ['description', 'homepage', 'private', 'has_issues', 'has_wiki', 'has_downloads'].each(function (s) {
+ stdin.prompt(s + ' ('+ o[s] + '): ');
+ var inp = stdin.gets(false);
+ if (inp === '') {
+ inp = o[s];
+ }
+ else if (!inp) {
+ alive = false;
+ return false;
+ }
+ o[s] = inp;
+ });
+ }
+ catch (e) {
+ defaultPrompt();
+ console.log('*** Aborted');
+ return;
+ }
+ defaultPrompt();
+ console.dir(gh.createRepository(o));
+ }
+ },
+ repo: {
+ help: 'repo repository - get info about specified repository',
+ fn: function(args) {
+ console.dir(gh.getRepository(args));
+ console.log('status: ' + gh.status);
+ }
+ },
+ editRepo: {
+ help: 'editRepo name - edit a repository',
+ fn: function (args) {
+ var fields = ['description', 'homepage', 'private', 'has_issues', 'has_wiki', 'has_downloads'];
+ var repo = gh.getRepository(args);
+ if (gh.status == 404) {
+ console.log('*** Repository ' + args + ' not found');
+ return;
+ }
+ var o = {};
+ fields.each(function(field) {
+ o[field] = repo[field];
+ });
+ o.name = args;
+ console.log('Enter repository information below. Hit ^C to abort.');
+ try {
+ fields.each(function (s) {
+ stdin.prompt(s + ' (' + o[s] + '): ');
+ var inp = stdin.gets(false);
+ if (inp === '') {
+ inp = o[s];
+ }
+ else if (!inp) {
+ alive = false;
+ return false;
+ }
+ o[s] = inp;
+ });
+ }
+ catch (e) {
+ defaultPrompt();
+ console.log('*** Aborted');
+ return;
+ }
+ defaultPrompt();
+ console.dir(gh.editRepository(args, o));
+ }
+ },
+ listContributors: {
+ help: 'listContributors [user/]repo - list a repository\'s contributors.',
+ fn: function(args) {
+ console.dir(gh.listContributors(args));
+ }
+ },
+ listLanguages: {
+ help: 'listLanguages [user/]repo - list a repository\'s languages.',
+ fn: function(args) {
+ console.dir(gh.listLanguages(args));
+ }
+ },
+ listTeams: {
+ help: 'listTeams [user/]repo - list a repository\'s teams.',
+ fn: function(args) {
+ console.dir(gh.listTeams(args));
+ }
+ },
+ listTags: {
+ help: 'listTags [user/]repo - list a repository\'s tags.',
+ fn: function(args) {
+ console.dir(gh.listTags(args));
+ }
+ },
+ listBranches: {
+ help: 'listBranches[user/]repo - list a repository\'s branches.',
+ fn: function(args) {
+ console.dir(gh.listBranches(args));
+ }
+ },
+ listCollaborators: {
+ help: 'listCollaborators user/repo - list a repository\'s collaborators.',
+ fn: function(args) {
+ console.dir(gh.listCollaborators(args));
+ }
+ },
+ cd: {
+ help: 'cd path - change working directory to path.',
+ fn: function(args) {
+ if (fs.chdir(args)) {
+ console.log('*** error: ' + fs.error());
+ }
+ else {
+ console.log('local directroy now ' + fs.getcwd());
+ }
+ }
+ },
+ '!': {
+ help: '!cmd - run cmd as a shell command.',
+ fn: function(cmd) {
+ console.log(process.exec(cmd));
+ }
+ },
+ quit: {
+ help: 'quit - exit the program.',
+ fn: function() {
+ alive = false;
+ }
+ },
+ help: {
+ help: 'help - show this list.',
+ fn: function() {
+ commands.each(function(cmd) {
+ console.log(' ' + cmd.help);
+ });
+ }
+ }
+}
+
+function main(username, password) {
+ if (!username) {
+ console.log('Usage: GitHub.js username [password]');
+ return;
+ }
+ if (!password) {
+ password = console.getPassword('GitHub password for ' + username + ': ');
+ }
+
+ gh = new GitHub(username, password);
+ stdin = new ReadLine('GitHub', 50);
+ defaultPrompt();
+ var line;
+ while (alive) {
+ try {
+ line = stdin.gets();
+ }
+ catch (e) {
+ if (e == 'SIGQUIT') {
+ continue;
+ }
+ else if (e == 'SIGTERM') {
+ break;
+ }
+ console.log('');
+ continue;
+ }
+ if (line === false) {
+ break;
+ }
+ line = line.trim();
+ if (!line.length) {
+ continue;
+ }
+ if (line[0] === '!') {
+ console.log(process.exec(line.substr(1).trim()));
+ continue;
+ }
+ var parts = line.split(/\s+/);
+ var cmd = parts.shift();
+ var args = parts.join(' ');
+ if (commands[cmd]) {
+ try {
+ commands[cmd].fn(args);
+ }
+ catch (e) {
+ console.log('Exception');
+ console.log(e);
+ }
+ }
+ else {
+ console.log('*** Invalid command. Type help for command list.');
+ }
+ }
+ console.log('');
+
+// console.dir(gh.status);
+// console.dir(gh.user);
+// console.dir(gh.deleteEmails('mykesx@gmail.com'));
+// console.dir(gh.getEmails());
+}
View
1  httpd/response.js
@@ -50,6 +50,7 @@ res = function() {
contentLength: 0,
contentType: 'text/html',
headers: {},
+ data: {},
init: function(sock, keepAlive, requestsHandled) {
buffer.reset(buf);
View
767 modules/GitHub.js
@@ -0,0 +1,767 @@
+/** @ignore */
+
+var cURL = require('cURL'),
+ Json = require('Json'),
+ console = require('console');
+
+/**
+ * @constructor GitHub
+ *
+ * ### Synopsis
+ *
+ * var gh = new GitHub(username, password);
+ *
+ * Create a new GitHub connection.
+ *
+ * ### Details
+ *
+ * Once connected, you may access the methods and member variables of the gh instance/object. The API maintains the following member variables:
+ *
+ * + {string} gh.username - username of the authenticated user.
+ * + {string} gh.password - password of the authenticated user.
+ * + {object} gh.user - current information about the authenticated user.
+ * + {int} gh.status - HTTP status code of the last transaction with GitHub API.
+ *
+ * @param {string} username - GitHub username for authentication.
+ * @param {string} password - GitHub password for authentication.
+ *
+ * ### Notes
+ *
+ * If the authentication fails or some other error is reported by GitHub, the error message is thrown.
+ */
+function GitHub(username, password) {
+ this.username = username;
+ this.password = password;
+ this.url = 'https://' + username + ':' + password + '@api.github.com';
+ var response = cURL({
+ url: this.url + '/users/'+username
+ });
+ var result = Json.decode(response.responseText);
+
+ if (response.status !== 200) {
+ throw result.message;
+ }
+ this.status = response.status;
+ this.user = result;
+}
+
+GitHub.prototype.extend({
+ /**
+ * @function GitHub.getUser
+ *
+ * ### Synopsis
+ *
+ * var user = gh.getUser();
+ * var user = gh.getUser(username);
+ *
+ * Get information about a GitHub user, or the currently authenticated user.
+ *
+ * @param {string} username - GitHub user to get information for.
+ * @return {object} user - information about the user.
+ *
+ */
+ getUser: function(username) {
+ username = username || this.username;
+ var response = cURL({
+ url: this.url + '/users/'+username
+ });
+ this.status = response.status;
+ var user = Json.decode(response.responseText);
+ if (username === this.username && response.status === 200) {
+ this.user = user;
+ }
+ return user;
+ },
+
+ /**
+ * @function GitHub.editUser
+ *
+ * ### Synopsis
+ *
+ * var result = gh.editUser(userInfo);
+ *
+ * Update the authenticated user.
+ *
+ * The userInfo object has the following form:
+ *
+ * + {string} name - optional
+ * + {string} email - optional
+ * + {string} blog - optional
+ * + {string} company - optional
+ * + {string} location - optional
+ * + {boolean} hireable - optional
+ * + {string} bio - optional
+ *
+ * @param {object} userInfo - Object containing information to update for the authenticated user.
+ * @return {object} result - Object containing the information about the authenticated user.
+ */
+ editUser: function(o) {
+ var response = cURL({
+ method: 'PATCH',
+ url: this.url + '/user',
+ params: Json.encode(o)
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @function GitHub.getEmails
+ *
+ * ### Synopsis
+ *
+ * var emails = gh.getEmails();
+ *
+ * Get email addresses for the authenticated user.
+ *
+ * @return {array} emails - array of email addresses for the user.
+ */
+ getEmails: function() {
+ var response = cURL({
+ url: this.url + '/user/emails'
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @function GitHub.addEmails
+ *
+ * ### Synopsis
+ *
+ * var emails = gh.addEmails(newEmails);
+ *
+ * Add email address(es).
+ *
+ * @param {array} newEmails - array of email addresses to add.
+ * @returrn {array} emails - array of emails for user, after the additions.
+ */
+ addEmails: function(emails) {
+ var response = cURL({
+ method: 'POST',
+ url: this.url + '/user/emails',
+ params: Json.encode(emails)
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @function GitHub.deleteEmails
+ *
+ * ### Synopsis
+ *
+ * gh.DeleteEmails(newEmails);
+ *
+ * Delete email address(es).
+ *
+ * @param {array} newEmails - array of email addresses to delete.
+ *
+ * ### Note
+ *
+ * This method will throw a descriptive error {object} if GitHub reports an error.
+ */
+ deleteEmails: function(emails) {
+ var response = cURL({
+ method: 'DELETE',
+ url: this.url + '/user/emails',
+ params: Json.encode(emails)
+ });
+ this.status = response.status;
+ if (this.status != 204) {
+ throw Json.decode(response.responseText);
+ }
+ },
+
+ /**
+ * @function GitHub.listFollowers
+ *
+ * ### Synopsis
+ *
+ * var followers = gh.listFollowers();
+ * var followers = gh.listFollowers(username);
+ *
+ * List followers of the specified GitHub user, or the currently authenticated user.
+ *
+ * @param {string} username - GitHub username to get followers for.
+ * @return {array} followers - array of objects describing each user that is following the specified user.
+ */
+ listFollowers: function(username) {
+ username = username || this.username;
+ var response = cURL({
+ url: this.url + '/users/'+username+'/followers'
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @function GitHub.listFollowing
+ *
+ * ### Synopsis
+ *
+ * var following = gh.listFollowing();
+ * var following = gh.listFollowing(username);
+ *
+ * List users that the specified GitHub user, or the currently authenticated user, is following.
+ *
+ * @param {string} username - GitHub username to get followers for.
+ * @return {array} following - array of objects describing each user that is being following by the specified user.
+ */
+ listFollowing: function(username) {
+ username = username || this.username;
+ var response = cURL({
+ url: this.url + '/users/'+username+'/following'
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @function GitHub.amFollowing
+ *
+ * ### Synopsis
+ *
+ * var amFollowing = gh.amFollowing(username);
+ *
+ * Check if authenticated user is following another user.
+ *
+ * @param {string} username - GitHub username to check if following.
+ * @return {boolean} amFollowing - true if authenticated user is following the specfied user.
+ */
+ amFollowing: function(username) {
+ username = username || this.username;
+ var response = cURL({
+ url: this.url + '/user/following/'+username
+ });
+ this.status = response.status;
+ return this.status === 204;
+ },
+
+ /**
+ * @function GitHub.follow
+ *
+ * ### Synopsis
+ *
+ * var success = gh.follow(username);
+ *
+ * Follow a user.
+ *
+ * @param {string} username - GitHub username to follow.
+ * @return {boolean} success - true if authenticaed user is following the specified user.
+ *
+ */
+ follow: function(username) {
+ var response = cURL({
+ method: 'PUT',
+ url: this.url + '/user/following/'+username,
+ params: ' ' // hack - forces a content-length: 1 header, required by github api
+ });
+ this.status = response.status;
+ console.log(this.status);
+ console.dir(response.responseText);
+ return this.status == 204;
+ },
+
+ /**
+ * @function GitHub.unfollow
+ *
+ * ### Synopsis
+ *
+ * var success = gh.unfollow(username);
+ *
+ * Stop following a user.
+ *
+ * @param {string} username - GitHub username to stop following.
+ * @return {boolean} success - true if authenticaed user is no longer following the specified user.
+ *
+ */
+ unfollow: function(username) {
+ var response = cURL({
+ method: 'DELETE',
+ url: this.url + '/user/following/'+username
+ });
+ this.status = response.status;
+ return this.status == 204;
+ },
+
+ /**
+ * @function GitHub.listKeys
+ *
+ * ### Synopsis
+ *
+ * var keys = gh.listKeys();
+ *
+ * List public keys for the authenticated user.
+ *
+ * @return {array} keys - array of objects describing each of the publich keys for the authenticated user.
+ */
+ listKeys: function() {
+ var response = cURL({
+ url: this.url + '/user/keys'
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @function GitHub.getKey
+ *
+ * ### Synopsis
+ *
+ * var key = gh.getKey(id);
+ *
+ * Get a single public key for the authenticated user.
+ *
+ * @param {string} id - id of the key to retrieve.
+ *
+ * ### Notes
+ *
+ * The GitHub.listKeys method returns an array of objects, one per key. Each object contains the id of the key.
+ */
+ getKey: function(id) {
+ var response = cURL({
+ url: this.url + '/user/keys/'+id
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @function GitHub.createKey
+ *
+ * ### Synopsis
+ *
+ * var response = gh.createKey(title, key);
+ *
+ * Create a public key.
+ *
+ * @param {string} title - The title for the key.
+ * @param {string} key - The text of the RSA/DSA/etc. key to add.
+ * @return {object} response - The key that was added, or an object with descriptive information about why the key could not be added.
+ *
+ * ### Discussion
+ *
+ * The GitHub WWW site allows you to upload RSA/DSA/etc. keys to allow git:repoURI authentication. You will generate the key (file) on your workstation and upload it to GitHub using this method. The title is a string that identifies the key, e.g. "my workstation."
+ */
+ createKey: function(title, key) {
+ var response = cURL({
+ method: 'POST',
+ url: this.url + '/user/keys',
+ params: Json.encode({ title: title, key: key })
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @function gh.updateKey
+ *
+ * ### Synopsis
+ *
+ * var response = GitHub.updateKey(id, title, key);
+ *
+ * Update an existing public key.
+ *
+ * @param {string} id - id of the key to update.
+ * @param {string} title - The title for the key.
+ * @param {string} key - The text of the RSA/DSA/etc. key to add.
+ * @return {object} response - The key that was updated, or an object with descriptive information about why the key could not be updated.
+ */
+ updateKey: function(id, title, key) {
+ var response = cURL({
+ method: 'PATCH',
+ url: this.url + '/user/keys/'+id,
+ params: Json.encode({ title: title, key: key })
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @function gh.deleteKey
+ *
+ * ### Synopsis
+ *
+ * var success = GitHub.deleteKey(id);
+ *
+ * Delete a public key.
+ *
+ * @param {string} id - id of the key to delete.
+ * @returns {boolean} success - true if the key was deleted.
+ */
+ deleteKey: function(id) {
+ var response = cURL({
+ method: 'delete',
+ url: this.url + '/user/keys/'+id
+ });
+ this.status = response.status;
+ return this.status == 204;
+ },
+
+ /**
+ * @function GitHub.listRepos
+ *
+ * ### Synopsis
+ *
+ * var repos = gh.listRepos();
+ * var repos = gh.listRepos(user);
+ * var repos = gh.listRepos(user, type);
+ *
+ * List repositories for the specified user. The type variable may be "all", "owner", "member" or "public" (default).
+ *
+ * @param {string} user - username of user to get repositories for. If ommitted, it is the current authenticated user.
+ * @param {string} type - type of repositories to get.
+ * @returns {array} repos - array of repo objects.
+ */
+ listRepos: function(user, type) {
+ user = user || this.username;
+ var url = this.url + '/users/' + user + '/repos';
+ if (type) {
+ url += '?type='+type;
+ }
+ var response = cURL({
+ url: url
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @function GitHub.listOrgRepos
+ *
+ * ### Synopsis
+ *
+ * var repos = gh.listOrgRepos(org);
+ *
+ * List repositories for the specified organization.
+ *
+ * @param {string} org - name of organization
+ * @return {array} repos - array of repo objects.
+ */
+ listOrgRepos: function(org) {
+ var response = cURL({
+ url: this.url + '/orgs/' + org + '/repos'
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @funciton GitHub.createRepository
+ *
+ * ### Synopsis
+ *
+ * var repo = gh.createRepository(config);
+ * var repo = gh.createRepository(config, org);
+ *
+ * Create a new repository for the authenticated user. The second form creates a new repository in the specified organization; the authenticated user must be a member of that organization.
+ *
+ * The config object has the following members:
+ *
+ * + {string} name - required, name of repository.
+ * + {string} description - optional
+ * + {string} homepage - optional
+ * + {boolean} private - optional, true to create a private repository, false to create a public one. Private repositories require a paid GitHub account. Default is false.
+ * + {boolean} has_issues - optional, true to enable issues for the repository, false to diaable them. Default is true.
+ * + {boolean} has_wiki - optional, true to enable the wiki for this repository, false to disable it. Default is true.
+ * + {boolean} has_downloads - optional, true to enable downloads for this repository, false to disable them. Default is true.
+ * + {int} team_id - the id of the team that will be granted access to this repository. This is only valid when creating a repo in an organization.
+ *
+ * @param {object} config - object describing the attributes of the repository to be created. See notes above.
+ * @return {object} repo - object describing the created repository.
+ */
+ createRepository: function(config, org) {
+ var url = this.url + (org ? ('/orgs/' + org + '/repos') : '/user/repos');
+ console.dir(url);
+ var response = cURL({
+ method: 'POST',
+ url: url,
+ params: Json.encode(config)
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @function GitHub.getRepository
+ *
+ * ### Synopsis
+ *
+ * var info = gh.getRepository(repo);
+ *
+ * Get information about a repository.
+ *
+ * @param {string} repo - name of repository to get information about (e.g. mschwartz/SilkJS or SilkJS)
+ *
+ */
+ getRepository: function(name) {
+ var parts = name.split('/');
+ var user;
+ if (parts.length > 1) {
+ name = parts[1];
+ user = parts[0];
+ }
+ else {
+ user = this.username;
+ }
+ var response = cURL({
+ url: this.url + '/repos/' + user + '/' + name
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @function GitHub.editRepository
+ *
+ * ### Synopsis
+ *
+ * var repoInfo = gh.editRepository(name, config);
+ *
+ * Update a repository's settings.
+ *
+ * The config object has the following members:
+ *
+ * + {string} name - required, name of repository.
+ * + {string} description - optional
+ * + {string} homepage - optional
+ * + {boolean} private - optional, true to create a private repository, false to create a public one. Private repositories require a paid GitHub account. Default is false.
+ * + {boolean} has_issues - optional, true to enable issues for the repository, false to diaable them. Default is true.
+ * + {boolean} has_wiki - optional, true to enable the wiki for this repository, false to disable it. Default is true.
+ * + {boolean} has_downloads - optional, true to enable downloads for this repository, false to disable them. Default is true.
+ * + {int} team_id - the id of the team that will be granted access to this repository. This is only valid when creating a repo in an organization.
+ *
+ * @param {string} name - name of repository to update, e.g. mschwartz/SilkJS or just SilkJS
+ * @param {object} config - object describing the attributes of the repository to be updated. See notes above.
+ * @return {object} repo - object describing the created repository.
+ */
+ editRepository: function(name, config) {
+ var parts = name.split('/');
+ var user;
+ if (parts.length > 1) {
+ name = parts[1];
+ user = parts[0];
+ }
+ else {
+ user = this.username;
+ }
+
+ var url = this.url + '/repos/' + user + '/' + name;
+ var response = cURL({
+ method: 'PATCH',
+ url: url,
+ params: Json.encode(config)
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @function GitHub.listContributors
+ *
+ * ### Synopsis
+ *
+ * var contributors = gh.listcontributors(repo);
+ *
+ * Get list of contributors for a repository.
+ *
+ * @param {string} repo - name of repo, e.g. "mschwartz/SilkJS" or "SilkJS" if the authenticated user is "mschwartz"
+ * @return {array} contributors - array of objects describing each contributor.
+ */
+ listContributors: function(name) {
+ var parts = name.split('/');
+ var user;
+ if (parts.length > 1) {
+ name = parts[1];
+ user = parts[0];
+ }
+ else {
+ user = this.username;
+ }
+
+ var url = this.url + '/repos/' + user + '/' + name + '/contributors';
+ var response = cURL({
+ url: url
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @function GitHub.listLanguages
+ *
+ * ### Synopsis
+ *
+ * var languages = gh.listLanguages(repo);
+ *
+ * Get list of languages of a repository (programming languages).
+ *
+ * @param {string} repo - name of repo, e.g. "mschwartz/SilkJS" or "SilkJS" if the authenticated user is "mschwartz"
+ * @return {object} collaborators - object/hash; key is language (e.g. "C"), value is (lines of code?)
+ */
+ listLanguages: function(name) {
+ var parts = name.split('/');
+ var user;
+ if (parts.length > 1) {
+ name = parts[1];
+ user = parts[0];
+ }
+ else {
+ user = this.username;
+ }
+
+ var url = this.url + '/repos/' + user + '/' + name + '/languages';
+ var response = cURL({
+ url: url
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @function GitHub.listTeams
+ *
+ * ### Synopsis
+ *
+ * var teams = gh.listTeams(repo);
+ *
+ * Get list of teams for a repository.
+ *
+ * @param {string} repo - name of repo, e.g. "mschwartz/SilkJS" or "SilkJS" if the authenticated user is "mschwartz"
+ * @return {array} collaborators - array of objects describing each team.
+ */
+ listTeams: function (name) {
+ var parts = name.split('/');
+ var user;
+ if (parts.length > 1) {
+ name = parts[1];
+ user = parts[0];
+ }
+ else {
+ user = this.username;
+ }
+
+ var url = this.url + '/repos/' + user + '/' + name + '/teams';
+ var response = cURL({
+ url: url
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @function GitHub.listTags
+ *
+ * ### Synopsis
+ *
+ * var tags = gh.listTags(repo);
+ *
+ * Get list of tags for a repository.
+ *
+ * @param {string} repo - name of repo, e.g. "mschwartz/SilkJS" or "SilkJS" if the authenticated user is "mschwartz"
+ * @return {array} tags - array of objects describing each tag.
+ */
+ listTags: function (name) {
+ var parts = name.split('/');
+ var user;
+ if (parts.length > 1) {
+ name = parts[1];
+ user = parts[0];
+ }
+ else {
+ user = this.username;
+ }
+
+ var url = this.url + '/repos/' + user + '/' + name + '/tags';
+ var response = cURL({
+ url: url
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @function GitHub.listBranches
+ *
+ * ### Synopsis
+ *
+ * var branches = gh.listBranches(repo);
+ *
+ * Get list of branches for a repository.
+ *
+ * @param {string} repo - name of repo, e.g. "mschwartz/SilkJS" or "SilkJS" if the authenticated user is "mschwartz"
+ * @return {array} tags - array of objects describing each branch.
+ */
+ listBranches: function (name) {
+ var parts = name.split('/');
+ var user;
+ if (parts.length > 1) {
+ name = parts[1];
+ user = parts[0];
+ }
+ else {
+ user = this.username;
+ }
+
+ var url = this.url + '/repos/' + user + '/' + name + '/branches';
+ var response = cURL({
+ url: url
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ /**
+ * @function GitHub.listCollaborators
+ *
+ * ### Synopsis
+ *
+ * var collaborators = gh.listCollaborators(repo);
+ *
+ * Get list of collaborators for a repository.
+ *
+ * @param {string} repo - name of repo, e.g. "mschwartz/SilkJS" or "SilkJS" if the authenticated user is "mschwartz"
+ * @return {array} collaborators - array of objects describing each collaborator.
+ */
+ listCollaborators: function (name) {
+ var parts = name.split('/');
+ var user;
+ if (parts.length > 1) {
+ name = parts[1];
+ user = parts[0];
+ }
+ else {
+ user = this.username;
+ }
+
+ var url = this.url + '/repos/' + user + '/' + name + '/collaborators';
+ var response = cURL({
+ url: url
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ },
+
+ isCollaborator: function(repo, oUser) {
+ var parts = repo.split('/');
+ var user;
+ if (parts.length > 1) {
+ repo = parts[1];
+ user = parts[0];
+ }
+ else {
+ user = this.username;
+ }
+
+ var url = this.url + '/repos/' + user + '/' + repo + '/collaborators/' + oUser;
+ var response = cURL({
+ url: url
+ });
+ this.status = response.status;
+ return Json.decode(response.responseText);
+ }
+
+});
+
+exports.GitHub = GitHub;
View
57 modules/ReadLine.js
@@ -18,7 +18,8 @@
* ```
*/
-var editline = require('builtin/editline');
+var editline = require('builtin/editline'),
+ process = require('builtin/process');
/**
* @constructor ReadLine
@@ -26,13 +27,22 @@ var editline = require('builtin/editline');
* ### Synopsis:
*
* var stdin = new ReadLine(program_name);
+ * var stdin = new ReadLine(program_name, historySize);
+ *
+ * Create a line editor instance. A history file is written to $HOME/.program_namerc.
+ *
*
* @param {string} program_name - name of program (see man editline).
+ * @param {int} historySize - number of lines of history to keep around. Defaults to 50.
*/
-function ReadLine(prog) {
+function ReadLine(prog, historySize) {
prog = prog || 'SilkJS';
- this.handle = editline.init(prog);
- editline.prompt(this.handle, '> ');
+ this.prog = prog;
+ this.historyFile = process.env().HOME + '/' + '.'+prog+'rc';
+ editline.loadHistory(this.historyFile);
+ this.promptString = '> ';
+// this.handle = editline.init(prog, historySize || 50);
+// editline.prompt(this.handle, '> ');
}
ReadLine.prototype.extend({
/**
@@ -48,7 +58,8 @@ ReadLine.prototype.extend({
*
*/
prompt: function(txt) {
- editline.prompt(this.handle, txt);
+ this.promptString = txt;
+// editline.prompt(this.handle, txt);
},
/**
@@ -56,14 +67,40 @@ ReadLine.prototype.extend({
*
* ### Synopsis
*
- * var line = stdin.gets();
+ * var line = stdin.gets(addHistory);
*
* Read a line from stdin with prompt.
- *
+ *
+ * @param {boolean} addHistory - set to false to not add input to history.
* @return {string} line - text that user entered, or false if EOF (e.g. user hit ^D).
*/
- gets: function() {
- return editline.gets(this.handle);
+ gets: function(addHistory) {
+ if (addHistory === undefined || addHistory === true) {
+ addHistory = true;
+ }
+ else {
+ addHistory = false;
+ }
+ var ret = editline.gets(this.promptString, addHistory );
+ if (ret === -1) {
+ throw 'SIGINT';
+ }
+ else if (ret === -2) {
+ throw 'SIGQUIT';
+ }
+ else if (ret === -3) {
+ throw 'SIGHUP';
+ }
+ else if (ret === -4) {
+ throw 'SIGTERM';
+ }
+ if (ret === false) {
+ console.log('^D hit');
+ }
+ if (addHistory) {
+ editline.saveHistory(this.historyFile);
+ }
+ return ret;
},
/**
@@ -76,7 +113,7 @@ ReadLine.prototype.extend({
* Close ReadLine instance and free any resources used.
*/
close: function() {
- editline.end(this.handle);
+// editline.end(this.handle);
}
});
View
19 modules/cURL.js
@@ -41,7 +41,8 @@ function isString(v) {
* + followRedirects (optional) true (default) to follow redirect responses from the server
* + cookies: (optional) Object, Array, or String representation of cookie header to send
* + headers: (optional) Object, Array, or String representation of HTTP headers to add to the request.
- * + params: (valid for POST only): POST variables to send to server
+ * + params: POST variables to send to server
+ * + verbose: set to > 0 to have cURL library log debugging info to console
*
* The result returned is an object of the form:
*
@@ -70,7 +71,9 @@ function cURL(config) {
// c.setMethod(handle, config.method);
break;
default:
- error('GET/POST are only allowed methods.');
+ method = config.method.toUpperCase();
+ c.setMethod(handle, method);
+ break;
}
}
if (config.followRedirects) {
@@ -112,12 +115,8 @@ function cURL(config) {
else {
error('Invalid params config');
}
- switch (method) {
- case 'POST':
- c.setPostFields(handle, paramString);
- break;
- default:
- error('The params config is only valid for POST');
+ if (method !== 'GET') {
+ c.setPostFields(handle, paramString);
}
}
@@ -142,8 +141,8 @@ function cURL(config) {
error('Invalid headers config');
}
}
-
- var errorCode = c.perform(handle);
+
+ var errorCode = c.perform(handle, config.verbose || 0);
if (errorCode) {
error(c.error(errorCode));
}
View
1  modules/console.js
@@ -88,6 +88,7 @@
* @returns {string} password - the password entered by the user
*/
getPassword: function(prompt) {
+ prompt = prompt || '';
return console.getPassword(prompt);
}
View
2  src/Makefile
@@ -1,6 +1,6 @@
ARCH := $(shell getconf LONG_BIT)
-CORE= main.o base64.o global.o console.o process.o net.o fs.o buffer.o v8.o http.o md5.o popen.o
+CORE= main.o base64.o global.o console.o process.o net.o fs.o buffer.o v8.o http.o md5.o popen.o linenoise.o
OBJ= mysql.o memcached.o gd.o ncurses.o sem.o logfile.o sqlite3.o xhrhelper.o curl.o ssh2.o sftp.o ftp.o ftplib.o editline.o
View
2  src/Makefile.osx
@@ -1,4 +1,4 @@
-OBJ= main.o base64.o global.o console.o process.o net.o fs.o buffer.o mysql.o http.o gd.o ncurses.o sem.o logfile.o v8.o md5.o sqlite3.o xhrhelper.o curl.o ssh2.o sftp.o memcached.o ftplib.o ftp.o editline.o popen.o
+OBJ= main.o base64.o global.o console.o process.o net.o fs.o buffer.o mysql.o http.o gd.o ncurses.o sem.o logfile.o v8.o md5.o sqlite3.o xhrhelper.o curl.o ssh2.o sftp.o memcached.o ftplib.o ftp.o editline.o popen.o linenoise.o
CFLAGS = -O6 -fomit-frame-pointer -fdata-sections -ffunction-sections -fno-strict-aliasing -fno-rtti -fno-exceptions -fvisibility=hidden -Wall -W -Wno-unused-parameter -Wnon-virtual-dtor -m64 -O3 -fomit-frame-pointer -fdata-sections -ffunction-sections -ansi -fno-strict-aliasing
View
17 src/curl.cpp
@@ -116,9 +116,12 @@ static JSVAL setMethod(JSARGS args) {
if (!strcasecmp(*method, "post")) {
return scope.Close(Integer::New(curl_easy_setopt(h->curl, CURLOPT_POST, 1)));
}
- else {
+ else if (!strcasecmp(*method, "get")) {
return scope.Close(Integer::New(curl_easy_setopt(h->curl, CURLOPT_HTTPGET, 1)));
}
+ else {
+ return scope.Close(Integer::New(curl_easy_setopt(h->curl, CURLOPT_CUSTOMREQUEST, *method)));
+ }
}
/**
@@ -225,12 +228,14 @@ static JSVAL setPostFields(JSARGS args) {
* ###Synopsis
*
* var status = curl.perform(handle);
+ * var status = curl.perform(handle, verbose);
*
* Perform the CURL request.
*
* This function is called after the curl.init() and all the method calls to set options are made, and will perform the transfer as described in the options. It must be called with the same handle as input as the curl.init() call returned.
*
* @param {object} handle - CURL handle
+ * @param {int} verbose - set to > 0 to have cURL library print debugging info to console
* @return {int} status - 0 for success, otherwise an error code.
*/
static JSVAL perform(JSARGS args) {
@@ -239,7 +244,9 @@ static JSVAL perform(JSARGS args) {
if (h->slist) {
curl_easy_setopt(h->curl, CURLOPT_HTTPHEADER, h->slist);
}
-// curl_easy_setopt(h->curl, CURLOPT_VERBOSE, 1);
+ if (args.Length() > 1) {
+ curl_easy_setopt(h->curl, CURLOPT_VERBOSE, args[1]->IntegerValue());
+ }
return scope.Close(Integer::New(curl_easy_perform(h->curl)));
}
@@ -280,6 +287,9 @@ static JSVAL getContentType(JSARGS args) {
CHANDLE *h = HANDLE(args[0]);
char *contentType;
curl_easy_getinfo(h->curl, CURLINFO_CONTENT_TYPE, &contentType);
+ if (!contentType) {
+ contentType = "";
+ }
return scope.Close(String::New(contentType));
}
@@ -298,6 +308,9 @@ static JSVAL getContentType(JSARGS args) {
static JSVAL getResponseText(JSARGS args) {
HandleScope scope;
CHANDLE *h = HANDLE(args[0]);
+ if (!h->size) {
+ return scope.Close(String::New("{}"));
+ }
return scope.Close(String::New(h->memory, h->size));
}
View
203 src/editline.cpp
@@ -1,11 +1,99 @@
+/**
+ * @module builtin/editline
+ *
+ * ### Synopsis
+ *
+ * var editline = require('builtin/editline');
+ *
+ * Interface to libedit (line editor/readline substitute)
+ */
#include "SilkJS.h"
+#include <setjmp.h>
+
+#define USE_LINENOISE
+#ifdef USE_LINENOISE
+// https://github.com/antirez/linenoise
+#include "linenoise.h"
+
+//static sig_atomic_t gotsig = 0;
+//static void sig(int i) {
+// printf("GOT SIGNAL %d\n", i);
+// signal(i, sig);
+// gotsig = i;
+//}
+
+static bool initialized = false;
+static JSVAL editline_gets(JSARGS args) {
+ HandleScope scope;
+ bool addHistory = true;
+ addHistory = args[1]->BooleanValue();
+// if (!initialized) {
+// printf("initializing\n");
+// initialized = true;
+// (void) signal(SIGINT, sig);
+// (void) signal(SIGQUIT, sig);
+// (void) signal(SIGHUP, sig);
+// (void) signal(SIGTERM, sig);
+// }
+ String::Utf8Value prompt(args[0]->ToString());
+ char *line = linenoise(*prompt);
+ if (line) {
+ if (addHistory) {
+ linenoiseHistoryAdd(line);
+ }
+ Local<Value> s = String::New(line);
+ delete [] line;
+ return scope.Close(s);
+ }
+ else {
+ if (errno == EAGAIN) {
+ return scope.Close(Integer::New(-4));
+ }
+ else if (errno == EPIPE) {
+ return scope.Close(Integer::New(-2));
+ }
+ return scope.Close(False());
+ }
+}
+
+static JSVAL editline_loadhistory(JSARGS args) {
+ HandleScope scope;
+ String::Utf8Value filename(args[0]->ToString());
+ linenoiseHistoryLoad(*filename);
+ return Undefined();
+}
+
+static JSVAL editline_savehistory(JSARGS args) {
+ HandleScope scope;
+ String::Utf8Value filename(args[0]->ToString());
+ linenoiseHistorySave(*filename);
+ return Undefined();
+}
+
+void init_editline_object() {
+ HandleScope scope;
+
+ JSOBJT editlineObject = ObjectTemplate::New();
+
+ editlineObject->Set(String::New("gets"), FunctionTemplate::New(editline_gets));
+ editlineObject->Set(String::New("loadHistory"), FunctionTemplate::New(editline_loadhistory));
+ editlineObject->Set(String::New("saveHistory"), FunctionTemplate::New(editline_savehistory));
+
+ builtinObject->Set(String::New("editline"), editlineObject);
+}
+
+#else
#include <histedit.h>
struct EHANDLE {
EditLine *el;
+ History *h;
char *prompt;
+ jmp_buf jmpbuf;
};
+static EHANDLE *currentHandle = NULL;
+
static inline EHANDLE *HANDLE(Handle<Value>v) {
if (v->IsNull()) {
ThrowException(String::New("Handle is NULL"));
@@ -20,37 +108,145 @@ static char *prompt(EditLine *el) {
return e->prompt;
}
+static sig_atomic_t gotsig = 0;
+
+static void sig(int i) {
+ signal(i, sig);
+ gotsig = i;
+ siglongjmp(currentHandle->jmpbuf, 1);
+}
+
+/**
+ * @function editline.init
+ *
+ * ### Synopsis
+ *
+ * var handle = editline.init(programName);
+ * var handle = editline.init(programName, historySize);
+ *
+ * Initialize editline. The programName is used to deal with .editrc. See man editline for details.
+ *
+ * @param (string) programName - name of program.
+ * @param (int) historySize - size of history (number of lines of history to keep), defaults to 50.
+ * @return {object} handle - opaque handle to use with other editline methods.
+ */
static JSVAL editline_init(JSARGS args) {
HandleScope scope;
String::Utf8Value prog(args[0]->ToString());
+ int historySize = 50;
+ if (args.Length() > 1) {
+ historySize = args[1]->IntegerValue();
+ }
EditLine *el = el_init(*prog, stdin, stdout, stderr);
EHANDLE *handle = new EHANDLE;
handle->el = el;
handle->prompt = strdup("> ");
+ el_set(el, EL_SIGNAL, 1);
el_set(el, EL_CLIENTDATA, handle);
el_set(el, EL_PROMPT, prompt);
+ el_set(el, EL_EDITOR, "vi");
+ handle->h = history_init();
+ HistEvent ev;
+ history(handle->h, &ev, H_SETSIZE, historySize);
+ history(handle->h, &ev, H_SETUNIQUE, 1);
+ el_set(el, EL_HIST, history, handle->h);
+ (void) signal(SIGINT, sig);
+ (void) signal(SIGQUIT, sig);
+ (void) signal(SIGHUP, sig);
+ (void) signal(SIGTERM, sig);
+
+ el_source(el, NULL);
return scope.Close(External::New(handle));
}
+/**
+ * @function editline.end
+ *
+ * ### Synopsis
+ *
+ * editline.end(handle);
+ *
+ * End editline session for the given handle, freeing any resources used.
+ *
+ * @param {object} handle - handle returned from editline.init();
+ */
static JSVAL editline_end(JSARGS args) {
HandleScope scope;
EHANDLE *e = HANDLE(args[0]);
+ history_end(e->h);
el_end(e->el);
delete [] e->prompt;
return Undefined();
}
+/**
+ * @function editline.gets
+ *
+ * ### Synopsis
+ *
+ * var line = editline.gets(handle);
+ * var line = editline.gets(handle, addHistory);
+ *
+ * Read a line from the console using libedit.
+ *
+ * @param {object} handle - handle returned from editline.init();
+ * @param {boolean} addHistory - true to add input line to history. Defaults to true.
+ * @return {string} line - line read from console or false on EOF or error.
+ */
static JSVAL editline_gets(JSARGS args) {
- HandleScope scope;
+// HandleScope scope;
EHANDLE *e = HANDLE(args[0]);
int count;
+
+ if (sigsetjmp(e->jmpbuf, 1) != 0) {
+ el_reset(e->el);
+ if (gotsig) {
+ int retval = 0;
+ switch (gotsig) {
+ case SIGINT:
+ retval = -1;
+ break;
+ case SIGQUIT:
+ retval = -2;
+ break;
+ case SIGHUP:
+ retval = -3;
+ break;
+ case SIGTERM:
+ retval = -4;
+ break;
+ }
+ gotsig = 0;
+ return Integer::New(retval);
+ }
+ else {
+ return False();
+ }
+ }
+ currentHandle = e;
const char *s = el_gets(e->el, &count);
if (!s) {
- return scope.Close(False());
+ return False();
}
- return scope.Close(String::New(s));
+ HistEvent ev;
+ history(e->h, &ev, H_ENTER, s);
+ return String::New(s);
}
+/**
+ * @function editline.prompt
+ *
+ * ### Synopsis
+ *
+ * editline.prompt(handle, prompt);
+ *
+ * Set the prompt for lines read with libedit.
+ *
+ * If you set the prompt to "foo> ", then the user will see "foo> " (without the quotes) and the cursor placed after that.
+ *
+ * @param {object} handle - handle returned from editline.init();
+ * @param {string) prompt - the prompt to set
+ */
static JSVAL editline_prompt(JSARGS args) {
HandleScope scope;
EHANDLE *e = HANDLE(args[0]);
@@ -74,3 +270,4 @@ void init_editline_object() {
builtinObject->Set(String::New("editline"), editlineObject);
}
+#endif
View
7 src/fs.cpp
@@ -731,6 +731,8 @@ static JSVAL fs_readfile(JSARGS args) {
if (fd == -1) {
return scope.Close(Null());
}
+ flock(fd, LOCK_SH);
+ lseek(fd, 0, 0);
std::string s;
// long size = lseek(fd, 0, 2); lseek(fd, 0, 0);
// printf("size = %ld\n", size);
@@ -742,6 +744,7 @@ static JSVAL fs_readfile(JSARGS args) {
// if (read(fd, buf, size) != size) {
// return scope.Close(Null());
// }
+ flock(fd, LOCK_UN);
close(fd);
Handle<String>ret = String::New(s.c_str(), s.size());
return scope.Close(ret);
@@ -772,6 +775,7 @@ static JSVAL fs_readfile64(JSARGS args) {
printf("%s\n%s\n", *path, strerror(errno));
return scope.Close(Null());
}
+ flock(fd, LOCK_SH);
long size = lseek(fd, 0, 2);
lseek(fd, 0, 0);
unsigned char buf[size];
@@ -831,10 +835,13 @@ static JSVAL fs_writefile(JSARGS args) {
if (fd == -1) {
return scope.Close(False());
}
+ flock(fd, LOCK_EX);
if (write(fd, *data, size) != size) {
+ flock(fd, LOCK_UN);
close(fd);
return scope.Close(False());
}
+ flock(fd, LOCK_UN);
close(fd);
return scope.Close(True());
}
View
1  src/ftplib.cpp
@@ -1,3 +1,4 @@
+/** @ignore */
/***************************************************************************/
/* */
/* ftplib.c - callable ftp access routines */
View
614 src/linenoise.cpp
@@ -0,0 +1,614 @@
+/* linenoise.c -- guerrilla line editing library against the idea that a
+ * line editing lib needs to be 20,000 lines of C code.
+ *
+ * You can find the latest source code at:
+ *
+ * http://github.com/antirez/linenoise
+ *
+ * Does a number of crazy assumptions that happen to be true in 99.9999% of
+ * the 2010 UNIX computers around.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * References:
+ * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+ * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
+ *
+ * Todo list:
+ * - Switch to gets() if $TERM is something we can't support.
+ * - Filter bogus Ctrl+<char> combinations.
+ * - Win32 support
+ *
+ * Bloat:
+ * - Completion?
+ * - History search like Ctrl+r in readline?
+ *
+ * List of escape sequences used by this program, we do everything just
+ * with three sequences. In order to be so cheap we may have some
+ * flickering effect with some slow terminal, but the lesser sequences
+ * the more compatible.
+ *
+ * CHA (Cursor Horizontal Absolute)
+ * Sequence: ESC [ n G
+ * Effect: moves cursor to column n
+ *
+ * EL (Erase Line)
+ * Sequence: ESC [ n K
+ * Effect: if n is 0 or missing, clear from cursor to end of line
+ * Effect: if n is 1, clear from beginning of line to cursor
+ * Effect: if n is 2, clear entire line
+ *
+ * CUF (CUrsor Forward)
+ * Sequence: ESC [ n C
+ * Effect: moves cursor forward of n chars
+ *
+ * The following are used to clear the screen: ESC [ H ESC [ 2 J
+ * This is actually composed of two sequences:
+ *
+ * cursorhome
+ * Sequence: ESC [ H
+ * Effect: moves the cursor to upper left corner
+ *
+ * ED2 (Clear entire screen)
+ * Sequence: ESC [ 2 J
+ * Effect: clear the whole screen
+ *
+ */
+
+#include <termios.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include "linenoise.h"
+
+#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
+#define LINENOISE_MAX_LINE 4096
+static const char *unsupported_term[] = {"dumb","cons25",NULL};
+static linenoiseCompletionCallback *completionCallback = NULL;
+
+static struct termios orig_termios; /* in order to restore at exit */
+static int rawmode = 0; /* for atexit() function to check if restore is needed*/
+static int atexit_registered = 0; /* register atexit just 1 time */
+static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
+static int history_len = 0;
+char **history = NULL;
+
+static void linenoiseAtExit(void);
+int linenoiseHistoryAdd(const char *line);
+
+static int isUnsupportedTerm(void) {
+ char *term = getenv("TERM");
+ int j;
+
+ if (term == NULL) return 0;
+ for (j = 0; unsupported_term[j]; j++)
+ if (!strcasecmp(term,unsupported_term[j])) return 1;
+ return 0;
+}
+
+static void freeHistory(void) {
+ if (history) {
+ int j;
+
+ for (j = 0; j < history_len; j++)
+ free(history[j]);
+ free(history);
+ }
+}
+
+static int enableRawMode(int fd) {
+ struct termios raw;
+
+ if (!isatty(STDIN_FILENO)) goto fatal;
+ if (!atexit_registered) {
+ atexit(linenoiseAtExit);
+ atexit_registered = 1;
+ }
+ if (tcgetattr(fd,&orig_termios) == -1) goto fatal;
+
+ raw = orig_termios; /* modify the original mode */
+ /* input modes: no break, no CR to NL, no parity check, no strip char,
+ * no start/stop output control. */
+ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+ /* output modes - disable post processing */
+ raw.c_oflag &= ~(OPOST);
+ /* control modes - set 8 bit chars */
+ raw.c_cflag |= (CS8);
+ /* local modes - choing off, canonical off, no extended functions,
+ * no signal chars (^Z,^C) */
+ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+ /* control chars - set return condition: min number of bytes and timer.
+ * We want read to return every single byte, without timeout. */
+ raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
+
+ /* put terminal in raw mode after flushing */
+ if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal;
+ rawmode = 1;
+ return 0;
+
+fatal:
+ errno = ENOTTY;
+ return -1;
+}
+
+static void disableRawMode(int fd) {
+ /* Don't even check the return value as it's too late. */
+ if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1)
+ rawmode = 0;
+}
+
+/* At exit we'll try to fix the terminal to the initial conditions. */
+static void linenoiseAtExit(void) {
+ disableRawMode(STDIN_FILENO);
+ freeHistory();
+}
+
+static int getColumns(void) {
+ struct winsize ws;
+
+ if (ioctl(1, TIOCGWINSZ, &ws) == -1) return 80;
+ return ws.ws_col;
+}
+
+static void refreshLine(int fd, const char *prompt, char *buf, size_t len, size_t pos, size_t cols) {
+ char seq[64];
+ size_t plen = strlen(prompt);
+
+ while((plen+pos) >= cols) {
+ buf++;
+ len--;
+ pos--;
+ }
+ while (plen+len > cols) {
+ len--;
+ }
+
+ /* Cursor to left edge */
+ snprintf(seq,64,"\x1b[0G");
+ if (write(fd,seq,strlen(seq)) == -1) return;
+ /* Write the prompt and the current buffer content */
+ if (write(fd,prompt,strlen(prompt)) == -1) return;
+ if (write(fd,buf,len) == -1) return;
+ /* Erase to right */
+ snprintf(seq,64,"\x1b[0K");
+ if (write(fd,seq,strlen(seq)) == -1) return;
+ /* Move cursor to original position. */
+ snprintf(seq,64,"\x1b[0G\x1b[%dC", (int)(pos+plen));
+ if (write(fd,seq,strlen(seq)) == -1) return;
+}
+
+static void beep() {
+ fprintf(stderr, "\x7");
+ fflush(stderr);
+}
+
+static void freeCompletions(linenoiseCompletions *lc) {
+ size_t i;
+ for (i = 0; i < lc->len; i++)
+ free(lc->cvec[i]);
+ if (lc->cvec != NULL)
+ free(lc->cvec);
+}
+
+static int completeLine(int fd, const char *prompt, char *buf, size_t buflen, size_t *len, size_t *pos, size_t cols) {
+ linenoiseCompletions lc = { 0, NULL };
+ int nread, nwritten;
+ char c = 0;
+
+ completionCallback(buf,&lc);
+ if (lc.len == 0) {
+ beep();
+ } else {
+ size_t stop = 0, i = 0;
+ size_t clen;
+
+ while(!stop) {
+ /* Show completion or original buffer */
+ if (i < lc.len) {
+ clen = strlen(lc.cvec[i]);
+ refreshLine(fd,prompt,lc.cvec[i],clen,clen,cols);
+ } else {
+ refreshLine(fd,prompt,buf,*len,*pos,cols);
+ }
+
+ nread = read(fd,&c,1);
+ if (nread <= 0) {
+ freeCompletions(&lc);
+ return -1;
+ }
+
+ switch(c) {
+ case 9: /* tab */
+ i = (i+1) % (lc.len+1);
+ if (i == lc.len) beep();
+ break;
+ case 27: /* escape */
+ /* Re-show original buffer */
+ if (i < lc.len) {
+ refreshLine(fd,prompt,buf,*len,*pos,cols);
+ }
+ stop = 1;
+ break;
+ default:
+ /* Update buffer and return */
+ if (i < lc.len) {
+ nwritten = snprintf(buf,buflen,"%s",lc.cvec[i]);
+ *len = *pos = nwritten;
+ }
+ stop = 1;
+ break;
+ }
+ }
+ }
+
+ freeCompletions(&lc);
+ return c; /* Return last read character */
+}
+
+void linenoiseClearScreen(void) {
+ if (write(STDIN_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
+ /* nothing to do, just to avoid warning. */
+ }
+}
+
+static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt) {
+ size_t plen = strlen(prompt);
+ size_t pos = 0;
+ size_t len = 0;
+ size_t cols = getColumns();
+ int history_index = 0;
+
+ buf[0] = '\0';
+ buflen--; /* Make sure there is always space for the nulterm */
+
+ /* The latest history entry is always our current buffer, that
+ * initially is just an empty string. */
+ linenoiseHistoryAdd("");
+
+ if (write(fd,prompt,plen) == -1) return -1;
+ while(1) {
+ char c;
+ int nread;
+ char seq[2], seq2[2];
+
+ nread = read(fd,&c,1);
+ if (nread <= 0) return len;
+
+ /* Only autocomplete when the callback is set. It returns < 0 when
+ * there was an error reading from fd. Otherwise it will return the
+ * character that should be handled next. */
+ if (c == 9 && completionCallback != NULL) {
+ c = completeLine(fd,prompt,buf,buflen,&len,&pos,cols);
+ /* Return on errors */
+ if (c < 0) return len;
+ /* Read next character when 0 */
+ if (c == 0) continue;
+ }
+
+ switch(c) {
+ case 13: /* enter */
+ history_len--;
+ free(history[history_len]);
+ return (int)len;
+ case 3: /* ctrl-c */
+ errno = EAGAIN;
+ return -1;
+ case 127: /* backspace */
+ case 8: /* ctrl-h */
+ if (pos > 0 && len > 0) {
+ memmove(buf+pos-1,buf+pos,len-pos);
+ pos--;
+ len--;
+ buf[len] = '\0';
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ }
+ break;
+ case 4: /* ctrl-d, remove char at right of cursor */
+ if (len > 1 && pos < (len-1)) {
+ memmove(buf+pos,buf+pos+1,len-pos);
+ len--;
+ buf[len] = '\0';
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ } else if (len == 0) {
+ history_len--;
+ free(history[history_len]);
+ errno = EPIPE;
+ return -1;
+ }
+ break;
+ case 20: /* ctrl-t */
+ if (pos > 0 && pos < len) {
+ int aux = buf[pos-1];
+ buf[pos-1] = buf[pos];
+ buf[pos] = aux;
+ if (pos != len-1) pos++;
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ }
+ break;
+ case 2: /* ctrl-b */
+ goto left_arrow;
+ case 6: /* ctrl-f */
+ goto right_arrow;
+ case 16: /* ctrl-p */
+ seq[1] = 65;
+ goto up_down_arrow;
+ case 14: /* ctrl-n */
+ seq[1] = 66;
+ goto up_down_arrow;
+ break;
+ case 27: /* escape sequence */
+ if (read(fd,seq,2) == -1) break;
+ if (seq[0] == 91 && seq[1] == 68) {
+left_arrow:
+ /* left arrow */
+ if (pos > 0) {
+ pos--;
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ }
+ } else if (seq[0] == 91 && seq[1] == 67) {
+right_arrow:
+ /* right arrow */
+ if (pos != len) {
+ pos++;
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ }
+ } else if (seq[0] == 91 && (seq[1] == 65 || seq[1] == 66)) {
+up_down_arrow:
+ /* up and down arrow: history */
+ if (history_len > 1) {
+ /* Update the current history entry before to
+ * overwrite it with tne next one. */
+ free(history[history_len-1-history_index]);
+ history[history_len-1-history_index] = strdup(buf);
+ /* Show the new entry */
+ history_index += (seq[1] == 65) ? 1 : -1;
+ if (history_index < 0) {
+ history_index = 0;
+ break;
+ } else if (history_index >= history_len) {
+ history_index = history_len-1;
+ break;
+ }
+ strncpy(buf,history[history_len-1-history_index],buflen);
+ buf[buflen] = '\0';
+ len = pos = strlen(buf);
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ }
+ } else if (seq[0] == 91 && seq[1] > 48 && seq[1] < 55) {
+ /* extended escape */
+ if (read(fd,seq2,2) == -1) break;
+ if (seq[1] == 51 && seq2[0] == 126) {
+ /* delete */
+ if (len > 0 && pos < len) {
+ memmove(buf+pos,buf+pos+1,len-pos-1);
+ len--;
+ buf[len] = '\0';
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ }
+ }
+ }
+ break;
+ default:
+ if (len < buflen) {
+ if (len == pos) {
+ buf[pos] = c;
+ pos++;
+ len++;
+ buf[len] = '\0';
+ if (plen+len < cols) {
+ /* Avoid a full update of the line in the
+ * trivial case. */
+ if (write(fd,&c,1) == -1) return -1;
+ } else {
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ }
+ } else {
+ memmove(buf+pos+1,buf+pos,len-pos);
+ buf[pos] = c;
+ len++;
+ pos++;
+ buf[len] = '\0';
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ }
+ }
+ break;
+ case 21: /* Ctrl+u, delete the whole line. */
+ buf[0] = '\0';
+ pos = len = 0;
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ break;
+ case 11: /* Ctrl+k, delete from current to end of line. */
+ buf[pos] = '\0';
+ len = pos;
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ break;
+ case 1: /* Ctrl+a, go to the start of the line */
+ pos = 0;
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ break;
+ case 5: /* ctrl+e, go to the end of the line */
+ pos = len;
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ break;
+ case 12: /* ctrl+l, clear screen */
+ linenoiseClearScreen();
+ refreshLine(fd,prompt,buf,len,pos,cols);
+ }
+ }
+ return len;
+}
+
+static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
+ int fd = STDIN_FILENO;
+ int count;
+
+ if (buflen == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!isatty(STDIN_FILENO)) {
+ if (fgets(buf, buflen, stdin) == NULL) return -1;
+ count = strlen(buf);