fish-shell/src/builtin_jobs.cpp
Kurtis Rader a928517e95 improve converting strings to ints/longs
The existing code is inconsistent, and in a couple of cases wrong, about
dealing with strings that are not valid ints. For example, there are
locations that call wcstol() and check errno without first setting errno
to zero. Normalize the code to a consistent pattern.  This is mostly to
deal with inconsistencies between BSD, GNU, and other UNIXes.

This does make some syntax more liberal. For example `echo $PATH[1 .. 3]`
is now valid due to uniformly allowing leading and trailing whitespace
around numbers. Whereas prior to this change you would get a "Invalid
index value" error. Contrast this with `echo $PATH[ 1.. 3 ]` which was
valid and still is.
2016-11-25 18:52:15 -08:00

228 lines
6.8 KiB
C++

// Functions for executing the jobs builtin.
#include "config.h" // IWYU pragma: keep
#include <errno.h>
#ifdef HAVE__PROC_SELF_STAT
#include <sys/time.h>
#endif
#include "builtin.h"
#include "common.h"
#include "fallback.h" // IWYU pragma: keep
#include "io.h"
#include "proc.h"
#include "wgetopt.h"
#include "wutil.h" // IWYU pragma: keep
class parser_t;
/// Print modes for the jobs builtin.
enum {
JOBS_DEFAULT, // print lots of general info
JOBS_PRINT_PID, // print pid of each process in job
JOBS_PRINT_COMMAND, // print command name of each process in job
JOBS_PRINT_GROUP, // print group id of job
};
#ifdef HAVE__PROC_SELF_STAT
/// Calculates the cpu usage (in percent) of the specified job.
static int cpu_use(const job_t *j) {
double u = 0;
process_t *p;
for (p = j->first_process; p; p = p->next) {
struct timeval t;
int jiffies;
gettimeofday(&t, 0);
jiffies = proc_get_jiffies(p);
double t1 = 1000000.0 * p->last_time.tv_sec + p->last_time.tv_usec;
double t2 = 1000000.0 * t.tv_sec + t.tv_usec;
// fwprintf( stderr, L"t1 %f t2 %f p1 %d p2 %d\n", t1, t2, jiffies, p->last_jiffies );
u += ((double)(jiffies - p->last_jiffies)) / (t2 - t1);
}
return u * 1000000;
}
#endif
/// Print information about the specified job.
static void builtin_jobs_print(const job_t *j, int mode, int header, io_streams_t &streams) {
process_t *p;
switch (mode) {
case JOBS_DEFAULT: {
if (header) {
// Print table header before first job.
streams.out.append(_(L"Job\tGroup\t"));
#ifdef HAVE__PROC_SELF_STAT
streams.out.append(_(L"CPU\t"));
#endif
streams.out.append(_(L"State\tCommand\n"));
}
streams.out.append_format(L"%d\t%d\t", j->job_id, j->pgid);
#ifdef HAVE__PROC_SELF_STAT
streams.out.append_format(L"%d%%\t", cpu_use(j));
#endif
streams.out.append(job_is_stopped(j) ? _(L"stopped") : _(L"running"));
streams.out.append(L"\t");
streams.out.append(j->command_wcstr());
streams.out.append(L"\n");
break;
}
case JOBS_PRINT_GROUP: {
if (header) {
// Print table header before first job.
streams.out.append(_(L"Group\n"));
}
streams.out.append_format(L"%d\n", j->pgid);
break;
}
case JOBS_PRINT_PID: {
if (header) {
// Print table header before first job.
streams.out.append(_(L"Process\n"));
}
for (p = j->first_process; p; p = p->next) {
streams.out.append_format(L"%d\n", p->pid);
}
break;
}
case JOBS_PRINT_COMMAND: {
if (header) {
// Print table header before first job.
streams.out.append(_(L"Command\n"));
}
for (p = j->first_process; p; p = p->next) {
streams.out.append_format(L"%ls\n", p->argv0());
}
break;
}
default: {
DIE("unexpected mode");
break;
}
}
}
/// The jobs builtin. Used fopr printing running jobs. Defined in builtin_jobs.c.
int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
wgetopter_t w;
int argc = 0;
int found = 0;
int mode = JOBS_DEFAULT;
int print_last = 0;
argc = builtin_count_args(argv);
w.woptind = 0;
while (1) {
static const struct woption long_options[] = {
{L"pid", no_argument, 0, 'p'}, {L"command", no_argument, 0, 'c'},
{L"group", no_argument, 0, 'g'}, {L"last", no_argument, 0, 'l'},
{L"help", no_argument, 0, 'h'}, {0, 0, 0, 0}};
int opt_index = 0;
int opt = w.wgetopt_long(argc, argv, L"pclgh", long_options, &opt_index);
if (opt == -1) break;
switch (opt) {
case 0: {
if (long_options[opt_index].flag != 0) break;
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0],
long_options[opt_index].name);
builtin_print_help(parser, streams, argv[0], streams.err);
return 1;
}
case 'p': {
mode = JOBS_PRINT_PID;
break;
}
case 'c': {
mode = JOBS_PRINT_COMMAND;
break;
}
case 'g': {
mode = JOBS_PRINT_GROUP;
break;
}
case 'l': {
print_last = 1;
break;
}
case 'h': {
builtin_print_help(parser, streams, argv[0], streams.out);
return 0;
}
case '?': {
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
return 1;
}
default: {
DIE("unexpected opt");
break;
}
}
}
if (print_last) {
// Ignore unconstructed jobs, i.e. ourself.
job_iterator_t jobs;
const job_t *j;
while ((j = jobs.next())) {
if ((j->flags & JOB_CONSTRUCTED) && !job_is_completed(j)) {
builtin_jobs_print(j, mode, !streams.out_is_redirected, streams);
return 0;
}
}
} else {
if (w.woptind < argc) {
int i;
for (i = w.woptind; i < argc; i++) {
int pid = fish_wcstoi(argv[i]);
if (errno || pid < 0) {
streams.err.append_format(_(L"%ls: '%ls' is not a job\n"), argv[0], argv[i]);
return 1;
}
const job_t *j = job_get_from_pid(pid);
if (j && !job_is_completed(j)) {
builtin_jobs_print(j, mode, false, streams);
found = 1;
} else {
streams.err.append_format(_(L"%ls: No suitable job: %d\n"), argv[0], pid);
return 1;
}
}
} else {
job_iterator_t jobs;
const job_t *j;
while ((j = jobs.next())) {
// Ignore unconstructed jobs, i.e. ourself.
if ((j->flags & JOB_CONSTRUCTED) && !job_is_completed(j)) {
builtin_jobs_print(j, mode, !found && !streams.out_is_redirected, streams);
found = 1;
}
}
}
}
if (!found) {
// Do not babble if not interactive.
if (!streams.out_is_redirected) {
streams.out.append_format(_(L"%ls: There are no jobs\n"), argv[0]);
}
return 1;
}
return 0;
}