<commit_before>#!/usr/bin/env node

global['debug'] = true;

// include packages
var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var minimist = require('minimist');
var moment = require('moment');
require('moment-duration-format');
var sqlite = require('sqlite3');
var os = require('os');
var tmpdir = (os.tmpdir || os.tmpDir)();
var printf = require('sprintf-js').sprintf;
var rls = require('readline-sync');
var utils = require('./util.js');
// make all functions from util.js global
for (var key in utils)
		global[key] = utils[key];

// parse commandline args
var argv = minimist(process.argv.slice(2), {
	alias: { v: 'verbose', h: 'help', c: 'client', s: 'short', t: 'title', d: 'description', f: 'filter', street: 'street1' }
});

// open config
var HOME = process.env.HOME || process.env.USERPROFILE;
HOME = path.join(HOME, ".tim/");
var confpath = path.join(HOME, 'settings.json');
var conf = false;
try { conf = fs.statSync(confpath) } catch (err) { }
conf = (conf.isFile()) ? fs.readFileSync(confpath, { encoding: 'utf8' }).toString() : "";
conf = (conf.trim() === "") ? {} : JSON.parse(conf);

// create data directory and config
var dbopen = false;
var datadir = process.env.timdata || argv.datadir || conf.datadir || HOME;
var dblogging = console.log;
var dbname = 'db.sqlite';
var model = require('./model.js')(path.join(datadir, dbname)); // model variable

/**
 *  processes commandline args
 */
function proc(model, argv) {
	var verb = argv._[0];
	//m = require('./model.js')(path.join(datadir, dbname));

  if (argv.h) {
    usage(0);
  }

  // ##### SEARCH #####
  // search something on the database
  // for now you can only search clients
  else if (verb === 'search') {
    var target = argv._[1];
    if (target === 'help')
      return usage('search', false);
    // search clients
    if (target === 'client' || !target) {
			var search = {};
      if (argv.c || argv.name) {
				search.name = argv.c || argv.name;
      }
      else if (argv.s) {
				search.short = argv.s
      }
      else {
        //console.log("Please specify a client name with -c|--client or a short key with -s|--short!");
        return usage('search', true);
      }

      getClient(search, function (err, clients) {
        if (err) {
          console.log(err);
          process.exit(-1);
        }
        if (!clients || (clients.constructor === Array && clients.length === 0)) {
          console.log('No clients with that name or short-code found!\n');
          process.exit(-1);
        }

        var cols = [["id", "ID"], ["name", "Name"], ["street1", "Street 1"], ["zip", "Zip"], ["city", "City"], "email", ["short", "Shortkey"]];
				clients = stripNullsFromObjects(clients);
				cols.unshift(clients);
				asTable.apply(this, cols);
      });
    }
    else {
      return usage('search', true);
    }
  }

  // ##### ADD #####
  // add a client to the database
  else if (verb === 'add') {
    var type = argv._[1];
    if (type === 'client') {
      if (typeof argv.name === 'undefined' || argv.name.trim() === "") {
        console.log("Please supply at least a name for the new customer!");
        process.exit(-1);
      }
      model.Client.create({
        $name: argv.name,
        $street1: argv.street1 || "",
        $zip: argv.zip+"" || "",
        $city: argv.city || "",
        $street2: argv.street2 || "",
        $short: argv.short || null
      });
    }
    else {
      usage('add', true);
    }
  }

  // ###### START ####
  // start time measurement
  else if (verb === 'start') {
    var spattern = "";
    var stype = "";
		var search = {};

		if (argv.c) {
			search.name = argv.c;
		}
		else if (argv.s) {
			search.short = argv.s;
		}
		else {
      console.log("Please specify a client name with -c|--client or a short key with -s|--short!");
      return;
    }

		getClient(search, function(err, clients) {
			if (err) {
				console.log(err);
				process.exit(-1);
			}

			if (!clients || (clients.constructor === Array && clients.length === 0)) {
				console.log('No clients with that name or short-code found!\n');
				process.exit(-1);
			}

			if (clients.length > 1) {
				selectFromList(clients, null, function(err, result) {
					if (err) {
						console.log("Error selecting a client:\n" + err);
						process.exit(-1);
					}

					clients = result;
				});
			}

			debuglog("Selected client:\n" + JSON.stringify(clients, null, 2));

      startTimeTracking(clients, argv.t, argv.d, argv.start);
    });
  }
  // ##### STATUS ####
  // print status of time measurement
  else if (verb === 'status') {
    model.Time.status(function (err, data) {
      if (err)
        console.log("There was an error while querying for actual status: " + err);

      // output status information if there's an open time track
      if (data) {
        // found a record, telling the user about the status
        console.log(printf("Time tracking started!\n\t%-15s %s\n\t%-15s %s\n\t%-15s %s\n\t%-15s %s\n\t%-15s %s",
                           "Client:",
                           data.row.clientname,
                           "Title:",
                           data.row.title,
                           "Description:",
                           data.row.description,
                           "Since:",
                           moment(data.row.start).format('llll'),
                           "Duration:",
                           data.diffstr));
      }
      else {
        console.log("No time tracking running!");
      }
    });
  }

  // ##### STOP ####
  // stop time tracking
  else if (verb === 'stop') {
    var end = (argv.end) ? new Date(argv.end) : new Date();

    model.Time.stop(end, function(err) {
      if (err)
        console.log("There was an error ending the timetracking:\n" + err);
    });
  }

  // #### LIST ####
  // list tracked times
  else if (verb === 'list') {
    var filts = null;
    if (argv.f) {
      filts = JSON.parse(argv.f);
    }
		else {
			filts = [
				["archived", 0],
				["fk_InvoicePos is null"]
			];
		}

    model.Time.list(filts, function(err, rows) {
      if (err)
        throw err;

      rows.map(function (r) {
        r.start = moment(r.start).format("llll");
				if (r.end !== '- running -') {
					r.end = moment(r.end).format("llll");
				}
      });

      var cols = [];
      if (argv.columns) {
        cols = argv.columns.split(',');
			}
      else if (argv.archived) {
        cols = ["start", "end", "title", ["fk_InvoicePos", "invoice pos"], "archived", "clientname", "diffstr"];
			}
			else {
        cols = ["start", "end", "title", "clientname", "diffstr"];
			}

      cols.unshift(rows);
      asTable.apply(this, cols);
    });
  }

  // #### STAGE ###
  // commits tracked times or invoice positions
  else if (verb === 'commit') {
    var what = argv._[1]; // commit what?
		if (what === "times") {
			var client = argv.c || argv.s;
			var filter = [
				["fk_InvoicePos is null"],
				["archived", 0],
				["end not null"]
			];
			if (argv.c) {
				filter.push(["clientName", argv.c]);
			}
			else {
				filter.push(["shortKey", argv.s]);
			}

			model.Time.list(filter, function(err, rows) {
				if (err)
					throw err;

				// show start and end times in a more readable way
				rows.map(function (r) {
					r.start = moment(r.start).format("llll");
					r.end = moment(r.end).format("llll");
				});

				// print table
				var cols = ["id","start", "end", "title", "clientname", "diffstr"];
				cols.unshift(rows);
				asTable.apply(this, cols);

				console.log(""); // empty line after table

				// ask for time-IDs to put into an invoice position
				var answer = rls.question("Select 'all' or a comma separated list of time-IDs: ");
				var ids = [];
				var times = [];
				var quantity = 0;
				var hourlyWage = conf.hourlyDefaultWage;
				if (answer === "all") {
					rows.forEach(function (item) {
						ids.push(item.id);
					});
					times = rows;
				}
				else {
					ids = answer.split(",");
					ids = ids.map(Number);
					rows.forEach(function (item) {
						if (ids.indexOf(item.id)>=0) {
							times.push(item);
						}
					});
				}

				// calculate totals
				rows.forEach(function (row) {
					if (ids.indexOf(row.id)>=0) {
						quantity += row.diff.asHours();
					}
				});

				// ask for other stuff
				var title = rls.question("Title for this position: ");
				var description = rls.question("Description for this position (optional): ");
				var value = rls.question("Value per hour (" + hourlyWage + "): ") | hourlyWage;
				var quantity = rls.question("Quantity (" + Math.ceil(quantity) + "): ") | Math.ceil(quantity);

				// save changes!
				model.InvoicePos.create(ids, quantity, value, title, description);
			});
		}
		else if (what === "pos") {

		}
		else {
			usage('commit', true);
		}
  }

  // #### HELP ####
  // output help for parameter
  else if (verb === 'help') {
    usage(argv._[1], false);
  }

  // ##### TEST ####
  // for testing stuff
  else if (verb === 'invoicetest') {
    var inv = require('./invoice.js')("./tpl/invoice.ejs");
    var cli = { "name": "Kunde", "street1": "Kundenstraße 15", "street2": "Apartment 1a", "zip": 55411, "city": "Bingen" };
    var pos = [{ "title": "Programmierung", "quantity": 5, "value": 20, "description": "code tests" }, { "title": "Programmierung", "quantity": 5, "value": 20, "description": "code tests" }];
    inv.create(cli, pos, 10, new Date());
  }
	else if (verb === 'test') {
		debuglog('util test');
	}

  else if (verb === 'init') {
		if (process.env.timdata || argv.datadir) {
			datadir = process.env.timdata || argv.datadir;
		}
		mkdirp.sync(datadir);
		var model = require('./model.js')(path.join(datadir, dbname), debug);
    model.initDb();
		conf.datadir = datadir;
		conf.hourlyDefaultWage = rls.question("What will be your default hourly wage? ");
		saveConfig();
  }

  else {
    usage(null, true);
  }
}

/**
 * starts timetracking for a specific client.
 * mandatory parameters:
 * - client
 * - title
 */
function startTimeTracking(client, title, description, date) {
  if (!client)
    throw "Please specify a client to track the time for!";
  if (!title)
    throw "You need to specify a title!";
  description = description || null;

  // parse date here
  date = (date) ? new Date(date) : new Date();
  if (!(date instanceof Date || date.toString() === "Invalid Date"))
    throw "This is not a valid Date";

	model.Time.start(client, title, description, date, function (err) {
		if (err)
			throw err;
		else
			console.log("Time tracking started for ", client.name, "!");
	});
}

/**
 * gets clients based on search-pattern (spattern) and search-type (stype)
 * and passes the results to callback
 */
function getClient(search, callback) {
	// search client by name
	if (search.name) {
    debuglog("Searching client by name: " + search.name);
    model.Client.findByName({ $name: search.name	}, callback)
  }
  // search for short key
  else if (search.short) {
    debuglog("Searching client by shortkey:" + search.short);
    model.Client.findByKey({ $short: search.short}, callback);
  }
  else {
		callback("No client search argument given!");
  }
}

/**
 * saves the configuration
 */
function saveConfig() {
	console.log("Saving config to " + confpath);
  fs.writeFileSync(confpath, JSON.stringify(conf, null, 2), { encoding: 'utf8' }, function (err) {
    if (err) throw err;
    console.log('Config saved!');
  });
}

/**
 * prints usage information for tim
 */
function usage(arg, invalid) {
  // parse arguments
  invalid = typeof invalid === 'undefined' ? false : invalid;
  if (invalid) console.log("Invalid command!");

  // define usage strings
  var usage = new Array();

  // help for search keyword
  usage['search'] = new Array();
  usage['search'][0] = "\ttim search [client] (-c|--client pattern)|(-s|--short key)";

  // help for add keyword
  usage['add'] = new Array();
  usage['add'][0] = "\ttim add client -c|--client ClientName [--street1 value] [--street2 value] [--zip zip] [--city city] [--short shortkey]";

  // start keyword
  usage['start'] = new Array();
  usage['start'][0] = "\ttim start ((-c|--client ClientName)|(-s|--short ShortKey)) -t|--title Title [--description Description] [--start StartTime]";

  usage['status'] = new Array();
  usage['status'][0] = "\ttim status";

  usage['stop'] = new Array();
  usage['stop'][0] = "\ttim stop [--end EndTime]";

	usage['list'] = new Array();
	usage['list'][0] = "\ttim list [filters]";

	usage['commit'] = new Array();
	usage['commit'].push("\ttim commit - Combines tracked times into an invoice position");
	usage['commit'].push("\ttim commit times|pos (-c|--client clientName)|(-s|--short shortKey)");

  // output usage / help
  console.log("Usage - incomplete but please go ahead and read what's already there:\n");
  if (arg && usage[arg] != undefined) {
    console.log("Usage for tim " + arg + ":");
    usage[arg].forEach(function (val) {
      console.log(val);
    });
  }
  else {
    for (var usg in usage) {
      usage[usg].forEach(function (val) {
        console.log(val);
      });
    }
  }

  process.exit(invalid ? -1 : 0);
}

proc(model, argv);
<commit_msg>minor bugs fixed<commit_after>#!/usr/bin/env node

global['debug'] = true;

// include packages
var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var minimist = require('minimist');
var moment = require('moment');
require('moment-duration-format');
var sqlite = require('sqlite3');
var os = require('os');
var tmpdir = (os.tmpdir || os.tmpDir)();
var printf = require('sprintf-js').sprintf;
var rls = require('readline-sync');
var utils = require('./util.js');
// make all functions from util.js global
for (var key in utils)
		global[key] = utils[key];

// parse commandline args
var argv = minimist(process.argv.slice(2), {
	alias: { v: 'verbose', h: 'help', c: 'client', s: 'short', t: 'title', d: 'description', f: 'filter', street: 'street1' }
});

// open config
var HOME = process.env.HOME || process.env.USERPROFILE;
HOME = path.join(HOME, ".tim/");
var confpath = path.join(HOME, 'settings.json');
var conf = false;
try { conf = fs.statSync(confpath) } catch (err) { }
conf = (conf.isFile()) ? fs.readFileSync(confpath, { encoding: 'utf8' }).toString() : "";
conf = (conf.trim() === "") ? {} : JSON.parse(conf);

// create data directory and config
var dbopen = false;
var datadir = process.env.timdata || argv.datadir || conf.datadir || HOME;
var dblogging = console.log;
var dbname = 'db.sqlite';
var model = require('./model.js')(path.join(datadir, dbname)); // model variable

/**
 *  processes commandline args
 */
function proc(model, argv) {
	var verb = argv._[0];
	//m = require('./model.js')(path.join(datadir, dbname));

  if (argv.h) {
    usage(0);
  }

  // ##### SEARCH #####
  // search something on the database
  // for now you can only search clients
  else if (verb === 'search') {
    var target = argv._[1];
    if (target === 'help')
      return usage('search', false);
    // search clients
    if (target === 'client' || !target) {
			var search = {};
      if (argv.c || argv.name) {
				search.name = argv.c || argv.name;
      }
      else if (argv.s) {
				search.short = argv.s
      }
      else {
        //console.log("Please specify a client name with -c|--client or a short key with -s|--short!");
        return usage('search', true);
      }

      getClient(search, function (err, clients) {
        if (err) {
          console.log(err);
          process.exit(-1);
        }
        if (!clients || (clients.constructor === Array && clients.length === 0)) {
          console.log('No clients with that name or short-code found!\n');
          process.exit(-1);
        }

        var cols = [["id", "ID"], ["name", "Name"], ["street1", "Street 1"], ["zip", "Zip"], ["city", "City"], "email", ["short", "Shortkey"]];
				clients = stripNullsFromObjects(clients);
				cols.unshift(clients);
				asTable.apply(this, cols);
      });
    }
    else {
      return usage('search', true);
    }
  }

  // ##### ADD #####
  // add a client to the database
  else if (verb === 'add') {
    var type = argv._[1];
    if (type === 'client') {
      if (typeof argv.name === 'undefined' || argv.name.trim() === "") {
        console.log("Please supply at least a name for the new customer!");
        process.exit(-1);
      }
      model.Client.create({
        $name: argv.name,
        $street1: argv.street1 || "",
        $zip: (argv.zip) ? argv.zip+"" : "",
        $city: argv.city || "",
        $street2: argv.street2 || "",
        $short: argv.short || null
      });
    }
    else {
      usage('add', true);
    }
  }

  // ###### START ####
  // start time measurement
  else if (verb === 'start') {
    var spattern = "";
    var stype = "";
		var search = {};

		if (argv.c) {
			search.name = argv.c;
		}
		else if (argv.s) {
			search.short = argv.s;
		}
		else {
      console.log("Please specify a client name with -c|--client or a short key with -s|--short!");
      return;
    }

		getClient(search, function(err, clients) {
			if (err) {
				console.log(err);
				process.exit(-1);
			}

			if (!clients || (clients.constructor === Array && clients.length === 0)) {
				console.log('No clients with that name or short-code found!\n');
				process.exit(-1);
			}

			if (clients.length > 1) {
				selectFromList(clients, null, function(err, result) {
					if (err) {
						console.log("Error selecting a client:\n" + err);
						process.exit(-1);
					}

					clients = result;
				});
			}

			if (clients.constructor === Array && clients.length === 1)
				clients = clients[0];

			debuglog("Selected client:\n" + JSON.stringify(clients, null, 2));

      startTimeTracking(clients, argv.t, argv.d, argv.start);
    });
  }
  // ##### STATUS ####
  // print status of time measurement
  else if (verb === 'status') {
    model.Time.status(function (err, data) {
      if (err)
        console.log("There was an error while querying for actual status: " + err);

      // output status information if there's an open time track
      if (data) {
        // found a record, telling the user about the status
        console.log(printf("Time tracking started!\n\t%-15s %s\n\t%-15s %s\n\t%-15s %s\n\t%-15s %s\n\t%-15s %s",
                           "Client:",
                           data.row.clientname,
                           "Title:",
                           data.row.title,
                           "Description:",
                           data.row.description,
                           "Since:",
                           moment(data.row.start).format('llll'),
                           "Duration:",
                           data.diffstr));
      }
      else {
        console.log("No time tracking running!");
      }
    });
  }

  // ##### STOP ####
  // stop time tracking
  else if (verb === 'stop') {
    var end = (argv.end) ? new Date(argv.end) : new Date();

    model.Time.stop(end, function(err) {
      if (err)
        console.log("There was an error ending the timetracking:\n" + err);
    });
  }

  // #### LIST ####
  // list tracked times
  else if (verb === 'list') {
    var filts = null;
    if (argv.f) {
      filts = JSON.parse(argv.f);
    }
		else {
			filts = [
				["archived", 0],
				["fk_InvoicePos is null"]
			];
		}

    model.Time.list(filts, function(err, rows) {
      if (err)
        throw err;

      rows.map(function (r) {
        r.start = moment(r.start).format("llll");
				if (r.end !== '- running -') {
					r.end = moment(r.end).format("llll");
				}
      });

      var cols = [];
      if (argv.columns) {
        cols = argv.columns.split(',');
			}
      else if (argv.archived) {
        cols = ["start", "end", "title", ["fk_InvoicePos", "invoice pos"], "archived", "clientname", "diffstr"];
			}
			else {
        cols = ["start", "end", "title", "clientname", "diffstr"];
			}

      cols.unshift(rows);
      asTable.apply(this, cols);
    });
  }

  // #### STAGE ###
  // commits tracked times or invoice positions
  else if (verb === 'commit') {
    var what = argv._[1]; // commit what?
		if (what === "times") {
			var client = argv.c || argv.s;
			var filter = [
				["fk_InvoicePos is null"],
				["archived", 0],
				["end not null"]
			];
			if (argv.c) {
				filter.push(["clientName", argv.c]);
			}
			else {
				filter.push(["shortKey", argv.s]);
			}

			model.Time.list(filter, function(err, rows) {
				if (err)
					throw err;

				// show start and end times in a more readable way
				rows.map(function (r) {
					r.start = moment(r.start).format("llll");
					r.end = moment(r.end).format("llll");
				});

				// print table
				var cols = ["id","start", "end", "title", "clientname", "diffstr"];
				cols.unshift(rows);
				asTable.apply(this, cols);

				console.log(""); // empty line after table

				// ask for time-IDs to put into an invoice position
				var answer = rls.question("Select 'all' or a comma separated list of time-IDs: ");
				var ids = [];
				var times = [];
				var quantity = 0;
				var hourlyWage = conf.hourlyDefaultWage;
				if (answer === "all") {
					rows.forEach(function (item) {
						ids.push(item.id);
					});
					times = rows;
				}
				else {
					ids = answer.split(",");
					ids = ids.map(Number);
					rows.forEach(function (item) {
						if (ids.indexOf(item.id)>=0) {
							times.push(item);
						}
					});
				}

				// calculate totals
				rows.forEach(function (row) {
					if (ids.indexOf(row.id)>=0) {
						quantity += row.diff.asHours();
					}
				});

				// ask for other stuff
				var title = rls.question("Title for this position: ");
				var description = rls.question("Description for this position (optional): ");
				var value = rls.question("Value per hour (" + hourlyWage + "): ") | hourlyWage;
				var quantity = rls.question("Quantity (" + Math.ceil(quantity) + "): ") | Math.ceil(quantity);

				// save changes!
				model.InvoicePos.create(ids, quantity, value, title, description);
			});
		}
		else if (what === "pos") {

		}
		else {
			usage('commit', true);
		}
  }

  // #### HELP ####
  // output help for parameter
  else if (verb === 'help') {
    usage(argv._[1], false);
  }

  // ##### TEST ####
  // for testing stuff
  else if (verb === 'invoicetest') {
    var inv = require('./invoice.js')("./tpl/invoice.ejs");
    var cli = { "name": "Kunde", "street1": "Kundenstraße 15", "street2": "Apartment 1a", "zip": 55411, "city": "Bingen" };
    var pos = [{ "title": "Programmierung", "quantity": 5, "value": 20, "description": "code tests" }, { "title": "Programmierung", "quantity": 5, "value": 20, "description": "code tests" }];
    inv.create(cli, pos, 10, new Date());
  }
	else if (verb === 'test') {
		debuglog('util test');
	}

  else if (verb === 'init') {
		if (process.env.timdata || argv.datadir) {
			datadir = process.env.timdata || argv.datadir;
		}
		mkdirp.sync(datadir);
		var model = require('./model.js')(path.join(datadir, dbname), debug);
    model.initDb();
		conf.datadir = datadir;
		conf.hourlyDefaultWage = rls.question("What will be your default hourly wage? ");
		saveConfig();
  }

  else {
    usage(null, true);
  }
}

/**
 * starts timetracking for a specific client.
 * mandatory parameters:
 * - client
 * - title
 */
function startTimeTracking(client, title, description, date) {
  if (!client)
    throw "Please specify a client to track the time for!";
  if (!title)
    throw "You need to specify a title!";
  description = description || null;

  // parse date here
  date = (date) ? new Date(date) : new Date();
  if (!(date instanceof Date || date.toString() === "Invalid Date"))
    throw "This is not a valid Date";

	model.Time.start(client, title, description, date, function (err) {
		if (err)
			throw err;
		else
			console.log("Time tracking started for", client.name + "!");
	});
}

/**
 * gets clients based on search-pattern (spattern) and search-type (stype)
 * and passes the results to callback
 */
function getClient(search, callback) {
	// search client by name
	if (search.name) {
    debuglog("Searching client by name: " + search.name);
    model.Client.findByName({ $name: search.name	}, callback)
  }
  // search for short key
  else if (search.short) {
    debuglog("Searching client by shortkey:" + search.short);
    model.Client.findByKey({ $short: search.short}, callback);
  }
  else {
		callback("No client search argument given!");
  }
}

/**
 * saves the configuration
 */
function saveConfig() {
	console.log("Saving config to " + confpath);
  fs.writeFileSync(confpath, JSON.stringify(conf, null, 2), { encoding: 'utf8' }, function (err) {
    if (err) throw err;
    console.log('Config saved!');
  });
}

/**
 * prints usage information for tim
 */
function usage(arg, invalid) {
  // parse arguments
  invalid = typeof invalid === 'undefined' ? false : invalid;
  if (invalid) console.log("Invalid command!");

  // define usage strings
  var usage = new Array();

  // help for search keyword
  usage['search'] = new Array();
  usage['search'][0] = "\ttim search [client] (-c|--client pattern)|(-s|--short key)";

  // help for add keyword
  usage['add'] = new Array();
  usage['add'][0] = "\ttim add client -c|--client ClientName [--street1 value] [--street2 value] [--zip zip] [--city city] [--short shortkey]";

  // start keyword
  usage['start'] = new Array();
  usage['start'][0] = "\ttim start ((-c|--client ClientName)|(-s|--short ShortKey)) -t|--title Title [--description Description] [--start StartTime]";

  usage['status'] = new Array();
  usage['status'][0] = "\ttim status";

  usage['stop'] = new Array();
  usage['stop'][0] = "\ttim stop [--end EndTime]";

	usage['list'] = new Array();
	usage['list'][0] = "\ttim list [filters]";

	usage['commit'] = new Array();
	usage['commit'].push("\ttim commit - Combines tracked times into an invoice position");
	usage['commit'].push("\ttim commit times|pos (-c|--client clientName)|(-s|--short shortKey)");

  // output usage / help
  console.log("Usage - incomplete but please go ahead and read what's already there:\n");
  if (arg && usage[arg] != undefined) {
    console.log("Usage for tim " + arg + ":");
    usage[arg].forEach(function (val) {
      console.log(val);
    });
  }
  else {
    for (var usg in usage) {
      usage[usg].forEach(function (val) {
        console.log(val);
      });
    }
  }

  process.exit(invalid ? -1 : 0);
}

proc(model, argv);
<|endoftext|>


<commit_before>/* @flow strict-local */
import React, { PureComponent } from 'react';
import { View, StyleSheet } from 'react-native';

import type { User, Dispatch } from '../types';
import { connect } from '../react-redux';
import { UserAvatar, ComponentList, RawLabel } from '../common';
import { getCurrentRealm, getUserStatusTextForUser } from '../selectors';
import PresenceStatusIndicator from '../common/PresenceStatusIndicator';
import ActivityText from '../title/ActivityText';
import { getAvatarFromUser } from '../utils/avatar';
import { nowInTimeZone } from '../utils/date';
import styles from '../styles';

const componentStyles = StyleSheet.create({
  componentListItem: {
    alignItems: 'center',
  },
  statusWrapper: {
    justifyContent: 'center',
    flexDirection: 'row',
  },
  statusText: {
    textAlign: 'center',
  },
});

const AVATAR_SIZE = 200;

type SelectorProps = {|
  realm: string,
  userStatusText: string | void,
|};

type Props = $ReadOnly<{|
  user: User,

  dispatch: Dispatch,
  ...SelectorProps,
|}>;

class AccountDetails extends PureComponent<Props> {
  render() {
    const { realm, user, userStatusText } = this.props;

    return (
      <View>
        <ComponentList outerSpacing itemStyle={componentStyles.componentListItem}>
          <View>
            <UserAvatar
              avatarUrl={getAvatarFromUser(user, realm, AVATAR_SIZE)}
              size={AVATAR_SIZE}
            />
          </View>
          <View style={componentStyles.statusWrapper}>
            <RawLabel style={[styles.largerText, styles.halfMarginRight]} text={user.full_name} />
            <PresenceStatusIndicator email={user.email} hideIfOffline={false} />
          </View>
          {userStatusText !== undefined && (
            <RawLabel
              style={[styles.largerText, componentStyles.statusText]}
              text={userStatusText}
            />
          )}
          <View>
            <ActivityText style={styles.largerText} user={user} />
          </View>
          {user.timezone ? (
            <View>
              <RawLabel
                style={styles.largerText}
                text={`${nowInTimeZone(user.timezone)} Local time`}
              />
            </View>
          ) : null}
        </ComponentList>
      </View>
    );
  }
}

export default connect((state, props): SelectorProps => ({
  realm: getCurrentRealm(state),
  userStatusText: getUserStatusTextForUser(state, props.user.user_id),
}))(AccountDetails);
<commit_msg>account details: remove unneeded <View><commit_after>/* @flow strict-local */
import React, { PureComponent } from 'react';
import { View, StyleSheet } from 'react-native';

import type { User, Dispatch } from '../types';
import { connect } from '../react-redux';
import { UserAvatar, ComponentList, RawLabel } from '../common';
import { getCurrentRealm, getUserStatusTextForUser } from '../selectors';
import PresenceStatusIndicator from '../common/PresenceStatusIndicator';
import ActivityText from '../title/ActivityText';
import { getAvatarFromUser } from '../utils/avatar';
import { nowInTimeZone } from '../utils/date';
import styles from '../styles';

const componentStyles = StyleSheet.create({
  componentListItem: {
    alignItems: 'center',
  },
  statusWrapper: {
    justifyContent: 'center',
    flexDirection: 'row',
  },
  statusText: {
    textAlign: 'center',
  },
});

const AVATAR_SIZE = 200;

type SelectorProps = {|
  realm: string,
  userStatusText: string | void,
|};

type Props = $ReadOnly<{|
  user: User,

  dispatch: Dispatch,
  ...SelectorProps,
|}>;

class AccountDetails extends PureComponent<Props> {
  render() {
    const { realm, user, userStatusText } = this.props;

    return (
      <ComponentList outerSpacing itemStyle={componentStyles.componentListItem}>
        <View>
          <UserAvatar avatarUrl={getAvatarFromUser(user, realm, AVATAR_SIZE)} size={AVATAR_SIZE} />
        </View>
        <View style={componentStyles.statusWrapper}>
          <RawLabel style={[styles.largerText, styles.halfMarginRight]} text={user.full_name} />
          <PresenceStatusIndicator email={user.email} hideIfOffline={false} />
        </View>
        {userStatusText !== undefined && (
          <RawLabel style={[styles.largerText, componentStyles.statusText]} text={userStatusText} />
        )}
        <View>
          <ActivityText style={styles.largerText} user={user} />
        </View>
        {user.timezone ? (
          <View>
            <RawLabel
              style={styles.largerText}
              text={`${nowInTimeZone(user.timezone)} Local time`}
            />
          </View>
        ) : null}
      </ComponentList>
    );
  }
}

export default connect((state, props): SelectorProps => ({
  realm: getCurrentRealm(state),
  userStatusText: getUserStatusTextForUser(state, props.user.user_id),
}))(AccountDetails);
<|endoftext|>


<commit_before>#!/usr/bin/env node

var fs = require('fs');
var perfectionist = require('../dist');
var read = require('read-file-stdin');
var write = require('write-file-stdout');

var opts = require('minimist')(process.argv.slice(2), {
    alias: {
        f: 'format',
        h: 'help',
        s: 'sourcemap',
        v: 'version'
    }
});

if (opts.version) {
    return console.log(require('../package.json').version);
}

var file = opts._[0];
var out  = opts._[1];

if (file === 'help' || opts.help) {
    return fs.createReadStream(__dirname + '/usage.txt')
        .pipe(process.stdout)
        .on('close', function () { process.exit(1); });
}

read(file, function (err, buf) {
    if (err) {
        throw err;
    }
    if (file) {
        opts.from = file;
    }
    if (out) {
        opts.to = out;
    }
    write(out, String(perfectionist.process(String(buf), opts)));
});
<commit_msg>Make bin executable.<commit_after>#!/usr/bin/env node

var fs = require('fs');
var perfectionist = require('../dist');
var read = require('read-file-stdin');
var write = require('write-file-stdout');

var opts = require('minimist')(process.argv.slice(2), {
    alias: {
        f: 'format',
        h: 'help',
        s: 'sourcemap',
        v: 'version'
    }
});

if (opts.version) {
    return console.log(require('../package.json').version);
}

var file = opts._[0];
var out  = opts._[1];

if (file === 'help' || opts.help) {
    return fs.createReadStream(__dirname + '/usage.txt')
        .pipe(process.stdout)
        .on('close', function () { process.exit(1); });
}

read(file, function (err, buf) {
    if (err) {
        throw err;
    }
    if (file) {
        opts.from = file;
    }
    if (out) {
        opts.to = out;
    }
    write(out, String(perfectionist.process(String(buf), opts)));
});
<|endoftext|>


<commit_before>#!/usr/bin/env node

var minimist = require('minimist')
var api = require('music163')
var Player = require('player')
var fs = require('fs')

var argv = minimist(process.argv.slice(2))

if (argv.h) usage(0)
else if (argv._[0] === 'search') {
  var keyword = argv._[1]
  api.search(keyword, function(err, res) {
    if (err) console.log(err)
    var data = res.result

    console.log('artists')
    if (data.artists) {
      data.artists.forEach(function(artist) {
        console.log(artist.name + ' ID: ' + artist.id)
      })
    }
    console.log()

    console.log('albums')
    if (data.albums) {
      data.albums.forEach(function(album) {
        console.log(album.name + ' by ' + album.artist.name + ' ID: ' + album.id)
      })
    }
    console.log()

    console.log('playlists')
    data.playlists.forEach(function(playlist) {
      console.log(playlist.name + ' created by ' + playlist.creator.nickname + ' ID: ' + playlist.id)
    })
    console.log()

    console.log('songs')
    if (data.songs) {
      data.songs.forEach(function(song) {
        console.log(song.name + ' ID: ' + song.id)
      })
    }
  })
} else if (argv._[0] === 'album') {
  var id = argv._[1]
  api.album(id, function(err, res) {
    if (err) console.log(err)
    console.log('Album "' + res.album.name + '" by "' + res.album.artist.name + '" list: ')
    res.album.songs.forEach(function(song) {
      console.log(song.artists[0].name + ' - ' + song.name + ' ID: ' + song.id)
    })
  })
} else if (argv._[0] === 'playlist') {
  var id = argv._[1]
  api.playlist(id, function(err, res) {
    if (err) console.log(err)
    console.log('Playlist by "' + res.result.creator.nickname + '" List: ')
    res.result.tracks.forEach(function(song) {
      console.log(song.artists[0].name + ' - ' + song.name + ' ID: ' + song.id)
    })
  })
} else if (argv._[0] === 'dj') {
  var id = argv._[1]
  api.dj(id, function(err, res) {
    if (err) console.log(err)
    console.log('DJ by "' + res.program.dj.nickname + '" List: ')
    res.program.songs.forEach(function(song) {
      console.log(song.artists[0].name + ' - ' + song.name + ': ' + song.mp3Url)
    })
  })
} else if (argv._[0] === 'detail') {
  var id = argv._[1]
  api.detail(id, function(err, res) {
    if (err) console.log(err)
    if (res.songs) {
      res.songs.forEach(function(song) {
        console.log('song info: ' + song.artists[0].name + ' by ' + song.name + ' ' + song.mp3Url)
      })
    }
  })
} else if (argv._[0] === 'play') {
  if (argv._.length < 2) return console.error('please set the type you want to play by \"-t TYPE \"')

  var id = argv._[1]
  var type = argv.type || argv.t
  var songs = []

  if (type === 'album') {
    api.album(id, function(err, res) {
      if (err) console.log(err)
      res.album.songs.forEach(function(song) {
        songs.push({
          name: song.name,
          artist: song.artists[0].name,
          src: song.mp3Url
        })
      })
    })
  } else if (type === 'playlist') {
    api.playlist(id, function(err, res) {
      if (err) console.log(err)
      res.result.tracks.forEach(function(song) {
        songs.push({
          name: song.name,
          artist: song.artists[0].name,
          src: song.mp3Url
        })
      })
      play(songs)
    })
  } else if (type === 'dj') {
    api.dj(id, function(err, res) {
      if (err) console.log(err)
      res.program.songs.forEach(function(song) {
        songs.push({
          name: song.name,
          artist: song.artists[0].name,
          src: song.mp3Url
        })
      })
    })
  } else if (type === 'detail') {
    api.detail(id, function(err, res) {
      if (err) console.log(err)
      res.songs.forEach(function(song) {
        songs.push({
          name: song.name,
          artist: song.artists[0].name,
          src: song.mp3Url
        })
      })
    })
  }
} else if (argv._[0] === 'download') {
  console.log('to be added...')
} else usage(1)

function usage(code) {
  var rs = fs.createReadStream(__dirname + '/usage.txt')
  rs.pipe(process.stdout)
  rs.on('close', function() {
    if (code) process.exit(code)
  })
}

function play(songs) {
  var player = new Player(songs)
  player.play(function(err, player){
    console.log('Done playing all the songs!')
  })

  player.on('playing', function(item) {
    console.log('now playing ' + item.name + ' by ' + item.artist)
  })

  player.on('playend', function(item) {
    console.log('song:' + item.name + ' play done, switching to next one ...')
  })

  player.on('error', function(err) {
    console.log(err)
  })
}<commit_msg>add code to play songlist<commit_after>#!/usr/bin/env node

var minimist = require('minimist')
var api = require('music163')
var Player = require('player')
var fs = require('fs')

var argv = minimist(process.argv.slice(2))

if (argv.h) usage(0)
else if (argv._[0] === 'search') {
  var keyword = argv._[1]
  api.search(keyword, function(err, res) {
    if (err) console.log(err)
    var data = res.result

    console.log('artists')
    if (data.artists) {
      data.artists.forEach(function(artist) {
        console.log(artist.name + ' ID: ' + artist.id)
      })
    }
    console.log()

    console.log('albums')
    if (data.albums) {
      data.albums.forEach(function(album) {
        console.log(album.name + ' by ' + album.artist.name + ' ID: ' + album.id)
      })
    }
    console.log()

    console.log('playlists')
    data.playlists.forEach(function(playlist) {
      console.log(playlist.name + ' created by ' + playlist.creator.nickname + ' ID: ' + playlist.id)
    })
    console.log()

    console.log('songs')
    if (data.songs) {
      data.songs.forEach(function(song) {
        console.log(song.name + ' ID: ' + song.id)
      })
    }
  })
} else if (argv._[0] === 'album') {
  var id = argv._[1]
  api.album(id, function(err, res) {
    if (err) console.log(err)
    console.log('Album "' + res.album.name + '" by "' + res.album.artist.name + '" list: ')
    res.album.songs.forEach(function(song) {
      console.log(song.artists[0].name + ' - ' + song.name + ' ID: ' + song.id)
    })
  })
} else if (argv._[0] === 'playlist') {
  var id = argv._[1]
  api.playlist(id, function(err, res) {
    if (err) console.log(err)
    console.log('Playlist by "' + res.result.creator.nickname + '" List: ')
    res.result.tracks.forEach(function(song) {
      console.log(song.artists[0].name + ' - ' + song.name + ' ID: ' + song.id)
    })
  })
} else if (argv._[0] === 'dj') {
  var id = argv._[1]
  api.dj(id, function(err, res) {
    if (err) console.log(err)
    console.log('DJ by "' + res.program.dj.nickname + '" List: ')
    res.program.songs.forEach(function(song) {
      console.log(song.artists[0].name + ' - ' + song.name + ': ' + song.mp3Url)
    })
  })
} else if (argv._[0] === 'detail') {
  var id = argv._[1]
  api.detail(id, function(err, res) {
    if (err) console.log(err)
    if (res.songs) {
      res.songs.forEach(function(song) {
        console.log('song info: ' + song.artists[0].name + ' by ' + song.name + ' ' + song.mp3Url)
      })
    }
  })
} else if (argv._[0] === 'play') {
  if (argv._.length < 2) return console.error('please set the type you want to play by \"-t TYPE \"')

  var id = argv._[1]
  var type = argv.type || argv.t
  var songs = []

  if (type === 'album') {
    api.album(id, function(err, res) {
      if (err) console.log(err)
      res.album.songs.forEach(function(song) {
        songs.push({
          name: song.name,
          artist: song.artists[0].name,
          src: song.mp3Url
        })
      })
      play(songs)
    })
  } else if (type === 'playlist') {
    api.playlist(id, function(err, res) {
      if (err) console.log(err)
      res.result.tracks.forEach(function(song) {
        songs.push({
          name: song.name,
          artist: song.artists[0].name,
          src: song.mp3Url
        })
      })
      play(songs)
    })
  } else if (type === 'dj') {
    api.dj(id, function(err, res) {
      if (err) console.log(err)
      res.program.songs.forEach(function(song) {
        songs.push({
          name: song.name,
          artist: song.artists[0].name,
          src: song.mp3Url
        })
      })
      play(songs)
    })
  } else if (type === 'detail') {
    api.detail(id, function(err, res) {
      if (err) console.log(err)
      res.songs.forEach(function(song) {
        songs.push({
          name: song.name,
          artist: song.artists[0].name,
          src: song.mp3Url
        })
      })
      play(songs)
    })
  }
} else if (argv._[0] === 'download') {
  console.log('to be added...')
} else usage(1)

function usage(code) {
  var rs = fs.createReadStream(__dirname + '/usage.txt')
  rs.pipe(process.stdout)
  rs.on('close', function() {
    if (code) process.exit(code)
  })
}

function play(songs) {
  var player = new Player(songs)
  player.play(function(err, player){
    console.log('Done playing all the songs!')
  })

  player.on('playing', function(item) {
    console.log('now playing ' + item.name + ' by ' + item.artist)
  })

  player.on('playend', function(item) {
    console.log('song:' + item.name + ' play done, switching to next one ...')
  })

  player.on('error', function(err) {
    console.log(err)
  })
}<|endoftext|>


<commit_before>module.exports = Cli

var defaults = require('defaults')
var minimist = require('minimist')
var multiline = require('multiline')
var stdin = require('get-stdin')
var fs = require('fs')

function Cli (opts) {
  var standard = require('../').linter(opts)

  opts = defaults(opts, {
    cmd: 'standard-engine',
    tagline: 'JavaScript Custom Style',
    version: require('../package.json').version
  })

  var argv = minimist(process.argv.slice(2), {
    alias: {
      format: 'F',
      global: 'globals',
      help: 'h',
      verbose: 'v'
    },
    boolean: [
      'format',
      'help',
      'stdin',
      'verbose',
      'version'
    ],
    string: [
      'global',
      'parser'
    ]
  })

  if (argv.format) {
    var typeFormatter = typeof opts.formatter

    if (typeFormatter !== 'object' || typeof opts.formatter.transform !== 'function') {
      console.error('Invalid formatter API')
      process.exit(0)
    }
  }

  // flag `-` is equivalent to `--stdin`
  if (argv._[0] === '-') {
    argv.stdin = true
    argv._.shift()
  }

  if (argv.help) {
    var fmtMsg = ''
    if (opts.formatter) {
      fmtMsg = '-F  --format    Automatically format code.'
      if (opts.formatterName) fmtMsg += ' (using ' + opts.formatterName + ')'
    }
    if (opts.tagline) console.log('%s - %s (%s)', opts.cmd, opts.tagline, opts.homepage)
    console.log(multiline.stripIndent(function () {
      /*

        Usage:
            %s <flags> [FILES...]

            If FILES is omitted, then all JavaScript source files (*.js, *.jsx) in the current
            working directory are checked, recursively.

            Certain paths (node_modules/, .git/, coverage/, *.min.js, bundle.js) are
            automatically excluded.

        Flags:
            %s
            -v, --verbose   Show error codes. (so you can ignore specific rules)
                --stdin     Read file text from stdin.
                --global    Declare global variable
                --parser    Use custom js parser (e.g. babel-eslint)
                --version   Show current version
            -h, --help      Show usage information
      */
    }), opts.cmd, fmtMsg)
    process.exit(0)
  }

  if (argv.version) {
    console.log(opts.version)
    process.exit(0)
  }

  var lintOpts = {
    parser: argv.parser,
    global: argv.global
  }

  if (argv.stdin) {
    stdin(function (text) {
      if (argv.format) {
        text = opts.formatter.transform(text)
        process.stdout.write(text)
      }
      standard.lintText(text, lintOpts, onResult)
    })
  } else {
    if (argv.format) {
      lintOpts._onFiles = function (files) {
        files.forEach(function (file) {
          var data = fs.readFileSync(file).toString()
          fs.writeFileSync(file, opts.formatter.transform(data))
        })
      }
    }
    standard.lintFiles(argv._, lintOpts, onResult)
  }

  function onResult (err, result) {
    if (err) return onError(err)
    if (result.errorCount === 0) process.exit(0)

    console.log(
      opts.cmd + ': Use %s (%s) ',
      opts.tagline,
      opts.homepage
    )

    result.results.forEach(function (result) {
      result.messages.forEach(function (message) {
        log(
          '  %s:%d:%d: %s%s',
          result.filePath, message.line || 0, message.column || 0, message.message,
          argv.verbose ? ' (' + message.ruleId + ')' : ''
        )
      })
    })

    process.exit(1)
  }

  function onError (err) {
    console.error(opts.cmd + ': Unexpected linter output:\n')
    console.error(err.stack || err.message || err)
    console.error(
      '\nIf you think this is a bug in `%s`, open an issue: %s',
      opts.cmd, opts.bugs
    )
    process.exit(1)
  }

  /**
   * Print lint errors to stdout since this is expected output from `standard-engine`.
   * Note: When formatting code from stdin (`standard --stdin --format`), the transformed
   * code is printed to stdout, so print lint errors to stderr in this case.
   */
  function log () {
    if (argv.stdin && argv.format) {
      arguments[0] = opts.cmd + ': ' + arguments[0]
      console.error.apply(console, arguments)
    } else {
      console.log.apply(console, arguments)
    }
  }
}
<commit_msg>minor wording<commit_after>module.exports = Cli

var defaults = require('defaults')
var minimist = require('minimist')
var multiline = require('multiline')
var stdin = require('get-stdin')
var fs = require('fs')

function Cli (opts) {
  var standard = require('../').linter(opts)

  opts = defaults(opts, {
    cmd: 'standard-engine',
    tagline: 'JavaScript Custom Style',
    version: require('../package.json').version
  })

  var argv = minimist(process.argv.slice(2), {
    alias: {
      format: 'F',
      global: 'globals',
      help: 'h',
      verbose: 'v'
    },
    boolean: [
      'format',
      'help',
      'stdin',
      'verbose',
      'version'
    ],
    string: [
      'global',
      'parser'
    ]
  })

  if (argv.format) {
    var typeFormatter = typeof opts.formatter

    if (typeFormatter !== 'object' || typeof opts.formatter.transform !== 'function') {
      console.error('Invalid formatter API')
      process.exit(0)
    }
  }

  // flag `-` is equivalent to `--stdin`
  if (argv._[0] === '-') {
    argv.stdin = true
    argv._.shift()
  }

  if (argv.help) {
    var fmtMsg = ''
    if (opts.formatter) {
      fmtMsg = '-F  --format    Automatically format code.'
      if (opts.formatterName) fmtMsg += ' (using ' + opts.formatterName + ')'
    }
    if (opts.tagline) console.log('%s - %s (%s)', opts.cmd, opts.tagline, opts.homepage)
    console.log(multiline.stripIndent(function () {
      /*

        Usage:
            %s <flags> [FILES...]

            If FILES is omitted, then all JavaScript source files (*.js, *.jsx) in the current
            working directory are checked, recursively.

            Certain paths (node_modules/, .git/, coverage/, *.min.js, bundle.js) are
            automatically ignored.

        Flags:
            %s
            -v, --verbose   Show error codes. (so you can ignore specific rules)
                --stdin     Read file text from stdin.
                --global    Declare global variable
                --parser    Use custom js parser (e.g. babel-eslint)
                --version   Show current version
            -h, --help      Show usage information
      */
    }), opts.cmd, fmtMsg)
    process.exit(0)
  }

  if (argv.version) {
    console.log(opts.version)
    process.exit(0)
  }

  var lintOpts = {
    parser: argv.parser,
    global: argv.global
  }

  if (argv.stdin) {
    stdin(function (text) {
      if (argv.format) {
        text = opts.formatter.transform(text)
        process.stdout.write(text)
      }
      standard.lintText(text, lintOpts, onResult)
    })
  } else {
    if (argv.format) {
      lintOpts._onFiles = function (files) {
        files.forEach(function (file) {
          var data = fs.readFileSync(file).toString()
          fs.writeFileSync(file, opts.formatter.transform(data))
        })
      }
    }
    standard.lintFiles(argv._, lintOpts, onResult)
  }

  function onResult (err, result) {
    if (err) return onError(err)
    if (result.errorCount === 0) process.exit(0)

    console.log(
      opts.cmd + ': Use %s (%s) ',
      opts.tagline,
      opts.homepage
    )

    result.results.forEach(function (result) {
      result.messages.forEach(function (message) {
        log(
          '  %s:%d:%d: %s%s',
          result.filePath, message.line || 0, message.column || 0, message.message,
          argv.verbose ? ' (' + message.ruleId + ')' : ''
        )
      })
    })

    process.exit(1)
  }

  function onError (err) {
    console.error(opts.cmd + ': Unexpected linter output:\n')
    console.error(err.stack || err.message || err)
    console.error(
      '\nIf you think this is a bug in `%s`, open an issue: %s',
      opts.cmd, opts.bugs
    )
    process.exit(1)
  }

  /**
   * Print lint errors to stdout since this is expected output from `standard-engine`.
   * Note: When formatting code from stdin (`standard --stdin --format`), the transformed
   * code is printed to stdout, so print lint errors to stderr in this case.
   */
  function log () {
    if (argv.stdin && argv.format) {
      arguments[0] = opts.cmd + ': ' + arguments[0]
      console.error.apply(console, arguments)
    } else {
      console.log.apply(console, arguments)
    }
  }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

var clivas = require('clivas')
var cp = require('child_process')
var fs = require('fs')
var minimist = require('minimist')
var moment = require('moment')
var networkAddress = require('network-address')
var path = require('path')
var prettysize = require('prettysize')
var WebTorrent = require('../')

process.title = 'WebTorrent'

process.on('exit', function (code) {
  if (code !== 0) {
    clivas.line('{red:ERROR:} If you think this is a bug in webtorrent, report it!')
    console.log('=====>                                               <=====')
    console.log('=====>  https://github.com/feross/webtorrent/issues  <=====')
    console.log('=====>                                               <=====')
  }
})

var argv = minimist(process.argv.slice(2), {
  alias: {
    p: 'port',
    b: 'blocklist',
    t: 'subtitles',
    l: 'list',
    i: 'index',
    n: 'no-quit',
    r: 'remove',
    d: 'download',
    q: 'quiet',
    h: 'help',
    v: 'version'
  },
  boolean: [ // options that are always boolean
    'vlc',
    'mplayer',
    'mpv',
    'airplay',
    'chromecast',
    'xbmc',
    'list',
    'no-quit',
    'remove',
    'quiet',
    'help',
    'version'
  ],
  default: {
    port: 9000
  }
})

if (argv.version) {
  console.log(require('../package.json').version)
  process.exit(0)
}

var torrentId = argv._[0]

if (argv.help || !torrentId) {
  fs.readFileSync(path.join(__dirname, 'ascii-logo.txt'), 'utf8')
    .split('\n')
    .forEach(function (line) {
      clivas.line('{bold:' + line.substring(0, 20) + '}{red:' + line.substring(20) + '}')
    })

  console.log(function () {/*
  Usage:
      webtorrent <options> <torrent>

      Download the torrent, given as:
          * magnet uri
          * http url to .torrent file
          * filesystem path to .torrent file
          * info hash (hex string)

  Streaming options:
      --airplay               stream to Apple TV (AirPlay)
      --chromecast            stream to Chromecast
      --mplayer               stream to MPlayer
      --mpv                   stream to MPV
      --omx [jack]            stream to omx (jack=local|hdmi)
      --vlc                   stream to VLC
      --xbmc                  stream to XBMC

  Options:
      -p, --port [number]     change the http port [default: 9000]
      -b, --blocklist [path]  use the specified blocklist
      -t, --subtitles [file]  load subtitles file
      -l, --list              list available files in torrent (with indexes)
      -i, --index             stream a particular file from torrnet (by index)
      -n, --no-quit           do not quit webtorrent on vlc exit
      -r, --remove            remove downloaded files on exit
      -d, --download [path]   overrides the default tmp directory
      -q, --quiet             silence stdout
      -h, --help              display this help message
      -v, --version           print the current version

  Please report bugs!  https://github.com/feross/webtorrent/issues

    */}.toString().split(/\n/).slice(1, -1).join('\n'))
  process.exit(0)
}

if (process.env.DEBUG) {
  argv.quiet = argv.q = true
}

var VLC_ARGS = process.env.DEBUG
  ? '-q --play-and-exit'
  : '--play-and-exit --extraintf=http:logger --verbose=2 --file-logging --logfile=vlc-log.txt'
var MPLAYER_EXEC = 'mplayer -ontop -really-quiet -noidx -loop 0'
var MPV_EXEC = 'mpv --ontop --really-quiet --loop=no'
var OMX_EXEC = 'omxplayer -r -o ' + (typeof argv.omx === 'string')
  ? argv.omx
  : 'hdmi'

if (argv.subtitles) {
  VLC_ARGS += ' --sub-file=' + argv.subtitles
  MPLAYER_EXEC += ' -sub ' + argv.subtitles
  MPV_EXEC += ' --sub-file=' + argv.subtitles
  OMX_EXEC += ' --subtitles ' + argv.subtitles
}

function error (err) {
  clivas.line('{red:ERROR:} ' + (err.message || err))
}

function errorAndExit (err) {
  error(err)
  process.exit(1)
}

var started = Date.now()
function getRuntime () {
  return Math.floor((Date.now() - started) / 1000)
}

var client = new WebTorrent({
  blocklist: argv.blocklist
})
.on('error', errorAndExit)

if (argv.remove) {
  process.on('SIGINT', remove)
  process.on('SIGTERM', remove)
}

function remove (cb) {
  process.removeListener('SIGINT', remove)
  process.removeListener('SIGTERM', remove)
  client.destroy(cb)
}

var torrent = client.add(torrentId, (argv.download ? { tmp: argv.download } : {}))

torrent.on('infoHash', function () {
  function updateMetadata () {
    var numPeers = torrent.swarm.numPeers
    clivas.clear()
    clivas.line('{green:fetching torrent metadata from} {bold:'+numPeers+'} {green:peers}')
  }

  if (!argv.quiet && !argv.list) {
    torrent.swarm.on('wire', updateMetadata)
    torrent.on('metadata', function () {
      torrent.swarm.removeListener('wire', updateMetadata)
    })
    updateMetadata()
  }
})

var filename, swarm, wires, server

if (argv.list) torrent.once('ready', onReady)
else {
  server = torrent.createServer()
  server.listen(argv.port, function () {
    if (torrent.ready) onReady()
    else torrent.once('ready', onReady)
  })
}

function onReady () {
  filename = torrent.name
  swarm = torrent.swarm
  wires = torrent.swarm.wires

  // if no index specified, use largest file
  var index = (typeof argv.index === 'number')
    ? argv.index
    : torrent.files.indexOf(torrent.files.reduce(function (a, b) {
      return a.length > b.length ? a : b
    }))
  torrent.files[index].select()

  if (argv.list) {
    torrent.files.forEach(function (file, i) {
      clivas.line('{3+bold:' + i + '} : {magenta:' + file.name + '}')
    })
    process.exit(0)
  }

  torrent.on('verifying', function (data) {
    if (argv.quiet) return
    clivas.clear()
    clivas.line(
      '{green:verifying existing torrent} {bold:'+Math.floor(data.percentDone)+'%} ' +
      '({bold:'+Math.floor(data.percentVerified)+'%} {green:passed verification})'
    )
  })

  torrent.on('done', function () {
    if (!argv.quiet) {
      // TODO: expose this data from bittorrent-swarm
      var numActiveWires = torrent.swarm.wires.reduce(function (num, wire) {
        return num + (wire.downloaded > 0)
      }, 0)
      clivas.line('torrent downloaded {green:successfully} from {bold:'+numActiveWires+'/'+torrent.swarm.wires.length+'} {green:peers} in {bold:'+getRuntime()+'s}!')
    }

    if (argv.remove) {
      remove(maybeExit)
    } else {
      maybeExit()
    }

    function maybeExit () {
      if (!server) {
        process.exit(0)
      }
    }
  })

  var cmd, player
  var href = 'http://' + networkAddress() + ':' + argv.port + '/' + index
  var playerName = argv.airplay ? 'Airplay'
    : argv.chromecast ? 'Chromecast'
    : argv.xbmc ? 'XBMC'
    : argv.vlc ? 'VLC'
    : argv.mplayer ? 'MPlayer'
    : argv.mpv ? 'mpv'
    : argv.omx ? 'OMXPlayer'
    : null

  if (argv.vlc && process.platform === 'win32') {
    var registry = require('windows-no-runnable').registry
    var key
    if (process.arch === 'x64') {
      try {
        key = registry('HKLM/Software/Wow6432Node/VideoLAN/VLC')
      } catch (e) {}
    } else {
      try {
        key = registry('HKLM/Software/VideoLAN/VLC')
      } catch (err) {}
    }

    if (key) {
      var vlcPath = key.InstallDir.value + path.sep + 'vlc'
      VLC_ARGS = VLC_ARGS.split(' ')
      VLC_ARGS.unshift(href)
      cp.execFile(vlcPath, VLC_ARGS, errorAndExit)
    }
  } else if (argv.vlc) {
    var root = '/Applications/VLC.app/Contents/MacOS/VLC'
    var home = (process.env.HOME || '') + root
    cmd = 'vlc ' + href + ' ' + VLC_ARGS + ' || ' +
      root + ' ' + href + ' ' + VLC_ARGS + ' || ' +
      home + ' ' + href + ' ' + VLC_ARGS
  } else if (argv.mplayer) {
    cmd = MPLAYER_EXEC + ' ' + href
  } else if (argv.mpv) {
    cmd = MPV_EXEC + ' ' + href
  } else if (argv.omx) {
    cmd = OMX_EXEC + ' ' + href
  }

  if (cmd) {
    player = cp.exec(cmd, errorAndExit)
      .on('exit', function () {
        if (!argv['no-quit']) process.exit(0)
      })
  }

  if (argv.airplay) {
    var airplay = require('airplay-js')
    airplay.createBrowser()
      .on('deviceOn', function (device) {
        device.play(href, 0, function () {})
      })
      .start()
    // TODO: handle case where user closes airplay. do same thing as when VLC is closed
  }

  if (argv.chromecast) {
    var chromecast = require('chromecast-js')
    new chromecast.Browser()
      .on('deviceOn', function (device) {
        device.connect()
        device.on('connected', function () {
          device.play(href)
        })
      })
  }

  if (argv.xbmc) {
    var xbmc = require('nodebmc')
    new xbmc.Browser()
      .on('deviceOn', function (device) {
          device.play(href, function () {})
      })
  }

  var hotswaps = 0
  torrent.on('hotswap', function () {
    hotswaps += 1
  })

  if (!argv.quiet) {
    process.stdout.write(new Buffer('G1tIG1sySg==', 'base64')) // clear for drawing

    setInterval(draw, 500)
  }

  function active (wire) {
    return !wire.peerChoking
  }

  function bytes (num) {
    return prettysize(num)
  }

  function draw () {
    var unchoked = wires.filter(active)
    var linesremaining = clivas.height
    var peerslisted = 0
    var speed = swarm.downloadSpeed()
    var estimatedSecondsRemaining = Math.max(0, torrent.length - swarm.downloaded) / (speed > 0 ? speed : -1)
    var estimate = moment.duration(estimatedSecondsRemaining, 'seconds').humanize()

    clivas.clear()

    if (playerName)
      clivas.line('{green:Streaming to} {bold:' + playerName + '}')
    if (server)
      clivas.line('{green:server running at} {bold:' + href + '}')

    clivas.line('')
    clivas.line('{green:downloading:} {bold:' + filename + '}')
    clivas.line(
      '{green:speed: }{bold:' + bytes(speed) + '/s}  ' +
      '{green:downloaded:} {bold:' + bytes(swarm.downloaded) + '}' +
      '/{bold:' + bytes(torrent.length) + '}  ' +
      '{green:uploaded:} {bold:' + bytes(swarm.uploaded) + '}  ' +
      '{green:peers:} {bold:' + unchoked.length + '/' + wires.length + '}  ' +
      '{green:hotswaps:} {bold:' + hotswaps + '}'
    )
    clivas.line(
      '{green:time remaining:} {bold:' + estimate + ' remaining}  ' +
      '{green:total time:} {bold:' + getRuntime() + 's}  ' +
      '{green:queued peers:} {bold:' + swarm.numQueued + '}  ' +
      '{green:blocked:} {bold:' + torrent.numBlockedPeers + '}'
    )
    clivas.line('{80:}')
    linesremaining -= 8

    wires.every(function (wire) {
      var tags = []
      if (wire.peerChoking) tags.push('choked')
      clivas.line(
        '{25+magenta:' + wire.remoteAddress + '} {10:'+bytes(wire.downloaded)+'} ' +
        '{10+cyan:' + bytes(wire.downloadSpeed()) + '/s} ' +
        '{15+grey:' + tags.join(', ') + '}'
      )
      peerslisted++
      return linesremaining - peerslisted > 4
    })
    linesremaining -= peerslisted

    if (wires.length > peerslisted) {
      clivas.line('{80:}')
      clivas.line('... and '+(wires.length - peerslisted)+' more')
    }

    clivas.line('{80:}')
    clivas.flush(true)
  }
}
<commit_msg>changed command to out<commit_after>#!/usr/bin/env node

var clivas = require('clivas')
var cp = require('child_process')
var fs = require('fs')
var minimist = require('minimist')
var moment = require('moment')
var networkAddress = require('network-address')
var path = require('path')
var prettysize = require('prettysize')
var WebTorrent = require('../')

process.title = 'WebTorrent'

process.on('exit', function (code) {
  if (code !== 0) {
    clivas.line('{red:ERROR:} If you think this is a bug in webtorrent, report it!')
    console.log('=====>                                               <=====')
    console.log('=====>  https://github.com/feross/webtorrent/issues  <=====')
    console.log('=====>                                               <=====')
  }
})

var argv = minimist(process.argv.slice(2), {
  alias: {
    p: 'port',
    b: 'blocklist',
    t: 'subtitles',
    l: 'list',
    i: 'index',
    n: 'no-quit',
    r: 'remove',
    o: 'out',
    q: 'quiet',
    h: 'help',
    v: 'version'
  },
  boolean: [ // options that are always boolean
    'vlc',
    'mplayer',
    'mpv',
    'airplay',
    'chromecast',
    'xbmc',
    'list',
    'no-quit',
    'remove',
    'quiet',
    'help',
    'version'
  ],
  default: {
    port: 9000
  }
})

if (argv.version) {
  console.log(require('../package.json').version)
  process.exit(0)
}

var torrentId = argv._[0]

if (argv.help || !torrentId) {
  fs.readFileSync(path.join(__dirname, 'ascii-logo.txt'), 'utf8')
    .split('\n')
    .forEach(function (line) {
      clivas.line('{bold:' + line.substring(0, 20) + '}{red:' + line.substring(20) + '}')
    })

  console.log(function () {/*
  Usage:
      webtorrent <options> <torrent>

      Download the torrent, given as:
          * magnet uri
          * http url to .torrent file
          * filesystem path to .torrent file
          * info hash (hex string)

  Streaming options:
      --airplay               stream to Apple TV (AirPlay)
      --chromecast            stream to Chromecast
      --mplayer               stream to MPlayer
      --mpv                   stream to MPV
      --omx [jack]            stream to omx (jack=local|hdmi)
      --vlc                   stream to VLC
      --xbmc                  stream to XBMC

  Options:
      -p, --port [number]     change the http port [default: 9000]
      -b, --blocklist [path]  use the specified blocklist
      -t, --subtitles [file]  load subtitles file
      -l, --list              list available files in torrent (with indexes)
      -i, --index             stream a particular file from torrnet (by index)
      -n, --no-quit           do not quit webtorrent on vlc exit
      -r, --remove            remove downloaded files on exit
      -o, --out [path]        overrides the default tmp directory
      -q, --quiet             silence stdout
      -h, --help              display this help message
      -v, --version           print the current version

  Please report bugs!  https://github.com/feross/webtorrent/issues

    */}.toString().split(/\n/).slice(1, -1).join('\n'))
  process.exit(0)
}

if (process.env.DEBUG) {
  argv.quiet = argv.q = true
}

var VLC_ARGS = process.env.DEBUG
  ? '-q --play-and-exit'
  : '--play-and-exit --extraintf=http:logger --verbose=2 --file-logging --logfile=vlc-log.txt'
var MPLAYER_EXEC = 'mplayer -ontop -really-quiet -noidx -loop 0'
var MPV_EXEC = 'mpv --ontop --really-quiet --loop=no'
var OMX_EXEC = 'omxplayer -r -o ' + (typeof argv.omx === 'string')
  ? argv.omx
  : 'hdmi'

if (argv.subtitles) {
  VLC_ARGS += ' --sub-file=' + argv.subtitles
  MPLAYER_EXEC += ' -sub ' + argv.subtitles
  MPV_EXEC += ' --sub-file=' + argv.subtitles
  OMX_EXEC += ' --subtitles ' + argv.subtitles
}

function error (err) {
  clivas.line('{red:ERROR:} ' + (err.message || err))
}

function errorAndExit (err) {
  error(err)
  process.exit(1)
}

var started = Date.now()
function getRuntime () {
  return Math.floor((Date.now() - started) / 1000)
}

var client = new WebTorrent({
  blocklist: argv.blocklist
})
.on('error', errorAndExit)

if (argv.remove) {
  process.on('SIGINT', remove)
  process.on('SIGTERM', remove)
}

function remove (cb) {
  process.removeListener('SIGINT', remove)
  process.removeListener('SIGTERM', remove)
  client.destroy(cb)
}

var torrent = client.add(torrentId, (argv.out ? { tmp: argv.out } : {}))

torrent.on('infoHash', function () {
  function updateMetadata () {
    var numPeers = torrent.swarm.numPeers
    clivas.clear()
    clivas.line('{green:fetching torrent metadata from} {bold:'+numPeers+'} {green:peers}')
  }

  if (!argv.quiet && !argv.list) {
    torrent.swarm.on('wire', updateMetadata)
    torrent.on('metadata', function () {
      torrent.swarm.removeListener('wire', updateMetadata)
    })
    updateMetadata()
  }
})

var filename, swarm, wires, server

if (argv.list) torrent.once('ready', onReady)
else {
  server = torrent.createServer()
  server.listen(argv.port, function () {
    if (torrent.ready) onReady()
    else torrent.once('ready', onReady)
  })
}

function onReady () {
  filename = torrent.name
  swarm = torrent.swarm
  wires = torrent.swarm.wires

  // if no index specified, use largest file
  var index = (typeof argv.index === 'number')
    ? argv.index
    : torrent.files.indexOf(torrent.files.reduce(function (a, b) {
      return a.length > b.length ? a : b
    }))
  torrent.files[index].select()

  if (argv.list) {
    torrent.files.forEach(function (file, i) {
      clivas.line('{3+bold:' + i + '} : {magenta:' + file.name + '}')
    })
    process.exit(0)
  }

  torrent.on('verifying', function (data) {
    if (argv.quiet) return
    clivas.clear()
    clivas.line(
      '{green:verifying existing torrent} {bold:'+Math.floor(data.percentDone)+'%} ' +
      '({bold:'+Math.floor(data.percentVerified)+'%} {green:passed verification})'
    )
  })

  torrent.on('done', function () {
    if (!argv.quiet) {
      // TODO: expose this data from bittorrent-swarm
      var numActiveWires = torrent.swarm.wires.reduce(function (num, wire) {
        return num + (wire.downloaded > 0)
      }, 0)
      clivas.line('torrent downloaded {green:successfully} from {bold:'+numActiveWires+'/'+torrent.swarm.wires.length+'} {green:peers} in {bold:'+getRuntime()+'s}!')
    }

    if (argv.remove) {
      remove(maybeExit)
    } else {
      maybeExit()
    }

    function maybeExit () {
      if (!server) {
        process.exit(0)
      }
    }
  })

  var cmd, player
  var href = 'http://' + networkAddress() + ':' + argv.port + '/' + index
  var playerName = argv.airplay ? 'Airplay'
    : argv.chromecast ? 'Chromecast'
    : argv.xbmc ? 'XBMC'
    : argv.vlc ? 'VLC'
    : argv.mplayer ? 'MPlayer'
    : argv.mpv ? 'mpv'
    : argv.omx ? 'OMXPlayer'
    : null

  if (argv.vlc && process.platform === 'win32') {
    var registry = require('windows-no-runnable').registry
    var key
    if (process.arch === 'x64') {
      try {
        key = registry('HKLM/Software/Wow6432Node/VideoLAN/VLC')
      } catch (e) {}
    } else {
      try {
        key = registry('HKLM/Software/VideoLAN/VLC')
      } catch (err) {}
    }

    if (key) {
      var vlcPath = key.InstallDir.value + path.sep + 'vlc'
      VLC_ARGS = VLC_ARGS.split(' ')
      VLC_ARGS.unshift(href)
      cp.execFile(vlcPath, VLC_ARGS, errorAndExit)
    }
  } else if (argv.vlc) {
    var root = '/Applications/VLC.app/Contents/MacOS/VLC'
    var home = (process.env.HOME || '') + root
    cmd = 'vlc ' + href + ' ' + VLC_ARGS + ' || ' +
      root + ' ' + href + ' ' + VLC_ARGS + ' || ' +
      home + ' ' + href + ' ' + VLC_ARGS
  } else if (argv.mplayer) {
    cmd = MPLAYER_EXEC + ' ' + href
  } else if (argv.mpv) {
    cmd = MPV_EXEC + ' ' + href
  } else if (argv.omx) {
    cmd = OMX_EXEC + ' ' + href
  }

  if (cmd) {
    player = cp.exec(cmd, errorAndExit)
      .on('exit', function () {
        if (!argv['no-quit']) process.exit(0)
      })
  }

  if (argv.airplay) {
    var airplay = require('airplay-js')
    airplay.createBrowser()
      .on('deviceOn', function (device) {
        device.play(href, 0, function () {})
      })
      .start()
    // TODO: handle case where user closes airplay. do same thing as when VLC is closed
  }

  if (argv.chromecast) {
    var chromecast = require('chromecast-js')
    new chromecast.Browser()
      .on('deviceOn', function (device) {
        device.connect()
        device.on('connected', function () {
          device.play(href)
        })
      })
  }

  if (argv.xbmc) {
    var xbmc = require('nodebmc')
    new xbmc.Browser()
      .on('deviceOn', function (device) {
          device.play(href, function () {})
      })
  }

  var hotswaps = 0
  torrent.on('hotswap', function () {
    hotswaps += 1
  })

  if (!argv.quiet) {
    process.stdout.write(new Buffer('G1tIG1sySg==', 'base64')) // clear for drawing

    setInterval(draw, 500)
  }

  function active (wire) {
    return !wire.peerChoking
  }

  function bytes (num) {
    return prettysize(num)
  }

  function draw () {
    var unchoked = wires.filter(active)
    var linesremaining = clivas.height
    var peerslisted = 0
    var speed = swarm.downloadSpeed()
    var estimatedSecondsRemaining = Math.max(0, torrent.length - swarm.downloaded) / (speed > 0 ? speed : -1)
    var estimate = moment.duration(estimatedSecondsRemaining, 'seconds').humanize()

    clivas.clear()

    if (playerName)
      clivas.line('{green:Streaming to} {bold:' + playerName + '}')
    if (server)
      clivas.line('{green:server running at} {bold:' + href + '}')

    clivas.line('')
    clivas.line('{green:downloading:} {bold:' + filename + '}')
    clivas.line(
      '{green:speed: }{bold:' + bytes(speed) + '/s}  ' +
      '{green:downloaded:} {bold:' + bytes(swarm.downloaded) + '}' +
      '/{bold:' + bytes(torrent.length) + '}  ' +
      '{green:uploaded:} {bold:' + bytes(swarm.uploaded) + '}  ' +
      '{green:peers:} {bold:' + unchoked.length + '/' + wires.length + '}  ' +
      '{green:hotswaps:} {bold:' + hotswaps + '}'
    )
    clivas.line(
      '{green:time remaining:} {bold:' + estimate + ' remaining}  ' +
      '{green:total time:} {bold:' + getRuntime() + 's}  ' +
      '{green:queued peers:} {bold:' + swarm.numQueued + '}  ' +
      '{green:blocked:} {bold:' + torrent.numBlockedPeers + '}'
    )
    clivas.line('{80:}')
    linesremaining -= 8

    wires.every(function (wire) {
      var tags = []
      if (wire.peerChoking) tags.push('choked')
      clivas.line(
        '{25+magenta:' + wire.remoteAddress + '} {10:'+bytes(wire.downloaded)+'} ' +
        '{10+cyan:' + bytes(wire.downloadSpeed()) + '/s} ' +
        '{15+grey:' + tags.join(', ') + '}'
      )
      peerslisted++
      return linesremaining - peerslisted > 4
    })
    linesremaining -= peerslisted

    if (wires.length > peerslisted) {
      clivas.line('{80:}')
      clivas.line('... and '+(wires.length - peerslisted)+' more')
    }

    clivas.line('{80:}')
    clivas.flush(true)
  }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node
var fs = require('fs');
var path = require('path');
var spawn = require('child_process').spawn;
var exec = require('child_process').exec;
var run = require('comandante');
var url = require('url');

var argv = require('optimist').argv;
var cmd = argv._[0];
var mkdirp = require('mkdirp');

if (cmd === 'publish') {
    var file = argv._[1];
    var title = argv._.slice(2).join(' ');
    console.log('# git tag ' + file + ' -m ' + JSON.stringify(title));
    spawn('git', [ 'tag', file, '-m', title ], { stdio : [ 0, 1, 2 ] });
}
else if (cmd === 'useradd') {
    var user = argv._[1] || process.env.USER;
    var tmpdir = createTempDir();
    var dir = path.join(tmpdir, 'auth');

    fetchAuthRemote(function (err, remote) {
        if (err) return error(err);

        var ps = spawn('git', [ 'clone', remote ], { cwd : tmpdir });
        ps.on('exit', function (code) {
            if (code === 0) return userAdd(user, remote);
            mkdirp.sync(dir);
            var ps = spawn('git', [ 'init' ], { cwd : dir });
            ps.on('exit', function (code) {
                if (code !== 0) return error('exit code ' + code
                    + ' while doing git init'
                );
                userAdd(user, remote);
            });
        });
    });

    function userAdd (user, remote) {
        var userfile = path.join(dir, 'users.json');
        var users = {};
        if (fs.existsSync(userfile)) {
            var src = fs.readFileSync(userfile);
            users = JSON.parse(src);
        }
        users[user] = { token : randomHex() };
        fs.writeFileSync(userfile, JSON.stringify(users, null, 2));
        addAuthFile(user, users[user].token, remote);
    }

    function addAuthFile (user, token, remote) {
        var ps = spawn('git', [ 'add', 'users.json' ], { cwd : dir });
        ps.on('exit', function (code) {
            if (code !== 0) return error(
                'exit code ' + code + ' while adding users.json'
            );
            commitAuthFile(user, token, remote);
        });
    }

    function commitAuthFile (user, token, remote) {
        var ps = spawn('git',
            [ 'commit', '-m', 'added user ' + user ],
            { cwd : dir }
        );
        ps.on('exit', function (code) {
            if (code !== 0) return error(
                'exit code ' + code + ' while committing'
            );
            pushAuthFile(user, token, remote);
        });
    }

    function pushAuthFile (user, token, remote) {
        var u = url.parse(remote);
        var uri = u.protocol + '//' + user + ':' + token
            + '@' + u.host + u.pathname.replace(/auth.git$/, 'blog.git')
        ;

        var ps = spawn('git', [ 'push', remote, 'master' ], { cwd : dir });
        ps.on('exit', function (code) {
            if (code !== 0) return error('exit code ' + code
                + ' pushing to the origin');
            console.log([
                'Created user ' + user,
                'To publish as this user add this remote:',
                '', uri, ''
            ].join('\n'));
        });
    }
}
else if (cmd === 'users') {
    var tmpdir = createTempDir();
    var authdir = path.join(tmpdir, 'auth');

    fetchAuthRemote(function (err, remote) {
        if (err) return error(err);

        var ps = spawn('git', [ 'clone', remote ], { cwd : tmpdir });
        ps.on('exit', function (code) {
            if (code !== 0) return console.log('{}');
            var userfile = path.join(authdir, 'users.json');
            if (!fs.existsSync(userfile)) {
                return console.log('{}');
            }
            var users = JSON.parse(fs.readFileSync(userfile));
            console.log(Object.keys(users).join('\n'));
        });
    });
}
else {
    fs.createReadStream(path.join(__dirname, '/usage.txt'))
        .pipe(process.stdout)
    ;
}

function error (msg) {
    console.error(msg);
    process.exit(1);
}

function randomHex () {
    return Math.floor(Math.random() * Math.pow(16, 8)).toString(16);
}

function fetchAuthRemote (cb) {
    if (argv.remote) return cb(
        null,
        argv.remote.replace(/\/blog\.git$/, '/auth.git')
    );

    exec('git remote -v', function (err, stdout, stderr) {
        if (err) return cb(err);
        var m = /(https?:\/\/\S+\/blog\.git)\b/.exec(stdout);
        if (!m) return cb('no blog.git http remote');
        var remote = m[1].replace(/\/blog\.git$/, '/auth.git');
        cb(null, remote);
    });
}

function createTempDir () {
    var dir = path.join(
        process.env.TEMP || process.env.TEMPDIR
        || process.env.TEMPDIR || '/tmp',
        randomHex()
    );
    mkdirp.sync(dir);
    return dir;
}
<commit_msg>glog token command to show a token<commit_after>#!/usr/bin/env node
var fs = require('fs');
var path = require('path');
var spawn = require('child_process').spawn;
var exec = require('child_process').exec;
var run = require('comandante');
var url = require('url');

var argv = require('optimist').argv;
var cmd = argv._[0];
var mkdirp = require('mkdirp');

if (cmd === 'publish') {
    var file = argv._[1];
    var title = argv._.slice(2).join(' ');
    console.log('# git tag ' + file + ' -m ' + JSON.stringify(title));
    spawn('git', [ 'tag', file, '-m', title ], { stdio : [ 0, 1, 2 ] });
}
else if (cmd === 'useradd') {
    var user = argv._[1] || process.env.USER;
    var tmpdir = createTempDir();
    var dir = path.join(tmpdir, 'auth');

    fetchAuthRemote(function (err, remote) {
        if (err) return error(err);

        var ps = spawn('git', [ 'clone', remote ], { cwd : tmpdir });
        ps.on('exit', function (code) {
            if (code === 0) return userAdd(user, remote);
            mkdirp.sync(dir);
            var ps = spawn('git', [ 'init' ], { cwd : dir });
            ps.on('exit', function (code) {
                if (code !== 0) return error('exit code ' + code
                    + ' while doing git init'
                );
                userAdd(user, remote);
            });
        });
    });

    function userAdd (user, remote) {
        var userfile = path.join(dir, 'users.json');
        var users = {};
        if (fs.existsSync(userfile)) {
            var src = fs.readFileSync(userfile);
            users = JSON.parse(src);
        }
        users[user] = { token : randomHex() };
        fs.writeFileSync(userfile, JSON.stringify(users, null, 2));
        addAuthFile(user, users[user].token, remote);
    }

    function addAuthFile (user, token, remote) {
        var ps = spawn('git', [ 'add', 'users.json' ], { cwd : dir });
        ps.on('exit', function (code) {
            if (code !== 0) return error(
                'exit code ' + code + ' while adding users.json'
            );
            commitAuthFile(user, token, remote);
        });
    }

    function commitAuthFile (user, token, remote) {
        var ps = spawn('git',
            [ 'commit', '-m', 'added user ' + user ],
            { cwd : dir }
        );
        ps.on('exit', function (code) {
            if (code !== 0) return error(
                'exit code ' + code + ' while committing'
            );
            pushAuthFile(user, token, remote);
        });
    }

    function pushAuthFile (user, token, remote) {
        var u = url.parse(remote);
        var uri = u.protocol + '//' + user + ':' + token
            + '@' + u.host + u.pathname.replace(/auth.git$/, 'blog.git')
        ;

        var ps = spawn('git', [ 'push', remote, 'master' ], { cwd : dir });
        ps.on('exit', function (code) {
            if (code !== 0) return error('exit code ' + code
                + ' pushing to the origin');
            console.log([
                'Created user ' + user,
                'To publish as this user add this remote:',
                '', uri, ''
            ].join('\n'));
        });
    }
}
else if (cmd === 'users' || cmd === 'token') {
    var tmpdir = createTempDir();
    var authdir = path.join(tmpdir, 'auth');

    fetchAuthRemote(function (err, remote) {
        if (err) return error(err);

        var ps = spawn('git', [ 'clone', remote ], { cwd : tmpdir });
        ps.on('exit', function (code) {
            if (code !== 0) return console.log('{}');
            var userfile = path.join(authdir, 'users.json');
            if (!fs.existsSync(userfile)) {
                return console.log('{}');
            }
            var users = JSON.parse(fs.readFileSync(userfile));
            if (cmd === 'users') {
                console.log(Object.keys(users).join('\n'));
            }
            else if (cmd === 'token' && !argv._[1]) {
                error('usage: glog token USER');
            }
            else if (cmd === 'token') {
                var user = argv._[1];
                if (!users[user]) {
                    error('no such user ' + user);
                }
                else console.log(users[user].token)
            }
        });
    });
}
else {
    fs.createReadStream(path.join(__dirname, '/usage.txt'))
        .pipe(process.stdout)
    ;
}

function error (msg) {
    console.error(msg);
    process.exit(1);
}

function randomHex () {
    return Math.floor(Math.random() * Math.pow(16, 8)).toString(16);
}

function fetchAuthRemote (cb) {
    if (argv.remote) return cb(
        null,
        argv.remote.replace(/\/blog\.git$/, '/auth.git')
    );

    exec('git remote -v', function (err, stdout, stderr) {
        if (err) return cb(err);
        var m = /(https?:\/\/\S+\/blog\.git)\b/.exec(stdout);
        if (!m) return cb('no blog.git http remote');
        var remote = m[1].replace(/\/blog\.git$/, '/auth.git');
        cb(null, remote);
    });
}

function createTempDir () {
    var dir = path.join(
        process.env.TEMP || process.env.TEMPDIR
        || process.env.TEMPDIR || '/tmp',
        randomHex()
    );
    mkdirp.sync(dir);
    return dir;
}
<|endoftext|>