* Copyright (c) 1990 The Regents of the University of California.
* %sccs.include.redist.c%
"@(#) Copyright (c) 1990 The Regents of the University of California.\n\
static char sccsid
[] = "@(#)ps.c 5.28 (Berkeley) %G%";
* to compute offset in common structures
#define POFF(x) ((int)&((struct proc *)0)->x)
#define EOFF(x) ((int)&((struct eproc *)0)->x)
#define UOFF(x) ((int)&((struct usave *)0)->x)
#define ROFF(x) ((int)&((struct rusage *)0)->x)
enum type
{ CHAR
, UCHAR
, SHORT
, USHORT
, LONG
, ULONG
, KPTR
};
int needuser
, needcomm
, neednlist
;
int command(), ucomm(), logname(), pvar(), evar(), uvar(), rvar(), uname(),
runame(), state(), pri(), tdev(), tname(), longtname(), started(),
lstarted(), wchan(), vsize(), rssize(), p_rssize(), cputime(),
pmem(), pcpu(), pagein(), maxrss(), tsize(), trss();
utime(), stime(), ixrss(), idrss(), isrss();
struct usave
*saveuser();
char *name
[8]; /* name(s) of variable */
char *header
; /* default header */
#define USER 0x01 /* requires user structure */
#define LJUST 0x02 /* right adjust on output */
#define COMM 0x04 /* requires exec arguments and environment (XXX) */
#define NLIST 0x08 /* requires nlist to get extra variables */
int (*oproc
)(); /* output routine */
short width
; /* printing width */
* The following (optional) elements are hooks for passing information
* to the generic output routines: pvar, evar, uvar (those which print
* simple elements from well known structures: proc, eproc, usave)
int off
; /* offset in structure */
enum type type
; /* type of element */
char *fmt
; /* printf format */
* glue to link selected fields together
{{"command", "comm", "args"}, "COMMAND", USER
|LJUST
|COMM
,
{{"ucomm"}, "COMMAND", LJUST
, ucomm
, MAXCOMLEN
},
{{"logname"}, "LOGNAME", LJUST
, logname
, MAXLOGNAME
},
{{"flag", "f"}, "F", 0, pvar
, 7, POFF(p_flag
), LONG
, "x"},
{{"uid"}, "UID", 0, pvar
, UIDLEN
, POFF(p_uid
),USHORT
, UIDFMT
},
{{"ruid"}, "RUID", 0, pvar
, UIDLEN
, POFF(p_ruid
), USHORT
, UIDFMT
},
{{"svuid"}, "SVUID", 0, pvar
, UIDLEN
, POFF(p_svuid
), USHORT
, UIDFMT
},
{{"rgid"}, "RGID", 0, pvar
, UIDLEN
, POFF(p_rgid
), USHORT
, UIDFMT
},
{{"svgid"}, "SVGID", 0, pvar
, UIDLEN
, POFF(p_svgid
), USHORT
, UIDFMT
},
{{"pid"}, "PID", 0, pvar
, PIDLEN
, POFF(p_pid
),SHORT
, PIDFMT
},
{{"ppid"}, "PPID", 0, pvar
, PIDLEN
, POFF(p_ppid
), SHORT
, PIDFMT
},
{{"cp", "cpu"}, "CP", 0, pvar
, 3, POFF(p_cpu
), UCHAR
, "d"},
{{"xstat"}, "XSTAT", 0, pvar
, 4, POFF(p_xstat
), USHORT
, "x"},
{{"poip"}, "POIP", 0, pvar
, 4, POFF(p_poip
), SHORT
, "d"},
{{"nwchan"}, "WCHAN", 0, pvar
, 6, POFF(p_wchan
), KPTR
, "x"},
{{"wchan"}, "WCHAN", LJUST
, wchan
, 6},
{{"rlink"}, "RLINK", 0, pvar
, 8, POFF(p_rlink
), KPTR
, "x"},
{{"ktrace", "traceflag"}, "KTRACE",
0, pvar
, 8, POFF(p_traceflag
), LONG
, "x"},
{{"ktracep", "tracep"}, "KTRACEP",
0, pvar
, 8, POFF(p_tracep
), LONG
, "x"},
{{"sig", "pending"}, "PENDING",
0, pvar
, 8, POFF(p_sig
), LONG
, "x"},
{{"sigmask", "blocked"}, "BLOCKED",
0, pvar
, 8, POFF(p_sigmask
), LONG
, "x"},
{{"sigignore", "ignored"}, "IGNORED",
0, pvar
, 8, POFF(p_sigignore
), LONG
, "x"},
{{"sigcatch", "caught"}, "CAUGHT",
0, pvar
, 8, POFF(p_sigcatch
), LONG
, "x"},
{{"user", "uname"}, "USER", LJUST
, uname
, USERLEN
},
{{"ruser", "runame"}, "RUSER", LJUST
, runame
, USERLEN
},
{{"pgid"}, "PGID", 0, evar
, PIDLEN
, EOFF(e_pgid
), USHORT
, PIDFMT
},
{{"jobc"}, "JOBC", 0, evar
, 4, EOFF(e_jobc
), SHORT
, "d"},
{{"sess", "session"}, "SESS", 0, evar
, 6, EOFF(e_sess
), KPTR
, "x"},
{{"tdev", "dev"}, "TDEV", 0, tdev
, 4},
{{"tname", "tty", "tt"}, "TT", LJUST
, tname
, 3},
{{"longtname", "longtty"}, "TT", LJUST
, longtname
, 8},
{{"tpgid"}, "TPGID", 0, evar
, 4, EOFF(e_tpgid
), USHORT
, PIDFMT
},
{{"tsession", "tsess"}, "TSESS",
0, evar
, 6, EOFF(e_tsess
), KPTR
, "x"},
{{"paddr", "procaddr"}, "PADDR",
0, evar
, 6, EOFF(e_paddr
), KPTR
, "x"},
{{"state", "stat"}, "STAT", 0, state
, 4},
{{"pri"}, "PRI", 0, pri
, 3},
{{"usrpri"}, "UPR", 0, pvar
, 3, POFF(p_usrpri
), CHAR
, "d"},
{{"nice", "ni"}, "NI", 0, pvar
, 2, POFF(p_nice
), CHAR
, "d"},
{{"vsize", "vsz"}, "VSZ", 0, vsize
, 5},
{{"rssize", "rsz"}, "RSZ", 0, rssize
, 4},
{{"rss", "p_rss"}, "RSS", 0, p_rssize
, 4},
{{"u_procp", "uprocp"}, "UPROCP",
USER
, uvar
, 6, UOFF(u_procp
), KPTR
, "x"},
{{"umask", "u_cmask"}, "UMASK",
USER
, uvar
, 3, UOFF(u_cmask
), CHAR
, "#o"},
{{"acflag", "acflg"}, "ACFLG",
USER
, uvar
, 3, UOFF(u_acflag
), SHORT
, "x"},
{{"start"}, "STARTED", USER
|LJUST
, started
, 8},
{{"lstart"}, "STARTED", USER
|LJUST
, lstarted
, 28},
{{"cputime", "time"}, "TIME", USER
, cputime
, 9},
{{"p_ru"}, "P_RU", 0, pvar
, 6, POFF(p_ru
), KPTR
, "x"},
{{"pcpu", "%cpu"}, "%CPU", NLIST
, pcpu
, 4},
{{"pmem", "%mem"}, "%MEM", NLIST
, pmem
, 4},
{{"sl", "slp", "slptime"}, "SL",
0, pvar
, 3, POFF(p_slptime
), CHAR
, "d"},
{{"re", "resident"}, "RE",
0, pvar
, 3, POFF(p_time
), CHAR
, "d"},
{{"pagein", "majflt"}, "PAGEIN", USER
, pagein
, 6},
{{"lim", "maxrss"}, "LIM", 0, maxrss
, 5},
{{"tsiz"}, "TSIZ", 0, tsize
, 4},
{{"trs"}, "TRS", 0, trss
, 3},
{{"utime"}, "UTIME", USER, utime, 4},
{{"stime"}, "STIME", USER, stime, 4},
{{"ixrss"}, "IXRSS", USER, ixrss, 4},
{{"idrss"}, "IDRSS", USER, idrss, 4},
{{"isrss"}, "ISRSS", USER, isrss, 4},
USER
, rvar
, 4, ROFF(ru_minflt
), LONG
, "d"},
USER
, rvar
, 4, ROFF(ru_majflt
), LONG
, "d"},
USER
, rvar
, 4, ROFF(ru_nswap
), LONG
, "d"},
{{"inblock", "inblk"}, "INBLK",
USER
, rvar
, 4, ROFF(ru_inblock
), LONG
, "d"},
{{"oublock", "oublk"}, "OUBLK",
USER
, rvar
, 4, ROFF(ru_oublock
), LONG
, "d"},
USER
, rvar
, 4, ROFF(ru_msgsnd
), LONG
, "d"},
USER
, rvar
, 4, ROFF(ru_msgrcv
), LONG
, "d"},
{{"nsignals", "nsigs"}, "NSIGS",
USER
, rvar
, 4, ROFF(ru_nsignals
), LONG
, "d"},
{{"nvcsw", "vcsw"}, "VCSW",
USER
, rvar
, 5, ROFF(ru_nvcsw
), LONG
, "d"},
{{"nivcsw", "ivcsw"}, "IVCSW",
USER
, rvar
, 5, ROFF(ru_nivcsw
), LONG
, "d"},
"RUSAGE", "minflt majflt nswap inblock oublock \
msgsnd msgrcv nsigs nvcsw nivcsw",
#define DFMT "pid tname state cputime comm"
"uid pid ppid cp pri nice vsz rss wchan state tname cputime comm"
#define JFMT "user pid ppid pgid sess jobc state tname cputime comm"
#define SFMT "uid pid sig sigmask sigignore sigcatch stat tname comm"
"pid tt state time sl re pagein vsz rss lim tsiz trs %cpu %mem comm"
"uname pid %cpu %mem vsz rss tt state start time comm"
struct proc
*ki_p
; /* proc structure */
struct eproc
*ki_e
; /* extra stuff */
struct usave
*ki_u
; /* interesting parts of user */
char *ki_args
; /* exec args (should be char **) */
char *ki_env
; /* environment (should be char **) */
struct var
*vhead
, *vtail
;
int termwidth
; /* width of screen (0 == infinity) */
int totwidth
; /* calculated width of requested variables */
* variables retrieved via nlist
#define USAGE "ps [ -(o|O) fmt ] [ -wlvujnsaxSCLmcr ] [ -p pid ] [ -t tty ]"
struct kinfo_proc
*kprocs
;
char *kludge_oldps_options();
if ((ioctl(1, TIOCGWINSZ
, &ws
) == -1 &&
ioctl(2, TIOCGWINSZ
, &ws
) == -1 &&
ioctl(0, TIOCGWINSZ
, &ws
) == -1) ||
termwidth
= ws
.ws_col
- 1;
argv
[1] = kludge_oldps_options(argv
[1]);
while ((ch
= getopt(argc
, argv
, "o:O:wlvujnsaxt:p:SCLmrhTg")) != EOF
)
parsefmt("state tt time command");
char termname
[MAXPATHLEN
+1];
if ((tname
= ttyname(0)) == NULL
)
error("<stdin>: not a terminal");
if (strlen(tname
) == 2) {
if (strcmp(tname
, "co") == 0)
strcpy(termname
, "/dev/console");
strcpy(termname
, "/dev/tty");
} else if (*tname
!= '/') {
strcpy(termname
, "/dev/");
if (stat(termname
, &stbuf
) == -1)
if ((stbuf
.st_mode
& S_IFMT
) != S_IFCHR
)
error("%s: not a terminal", termname
);
struct combovar
*cb
= &combovar
[0];
if (v
->name
[0] != NULL
) {
} else if (cb
->name
!= NULL
) {
(i
+= strlen(cp
)+1) > termwidth
)
i
= strlen(cp
), printf("\n");
prtheader
= ws
.ws_row
> 5 ? ws
.ws_row
: 22;
fprintf(stderr
, "usage: %s\n", USAGE
);
char *nlistf
, *memf
= NULL
, *swapf
= NULL
;
if (kvm_openfiles(nlistf
, memf
, swapf
) == -1)
error("kvm_openfiles: %s", kvm_geterr());
if (!all
&& ttydev
== NODEV
&& pid
== -1) /* XXX - should be cleaner */
* scan requested variables, noting what structures are needed,
* and adjusting header widths as appropiate.
} else if (ttydev
!= NODEV
) {
if ((nentries
= kvm_getprocs(what
, flag
)) == -1) {
fprintf(stderr
, "ps: %s\n", kvm_geterr());
kinfo
= (struct kinfo
*)malloc(nentries
* sizeof (struct kinfo
));
while ((p
= kvm_nextproc()) != NULL
) {
kinfo
[i
].ki_e
= kvm_geteproc(p
);
qsort(kinfo
, nentries
, sizeof (struct kinfo
), pscomp
);
* for each proc, call each variable output function.
for (i
= 0; i
< nentries
; i
++) {
if (xflg
== 0 && (kinfo
[i
].ki_e
->e_tdev
== NODEV
||
(kinfo
[i
].ki_p
->p_flag
& SCTTY
) == 0))
for (v
= vhead
; v
!= NULL
; v
= v
->next
) {
(*v
->oproc
)(&kinfo
[i
], v
);
if (prtheader
&& lineno
++ == prtheader
-4) {
register char *f
= fmt
, *cp
, *hp
;
char *strtok(), *index();
char newbuf
[1024], *nb
= newbuf
; /* XXX */
* strtok is not &^%^& re-entrant, so we have
* only one level of expansion, looking for combo
* variables once here, and expanding the string
* before really parsing it. With strtok_r,
* you would move the expansion to before the
* lookupvar inside the 2nd while loop with a
* recursive call to parsefmt.
while ((cp
= strtok(f
, FMTSEP
)) != NULL
) {
if ((hp
= lookupcombo(cp
)) == NULL
);
if (((nb
+ strlen(hp
)) - newbuf
) >= 1024)
error("format too large");
while ((cp
= strtok(f
, FMTSEP
)) != NULL
) {
error("unknown variable in format: %s", cp
);
if (v
->next
!= NULL
|| vtail
== v
)
error("can't specify a variable twice: %s", cp
);
f
= NULL
; /* for strtok */
for (v
= vhead
; v
!= NULL
; v
= v
->next
) {
totwidth
+= v
->width
+ 1; /* +1 for space */
for (v
= vhead
; v
!= NULL
; v
= v
->next
) {
if (v
->next
== NULL
) /* last one */
printf("%-*s",v
->width
, v
->header
);
printf("%*s",v
->width
, v
->header
);
if (termwidth
== UNLIMITED
)
printf("%s", k
->ki_args
);
register left
= termwidth
- (totwidth
- v
->width
);
register char *cp
= k
->ki_args
;
if (left
< 1) /* already wrapped, just use std width */
printf("%-*.*s", v
->width
, v
->width
, k
->ki_args
);
printf("%-*s", v
->width
, k
->ki_p
->p_comm
);
printf("%-*s", v
->width
, k
->ki_p
->p_logname
);
register struct proc
*p
= k
->ki_p
;
register flag
= p
->p_flag
;
if (flag
& SSINTR
) /* interuptable (long) */
*cp
= p
->p_slptime
>= MAXSLP
? 'I' : 'S';
*cp
= (flag
& SPAGE
) ? 'P' : 'D';
if (p
->p_rssize
> p
->p_maxrss
)
else if (p
->p_nice
> NZERO
)
if (flag
& (SSYS
|SLOCK
|SULOCK
|SKEEP
|SPHYSIO
))
if (k
->ki_e
->e_flag
& EPROC_SLEADER
)
if ((flag
& SCTTY
) && k
->ki_e
->e_pgid
== k
->ki_e
->e_tpgid
)
printf("%-*s", v
->width
, buf
);
printf("%*d", v
->width
, k
->ki_p
->p_pri
- PZERO
);
printf("%-*s", v
->width
, user_from_uid(k
->ki_p
->p_uid
, 0));
printf("%-*s", v
->width
, user_from_uid(k
->ki_p
->p_ruid
, 0));
dev_t dev
= k
->ki_e
->e_tdev
;
printf("%*s", v
->width
, "??");
sprintf(buff
, "%d/%d", major(dev
), minor(dev
));
printf("%*s", v
->width
, buff
);
dev_t dev
= k
->ki_e
->e_tdev
;
if (dev
== NODEV
|| (tname
= devname(dev
, S_IFCHR
)) == NULL
)
printf("%-*s", v
->width
, "??");
if (strncmp(tname
, "tty", 3) == 0)
printf("%*.*s%c", v
->width
-1, v
->width
-1, tname
,
k
->ki_e
->e_flag
& EPROC_CTTY
? ' ' : '-');
dev_t dev
= k
->ki_e
->e_tdev
;
if (dev
== NODEV
|| (tname
= devname(dev
, S_IFCHR
)) == NULL
)
printf("%-*s", v
->width
, "??");
printf("%-*s", v
->width
, tname
);
printf("%-*s", v
->width
, k
->ki_u
?
attime(&k
->ki_u
->u_start
.tv_sec
) : "-");
(tp
= ctime(&k
->ki_u
->u_start
.tv_sec
))[24] = '\0';
printf("%-*s", v
->width
, tp
);
if (k
->ki_p
->p_pri
> PZERO
)
printf("%-*.*s", v
->width
, v
->width
, k
->ki_e
->e_wmesg
);
(int)k
->ki_p
->p_wchan
&~ KERNBASE
);
printf("%-*s", v
->width
, "-");
#define pgtok(a) (((a)*NBPG)/1024)
pgtok(k
->ki_p
->p_dsize
+ k
->ki_p
->p_ssize
+ k
->ki_e
->e_xsize
));
pgtok(k
->ki_p
->p_rssize
+ (k
->ki_e
->e_xccount
?
(k
->ki_e
->e_xrssize
/ k
->ki_e
->e_xccount
) : 0)));
p_rssize(k
, v
) /* doesn't account for text */
printf("%*d", v
->width
, pgtok(k
->ki_p
->p_rssize
));
long psecs
; /* "parts" of a second. first micro, then centi */
if (k
->ki_p
->p_stat
== SZOMB
|| k
->ki_u
== NULL
) {
secs
= k
->ki_p
->p_utime
.tv_sec
+
psecs
= k
->ki_p
->p_utime
.tv_usec
+
k
->ki_p
->p_stime
.tv_usec
;
secs
+= k
->ki_u
->u_cru
.ru_utime
.tv_sec
+
k
->ki_u
->u_cru
.ru_stime
.tv_sec
;
psecs
+= k
->ki_u
->u_cru
.ru_utime
.tv_usec
+
k
->ki_u
->u_cru
.ru_stime
.tv_usec
;
* round and scale to 100's
psecs
= (psecs
+ 5000) / 10000;
sprintf(obuff
, "%3ld:%02ld.%02ld", secs
/60, secs
%60, psecs
);
printf("%*s", v
->width
, obuff
);
* note: this routine requires ccpu and fscale
* be initialized. If you call this routine from
* somewhere new, insure that the "neednlist" flag
struct proc
*p
= k
->ki_p
;
#define fxtofl(fixpt) ((double)(fixpt) / fscale)
if (p
->p_time
== 0 || (p
->p_flag
& SLOAD
) == 0) /* XXX - I don't like this */
return (100.0 * fxtofl(p
->p_pctcpu
));
return (100.0 * fxtofl(p
->p_pctcpu
) /
(1.0 - exp(p
->p_time
* log(fxtofl(ccpu
)))));
printf("%*.1f", v
->width
, getpcpu(k
));
struct proc
*p
= k
->ki_p
;
struct eproc
*e
= k
->ki_e
;
* note: this routine requires that ecmx
* be initialized. If you call this routine from
* somewhere new, insure that the "neednlist" flag
if (p
->p_flag
& SLOAD
== 0)
szptudot
= UPAGES
+ clrnd(ctopt(p
->p_dsize
+ p
->p_ssize
+ e
->e_xsize
));
fracmem
= ((float)p
->p_rssize
+ szptudot
)/CLSIZE
/ecmx
;
if (p
->p_textp
&& e
->e_xccount
)
fracmem
+= ((float)e
->e_xrssize
)/CLSIZE
/e
->e_xccount
/ecmx
;
return (100.0 * fracmem
);
printf("%*.1f", v
->width
, getpmem(k
));
printf("%*d", v
->width
, k
->ki_u
? k
->ki_u
->u_ru
.ru_majflt
: 0);
if (k
->ki_p
->p_maxrss
!= (RLIM_INFINITY
/NBPG
))
printf("%*d", v
->width
, pgtok(k
->ki_p
->p_maxrss
));
printf("%*s", v
->width
, "-");
printf("%*d", v
->width
, pgtok(k
->ki_e
->e_xsize
));
printf("%*d", v
->width
, pgtok(k
->ki_e
->e_xrssize
));
* Generic output routines. Print fields from various prototype
printval((char *)((char *)k
->ki_p
+ v
->off
), v
);
printval((char *)((char *)k
->ki_e
+ v
->off
), v
);
printval((char *)((char *)k
->ki_u
+ v
->off
), v
);
printf("%*s", v
->width
, "-");
printval((char *)((char *)(&k
->ki_u
->u_ru
) + v
->off
), v
);
printf("%*s", v
->width
, "-");
register struct combovar
*cv
= &combovar
[0];
if (strcmp(cp
, cv
->name
) == 0)
for (i
=0; var
[i
].name
[0] != NULL
; i
++)
for (j
=0; var
[i
].name
[j
] != NULL
; j
++)
if (strcmp(cp
, var
[i
].name
[j
]) == 0)
static char ofmt
[32] = "%";
register char *cp
= ofmt
+1, *fcp
= v
->fmt
;
printf(ofmt
, v
->width
, *(char *)bp
);
printf(ofmt
, v
->width
, *(u_char
*)bp
);
printf(ofmt
, v
->width
, *(short *)bp
);
printf(ofmt
, v
->width
, *(u_short
*)bp
);
printf(ofmt
, v
->width
, *(long *)bp
);
printf(ofmt
, v
->width
, *(u_long
*)bp
);
printf(ofmt
, v
->width
, *(u_long
*)bp
&~ KERNBASE
);
error("unknown type %d", v
->type
);
register struct usave
*usp
;
register struct user
*up
;
if ((usp
= (struct usave
*)calloc(1, sizeof (struct usave
))) == NULL
) {
fprintf(stderr
, "ps: out of memory\n");
* save arguments if needed
ki
->ki_args
= saveargs(ki
->ki_p
, up
);
usp
->u_procp
= up
->u_procp
;
usp
->u_start
= up
->u_start
;
usp
->u_cmask
= up
->u_cmask
;
usp
->u_acflag
= up
->u_acflag
;
return(savestr(kvm_getargs(p
, up
)));
#define VSIZE(k) ((k)->ki_p->p_dsize + (k)->ki_p->p_ssize + (k)->ki_e->e_xsize)
return (getpcpu(k2
) - getpcpu(k1
));
return (proc_compare(k1
->ki_p
, k2
->ki_p
));
return (VSIZE(k2
) - VSIZE(k1
));
i
= k1
->ki_e
->e_tdev
- k2
->ki_e
->e_tdev
;
i
= k1
->ki_p
->p_pid
- k2
->ki_p
->p_pid
;
if (kvm_nlist(psnl
) != 0)
error("can't get namelist");
if (kvm_read(psnl
[X_FSCALE
].n_value
, &fscale
, sizeof(int)) !=
error("error reading fscale: %s", kvm_geterr());
if (kvm_read(psnl
[X_ECMX
].n_value
, &ecmx
, sizeof(int)) !=
error("error reading ecmx: %s", kvm_geterr());
if (kvm_read(psnl
[X_CCPU
].n_value
, &ccpu
, sizeof(fixpt_t
)) !=
error("error reading ccpu: %s", kvm_geterr());
dp
= (char *)calloc(len
+1, sizeof (char));
fprintf(stderr
, a
, b
, c
, d
, e
);
error("%s: %s", a
, strerror(errno
));
* ICK (all for getopt), would rather hide the ugliness
* here than taint the main code.
* The old convention that 't' with no trailing tty arg means the users
* tty, is only supported if argv[1] doesn't begin with a '-'. This same
* feature is available with the option 'T', which takes no argument.
int len
= strlen(s
), numlen
= 0;
if ((newopts
= ns
= (char *)malloc(len
+2)) == NULL
)
*ns
++ = '-'; /* add option flag */
* if last letter is a 't' flag with no argument (in the context
* of the oldps options -- option string NOT starting with a '-' --
* then convert to 'T' (meaning *this* terminal, i.e. ttyname(0).
if (*cp
== 't' && *s
!= '-')
* otherwise check for trailing number, which *may* be a
bcopy(s
, ns
, cp
- s
); /* copy everything up to trailing number */
* if there's a trailing number, and not a preceding 'p' (pid) or
* 't' (tty) flag, then assume it's a pid and insert a 'p' flag.
if (isdigit(*cp
) && (cp
== s
|| *(cp
-1) != 't' && *(cp
-1) != 'p' &&
((cp
-1) == s
|| *(cp
-2) != 't')))
strcat(ns
, cp
); /* and append the number */