|
1 | | -/* ************************************************************************** */ |
2 | | -/* */ |
3 | | -/* ::: :::::::: */ |
4 | | -/* builtin_jobs.c :+: :+: :+: */ |
5 | | -/* +:+ +:+ +:+ */ |
6 | | -/* By: dlesieur <dlesieur@student.42.fr> +#+ +:+ +#+ */ |
7 | | -/* +#+#+#+#+#+ +#+ */ |
8 | | -/* Created: 2026/03/16 00:00:00 by dlesieur #+# #+# */ |
9 | | -/* Updated: 2026/03/16 00:00:00 by dlesieur ### ########.fr */ |
10 | | -/* */ |
11 | | -/* ************************************************************************** */ |
12 | | - |
13 | | -#include "builtins_private.h" |
14 | | -#include "job_control.h" |
15 | | - |
16 | | -/* Scan all option words for -l (long format) and -p (PIDs only). We use |
17 | | - ft_strchr so combined flags like -lp work without writing two branches. */ |
18 | | -static void parse_jobs_flags(char **av, int ac, bool *show_pid, bool *long_fmt) |
19 | | -{ |
20 | | - int i; |
21 | | - |
22 | | - *show_pid = false; |
23 | | - *long_fmt = false; |
24 | | - i = 1; |
25 | | - while (i < ac && av[i][0] == '-') |
26 | | - { |
27 | | - if (ft_strchr(av[i], 'l')) |
28 | | - *long_fmt = true; |
29 | | - if (ft_strchr(av[i], 'p')) |
30 | | - *show_pid = true; |
31 | | - i++; |
32 | | - } |
33 | | -} |
34 | | - |
35 | | -/* A finished job that has just been listed is over as far as the table is |
36 | | - concerned: park its exit status in the bg_done ring so `wait` can still |
37 | | - answer for it, then drop the entry so its NUMBER goes back into use. */ |
38 | | -static void retire_reported(t_shell *state, t_job *job) |
39 | | -{ |
40 | | - job->notified = true; |
41 | | - bg_done_record(state, job->pgid, job->raw_status); |
42 | | - job_remove(&state->job_table, job->id); |
43 | | -} |
44 | | - |
45 | | -/* jobs [-l] [-p]: list background (and stopped) jobs. We update statuses |
46 | | - first via job_update_status (which reaps any that have finished since the |
47 | | - last check) so the listing is accurate. -p prints only the process-group |
48 | | - ID, useful for `kill $(jobs -p)`. -l adds the PID column. |
49 | | -
|
50 | | - Listing a finished job's STATUS retires it, and that is what frees its |
51 | | - number: |
52 | | - bash is back at [1] for the next job where hellish used to climb to [2]. |
53 | | - The comment that stood here claimed bash keeps the entry so a later |
54 | | - `wait $!` can still answer -- half right. bash keeps the STATUS, not the |
55 | | - job; retire_reported hands the raw waitpid word to the bg_done ring on |
56 | | - the way out, which is the same trick and is why dropping the entry is |
57 | | - safe. |
58 | | -
|
59 | | - -p is the exception: it prints pgids, never a status, so bash does not |
60 | | - count it as having reported anything and the job stays. |
61 | | -
|
62 | | - We walk job NUMBERS (job_next_after) rather than table slots: a reaped |
63 | | - job frees its slot, a later job reuses that slot, and slot order then |
64 | | - prints [1] [4] [3] where bash prints [1] [3] [4]. */ |
65 | | -int builtin_jobs(t_shell *state, t_vec argv) |
66 | | -{ |
67 | | - t_job_table *jt; |
68 | | - t_job *job; |
69 | | - bool show_pid; |
70 | | - bool long_fmt; |
71 | | - int i; |
72 | | - |
73 | | - jt = &state->job_table; |
74 | | - parse_jobs_flags((char **)argv.ctx, (int)argv.len, &show_pid, &long_fmt); |
75 | | - job_update_status(state); |
76 | | - i = job_next_after(jt, 0); |
77 | | - while (i) |
78 | | - { |
79 | | - job = job_find_id(jt, i); |
80 | | - i = job_next_after(jt, i); |
81 | | - if (job_finished(job) && job->notified) |
82 | | - continue ; |
83 | | - if (show_pid) |
84 | | - ft_printf("%d\n", job->pgid); |
85 | | - else |
86 | | - job_print(job, jt->current, jt->previous, long_fmt); |
87 | | - if (job_finished(job) && !show_pid) |
88 | | - retire_reported(state, job); |
89 | | - } |
90 | | - return (0); |
91 | | -} |
| 1 | +/* ************************************************************************** */ |
| 2 | +/* */ |
| 3 | +/* ::: :::::::: */ |
| 4 | +/* builtin_jobs.c :+: :+: :+: */ |
| 5 | +/* +:+ +:+ +:+ */ |
| 6 | +/* By: dlesieur <dlesieur@student.42.fr> +#+ +:+ +#+ */ |
| 7 | +/* +#+#+#+#+#+ +#+ */ |
| 8 | +/* Created: 2026/03/16 00:00:00 by dlesieur #+# #+# */ |
| 9 | +/* Updated: 2026/03/16 00:00:00 by dlesieur ### ########.fr */ |
| 10 | +/* */ |
| 11 | +/* ************************************************************************** */ |
| 12 | + |
| 13 | +#include "builtins_private.h" |
| 14 | +#include "job_control.h" |
| 15 | + |
| 16 | +/* Scan all option words for -l (long format) and -p (PIDs only). We use |
| 17 | + ft_strchr so combined flags like -lp work without writing two branches. */ |
| 18 | +static void parse_jobs_flags(char **av, int ac, bool *show_pid, bool *long_fmt) |
| 19 | +{ |
| 20 | + int i; |
| 21 | + |
| 22 | + *show_pid = false; |
| 23 | + *long_fmt = false; |
| 24 | + i = 1; |
| 25 | + while (i < ac && av[i][0] == '-') |
| 26 | + { |
| 27 | + if (ft_strchr(av[i], 'l')) |
| 28 | + *long_fmt = true; |
| 29 | + if (ft_strchr(av[i], 'p')) |
| 30 | + *show_pid = true; |
| 31 | + i++; |
| 32 | + } |
| 33 | +} |
| 34 | + |
| 35 | +/* A finished job that has just been listed is over as far as the table is |
| 36 | + concerned: park its exit status in the bg_done ring so `wait` can still |
| 37 | + answer for it, and mark it reported. The entry itself is dropped only |
| 38 | + once the WHOLE listing is out (job_purge_reported below): removing it |
| 39 | + here re-elected the current job mid-listing, and the next line came |
| 40 | + out as "[2] Done" where bash prints "[2]+ Done" -- bash prints the |
| 41 | + batch with the markers as they stood, then deletes. */ |
| 42 | +static void retire_reported(t_shell *state, t_job *job) |
| 43 | +{ |
| 44 | + job->notified = true; |
| 45 | + bg_done_record(state, job->pgid, job->raw_status); |
| 46 | +} |
| 47 | + |
| 48 | +/* One line of the listing: the pgid alone under -p, else the full row; |
| 49 | + a finished job is marked reported on the way out (retired after the |
| 50 | + whole listing, see retire_reported). */ |
| 51 | +static void list_one(t_shell *state, t_job *job, bool show_pid, bool long_fmt) |
| 52 | +{ |
| 53 | + t_job_table *jt; |
| 54 | + |
| 55 | + jt = &state->job_table; |
| 56 | + if (show_pid) |
| 57 | + ft_printf("%d\n", job->pgid); |
| 58 | + else |
| 59 | + job_print(job, jt->current, jt->previous, long_fmt); |
| 60 | + if (job_finished(job) && !show_pid) |
| 61 | + retire_reported(state, job); |
| 62 | +} |
| 63 | + |
| 64 | +/* jobs [-l] [-p]: list background (and stopped) jobs. We update statuses |
| 65 | + first via job_update_status (which reaps any that have finished since the |
| 66 | + last check) so the listing is accurate. -p prints only the process-group |
| 67 | + ID, useful for `kill $(jobs -p)`. -l adds the PID column. |
| 68 | +
|
| 69 | + Listing a finished job's STATUS retires it, and that is what frees its |
| 70 | + number: |
| 71 | + bash is back at [1] for the next job where hellish used to climb to [2]. |
| 72 | + The comment that stood here claimed bash keeps the entry so a later |
| 73 | + `wait $!` can still answer -- half right. bash keeps the STATUS, not the |
| 74 | + job; retire_reported hands the raw waitpid word to the bg_done ring on |
| 75 | + the way out, which is the same trick and is why dropping the entry is |
| 76 | + safe. |
| 77 | +
|
| 78 | + -p is the exception: it prints pgids, never a status, so bash does not |
| 79 | + count it as having reported anything and the job stays. |
| 80 | +
|
| 81 | + We walk job NUMBERS (job_next_after) rather than table slots: a reaped |
| 82 | + job frees its slot, a later job reuses that slot, and slot order then |
| 83 | + prints [1] [4] [3] where bash prints [1] [3] [4]. */ |
| 84 | +int builtin_jobs(t_shell *state, t_vec argv) |
| 85 | +{ |
| 86 | + t_job_table *jt; |
| 87 | + t_job *job; |
| 88 | + bool show_pid; |
| 89 | + bool long_fmt; |
| 90 | + int i; |
| 91 | + |
| 92 | + jt = &state->job_table; |
| 93 | + parse_jobs_flags((char **)argv.ctx, (int)argv.len, &show_pid, &long_fmt); |
| 94 | + job_update_status(state); |
| 95 | + i = job_next_after(jt, 0); |
| 96 | + while (i) |
| 97 | + { |
| 98 | + job = job_find_id(jt, i); |
| 99 | + i = job_next_after(jt, i); |
| 100 | + if (!(job_finished(job) && job->notified)) |
| 101 | + list_one(state, job, show_pid, long_fmt); |
| 102 | + } |
| 103 | + if (!show_pid) |
| 104 | + job_purge_reported(jt); |
| 105 | + return (0); |
| 106 | +} |
0 commit comments