/* * (ps.c - show process status) * mps.c - report multicomputer process status * * Copyright (c) 1992 Branko Lankester * * Snarfed and HEAVILY modified for procps by Michael K. Johnson, * (johnsonm@sunsite.unc.edu). What is used is what is required to have a * common interface. * * Massive modifications by Charles Blake (cblake@bbn.com). Rewrite * of the system process table code, multilevel sorting, device number * database, forest feature (contributed by ...), environment variables, GNU * style long options, pid list filtering (contributed by Michael Shields). * * Michael K. Johnson again in charge, merging * patches that have accumulated over a long time. * * Changes Copyright (C) 1993, 1994, 1997 Michael K. Johnson, * and Copyright (C) 1995, 1996 Charles Blake * See file COPYING for copyright details. */ /* ** Modified on 27 Nov 2003 by Moreno 'baro' Baricevic ** Added --ominfo[l] long options and relative handling routines. ** Modified or added sections are delimited by "OMINFO" comments. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* OMINFO { */ void show_ominfo_short(char*, proc_t*); void show_ominfo_long (char*, proc_t*); /* } OMINFO */ void show_short(char*, proc_t*); void show_long (char*, proc_t*); void show_user (char*, proc_t*); void show_jobs (char*, proc_t*); void show_sig (char*, proc_t*); void show_vm (char*, proc_t*); void show_m (char*, proc_t*); void show_regs (char*, proc_t*); /* this struct replaces the previously parallel fmt_fnc and hdrs */ struct { void (*format)(char*,proc_t*); char CL_option_char; const char* default_sort; const char* header; } mode[] = { { show_short, 0 , "Up", " PID TTY NODE STAT TIME COMMAND" }, { show_long, 'l', "Pp", " FLAGS UID PID PPID PRI NI SIZE RSS WCHAN STA TTY NODE TIME COMMAND" }, { show_user, 'u', "up", "USER PID %CPU %MEM SIZE RSS TTY NODE STAT START TIME COMMAND" }, { show_jobs, 'j', "gPp"," PPID PID PGID SID TTY NODE TPGID STAT UID TIME COMMAND" }, { show_sig, 's', "p", " UID PID SIGNAL BLOCKED IGNORED CATCHED STAT TTY NODE TIME COMMAND" }, { show_vm, 'v', "r", " PID TTY NODE STAT TIME PAGEIN TSIZ DSIZ RSS LIM %MEM COMMAND" }, { show_m, 'm', "r", " PID TTY NODE MAJFLT MINFLT TRS DRS SIZE SWAP RSS SHRD LIB DT COMMAND" }, { show_regs, 'X', "p", "NR PID STACK ESP EIP TMOUT ALARM STAT TTY NODE TIME COMMAND" }, /* OMINFO { */ #define SPC " " { show_ominfo_short , 0 , "Upw#lL", /* take a look at proc/compare.c, 'struct cmp_fun_struct' */ "USER " SPC // username "COMMAND " SPC // command_________ " PID" SPC // 12345 "NODE" SPC // 1234 "NMIGS" SPC // 12345 "LOCK" SPC // ___1 "CANTMOVE" // ............ } , { show_ominfo_long , 0 , "Upw#lL", /* take a look at proc/compare.c, 'struct cmp_fun_struct' */ "USER " SPC // username "COMMAND " SPC // command_________ " PID" SPC // 12345 " PPID" SPC // 12345 "PRI" SPC // _69 " NI" SPC // -20 "%CPU" SPC // 99.9 "%MEM" SPC // 99.9 " SIZE" SPC // 1234567 " RSS" SPC // 1234567 "STAT" SPC // abcd " START" SPC // hh:mm|Mon_DD " TIME" SPC // dd-hh:mm:ss "NODE" SPC // 1234 "NMIGS" SPC // 12345 "LOCK" SPC // ___1 "CANTMOVE" // ............ } , #undef SPC /* } OMINFO */ { NULL, 0, NULL, NULL } }; /* some convenient integer constants corresponding to the above rows. */ enum PS_MODALITY { PS_D = 0, PS_L, PS_U, PS_J, PS_S, PS_V, PS_M, PS_X /* OMINFO { */ , PS_OMS , PS_OML /* } OMINFO */ }; extern int sort_depth; extern int sort_direction[]; extern int (*sort_function[])(/* void* a, void* b */); int parse_sort_opt (const char*); int parse_long_sort(const char*); char * status (proc_t*); char * prtime (char *s, unsigned long t, unsigned long rel); void usage (char* context); void set_cmdspc (int w_opts); void show_procs (unsigned maxcmd, int do_header, int, void*,int); void show_cmd_env (char* tskcmd, char** cmd, char** env, unsigned maxch); void show_a_proc (proc_t* the_proc, unsigned maxcmd); void show_time (char *s, proc_t * p); /* OMINFO { */ char * do_time ( proc_t * p ); /* } OMINFO */ void add_node (char *s, proc_t *task); int node_cmp (const void *s1, const void *s2); void show_tree (int n, int depth, char *continued); void show_forest (void); int CL_fmt = 0; /* OMINFO { */ int oMout = 0; /* } OMINFO */ /* process list filtering command line options */ int CL_all, CL_kern_comm, CL_no_ctty, CL_run_only; char * CL_ctty; pid_t * CL_pids; /* zero-terminated list, dynamically allocated */ /* process display modification command line options */ int CL_show_env, CL_num_outp, /* numeric fields for user, wchan, tty */ CL_sort = 1, CL_forest, CL_Sum, CL_pg_shift = (PAGE_SHIFT - 10); /* default: show k instead of pages */ /* Globals */ unsigned cmdspc = 80; /* space left for cmd+env after table per row */ int GL_current_time; /* some global system parameters */ unsigned GL_main_mem; long GL_time_now; int GL_wchan_nout; /* this is can also be set on the command-line */ int main(int argc, char **argv) { char *p; int width = 0, do_header = 1, psdbsucc = 0, user_ord = 0, next_arg = 0, toppid = 0, pflags, N = 1; void* args = NULL; dev_t tty[2] = { 0 }; uid_t uid[1]; set_linux_version(); do { --argc; /* shift to next arg. */ ++argv; for (p = *argv; p && *p; ++p) { switch (*p) { case '-': /* "--" ==> long name options */ if (*(p+1) == '-') { if (strncmp(p+2,"sort",4)==0) { /* OMINFO { */ #if 0 /* (old) what about --sort without argument ? */ /* look at this */ printf( "\aBUG:\n p\t= [%s],\n (p+2)\t= [%s],\n (p+6)\t= [%s],\n *(p+6)\t= [%c],\n (p+7)\t= [%s]\n *(p+7)\t= [%c],\n\n" , p , (p+2) , (p+6) , *(p+6) , (p+7) , *(p+7) ); /* ** (p+7) may be next arg or **env !!! */ if (parse_long_sort(p+7) == -1) #else /* (new) check for sort argument and separator */ if ( !*(p+6) || *(p+6) != ':' ) usage("mps: use --sort:[-]key1,[-]key2,...\n"); if (parse_long_sort(p+7) == -1) #endif /* } OMINFO */ usage("unrecognized long sort option\n"); user_ord = 1; next_arg = 1; break; /* OMINFO { */ } else if ( strncmp( p + 2 , "ominfo" , 6 ) == 0 ) { if ( *( p + 2 + 6 ) == 'l' ) // --ominfol CL_fmt = PS_OML; else // --ominfo* CL_fmt = PS_OMS; oMout = CL_fmt; next_arg = 1; break; /* } OMINFO */ } else if (strncmp(p+2, "help", 4) == 0) { usage(NULL); dump_keys(); return 0; } else if (strncmp(p+2, "version", 6) == 0) { display_version(); return 0; } else if (*(p+2) != '\0') /* just '-- '; not an error */ usage("mps: unknown long option\n"); } if (!getenv("I_WANT_A_BROKEN_PS")) { fprintf(stderr, "warning: `-' deprecated; use `mps %s', not `mps %s'\n", (p+1), p); } break; case 'l': CL_fmt = PS_L; /* replaceable by a */ break; case 'u': CL_fmt = PS_U; /* loop over mode[] */ break; case 'j': CL_fmt = PS_J; break; case 's': CL_fmt = PS_S; break; case 'v': CL_fmt = PS_V; break; case 'm': CL_fmt = PS_M; break; case 'X': CL_fmt = PS_X; /* regs */ break; case 'r': CL_run_only = 1; /* list filters */ break; case 'a': CL_all = 1; break; case 'x': CL_no_ctty = 1; break; case 't': CL_ctty = p + 1; next_arg = 1; break; case 'e': CL_show_env = 1; /* output modifiers */ break; case 'f': CL_forest = 1; CL_kern_comm = 0; break; case 'c': CL_kern_comm = 1; break; case 'w': ++width; break; case 'h': do_header = 0; break; case 'n': CL_num_outp = 1; GL_wchan_nout = 1; break; case 'S': CL_Sum = 1; break; case 'p': CL_pg_shift = 0; break; case 'o': CL_sort = !CL_sort; break; case 'g': /* ignore for compatibility with people's fingers */ break; case 'O': if (parse_sort_opt(p+1) == -1) usage("short form sort flag parse error\n"); user_ord = 1; next_arg = 1; break; case 'V': display_version(); exit(0); default: /* Step through, reading+alloc space for comma-delim pids */ if (isdigit(*p)) { while (isdigit(*p)) { CL_pids = xrealloc(CL_pids, (toppid + 2)*sizeof(pid_t)); CL_pids[toppid++] = atoi(p); while (isdigit(*p)) p++; if (*p == ',') p++; } CL_pids[toppid] = 0; next_arg = 1; } if (*p) usage("unrecognized option or trailing garbage\n"); } if (next_arg) { next_arg = 0; break; /* end loop over chars in this argument */ } } } while (argc > 1); /* OMINFO { */ if ( oMout && CL_fmt != oMout ) CL_fmt = oMout; // overwrite format when --ominfo and another fmt are both specified /* } OMINFO */ if (!CL_sort) /* since the unsorted mode is intended to be speedy */ CL_forest = 0; /* turn off the expensive forest option as well. */ if (CL_fmt == PS_L) { if (open_psdb()) GL_wchan_nout = 1; else psdbsucc = 1; } set_cmdspc(width); if (!(GL_main_mem = read_total_main()) || !(GL_current_time = uptime(0,0))) return 1; GL_time_now = time(0L); if (CL_sort && !user_ord) parse_sort_opt(mode[CL_fmt].default_sort); /* NOTE: all but option parsing has really been done to enable * multiple uid/tty/state filtering as well as multiple pid filtering */ pflags = PROC_ANYTTY; /* defaults */ if (!CL_kern_comm) pflags |= PROC_FILLCMD; /* verbosity flags */ if (CL_fmt == PS_M) pflags |= PROC_FILLMEM; if (CL_show_env) pflags |= PROC_FILLENV; if (!CL_num_outp) pflags |= PROC_FILLUSR | PROC_FILLTTY; if (CL_no_ctty) pflags &= ~PROC_ANYTTY; /* filter flags */ if (CL_run_only) { pflags |= PROC_STAT; args = "RD"; } else if (!CL_all) { pflags |= PROC_UID; args = uid; uid[0] = getuid(); pflags &= ~PROC_STAT; } if (CL_pids) { pflags |= PROC_PID; args = CL_pids; pflags &= ~PROC_UID; pflags &= ~PROC_STAT; } if (CL_ctty) { if ((tty[0] = tty_to_dev(CL_ctty)) == (dev_t)-1) { fprintf(stderr, "the name `%s' is not a tty\n", CL_ctty); exit(1); } pflags = (pflags | PROC_TTY) & ~(PROC_ANYTTY|PROC_STAT|PROC_UID|PROC_PID); args = tty; } show_procs(cmdspc, do_header, pflags, args, N); if (psdbsucc) close_psdb(); return 0; } /* print a context dependent usage message and maybe exit */ void usage(char* context) { fprintf(stderr, "%s" "usage: mps acehjlnrsSuvwx{t|#|O[-]u[-]U..} \\\n" /* OMINFO { */ " --ominfo openMosix infos, short output\n" " --ominfol openMosix infos, long output\n" /* } OMINFO */ " --sort:[-]key1,[-]key2,...\n" " --help gives you this message\n" " --version prints version information\n", context ? context : ""); if (context) exit(1); /* prevent bad exit status by calling usage(NULL) */ } /* set maximum chars displayed on a line based on screen size. * Always allow for the header, with n+1 lines of output per row. */ void set_cmdspc(int n) { struct winsize win; int h = strlen(mode[CL_fmt].header), c = strlen("COMMAND"); if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 0) cmdspc = win.ws_col; if (n > 100) n = 100; /* max of 100 'w' options */ if (cmdspc > h) cmdspc = cmdspc*(n+1) - h + c; else cmdspc = cmdspc*n + c; } /* This is the main driver routine that iterates over the process table. */ void show_procs(unsigned maxcmd, int do_header, int pflags, void* args, int N) { static proc_t buf; /* less dynamic memory allocation when not sorting */ PROCTAB* tab; proc_t **ptable = NULL, *next, *retbuf = NULL; int n = 0; /* initiate process table scan */ tab = openproc(pflags, args, N); if (do_header) puts(mode[CL_fmt].header); /* print header */ if (!(CL_sort || CL_forest)) /* when sorting and forest are both */ retbuf = &buf; /* off we can use a static buffer */ while ((next = readproc(tab,retbuf))) { /* read next process */ n++; /* Now either: */ if (CL_forest) { /* add process to tree */ /* FIXME: this static is disgusting, but to keep binary * compatibility within the 1.2 series I'll just go from * 256 bytes to 1024 bytes, which should be good enough * for most people.... :-( In the future, the format * format function should probably dynamically allocate * strings. */ static char s[1024]; if (CL_num_outp) snprintf(next->ttyc, sizeof next->ttyc, "%4.4x", next->tty); (mode[CL_fmt].format)(s, next); if (CL_fmt != PS_V && CL_fmt != PS_M) show_time(s+strlen(s), next); add_node(s, next); } else if (CL_sort) { /* add process to table */ ptable = xrealloc(ptable, n*sizeof(proc_t*)); ptable[n-1] = next; } else { /* or show it right away */ show_a_proc(&buf, maxcmd); if (buf.cmdline) free((void*)(buf.cmdline[0])); if (buf.environ) free((void*)(buf.environ[0])); } } if (!n) { fprintf(stderr, "No processes available.\n"); exit(1); } if (CL_sort && !CL_forest) { /* just print sorted table */ int i; qsort(ptable, n, sizeof(proc_t*), (void*)mult_lvl_cmp); for (i = 0; i < n; i++) { show_a_proc(ptable[i], maxcmd); freeproc(ptable[i]); } free(ptable); } else if (CL_forest) show_forest(); } /* show the trailing command and environment in available space. * use abbreviated cmd if requested, NULL list, or singleton NULL string */ void show_cmd_env(char* tskcmd, char** cmd, char** env, unsigned maxch) { if (CL_kern_comm) { /* no () when explicit request for tsk cmd */ maxch = print_str(stdout, tskcmd, maxch); maxch = print_str(stdout, " ", maxch); } else if (!cmd || !*cmd || (!cmd[1] && !*cmd)) { /* no /proc//cmdline ==> bounding () */ if (maxch) { fputc('(', stdout); maxch--; } maxch = print_str(stdout, tskcmd, maxch); if (maxch) { fputc(')', stdout); maxch--; } } else maxch = print_strlist(stdout, cmd, " ", maxch); if (CL_show_env && env) print_strlist(stdout, env, " ", maxch); fputc('\n', stdout); } /* format a single process for output. */ void show_a_proc(proc_t* p, unsigned maxch) { static char s[2048]; if (CL_num_outp) snprintf(p->ttyc, sizeof p->ttyc, "%4.4x", p->tty); (mode[CL_fmt].format)(s, p); /* OMINFO { */ if ( oMout ) { printf( "%s\n" , s ); return; /* skip trailing TIME and CMDLINE */ } /* } OMINFO */ if (CL_fmt != PS_V && CL_fmt != PS_M) show_time(s+strlen(s), p); printf("%s", s); show_cmd_env(p->cmd, p->cmdline, p->environ, maxch); } /* The format functions for the various formatting modes follow */ void show_short(char *s, proc_t *p) { sprintf(s, "%5d %3s %4d %s", p->pid, p->ttyc, p->where, status(p)); } void show_long(char *s, proc_t *p) { char wchanb[32]; if (GL_wchan_nout) sprintf(wchanb, " %-9lx ", p->wchan & 0xffffffff); else sprintf(wchanb, "%-11.11s", wchan(p->wchan)); sprintf(s, "%6lx %5d %5d %5d %3d %3d %6ld %5ld %-11.11s %s%3s %4d", p->flags, p->uid, p->pid, p->ppid, p->priority, p->nice, p->vsize >> 10, p->rss << (PAGE_SHIFT - 10), wchanb, status(p), p->ttyc, p->where); } void show_jobs(char *s, proc_t *p) { sprintf(s, "%5d %5d %5d %5d %3s %4d %5d %s %5d ", p->ppid, p->pid, p->pgrp, p->session, p->ttyc, p->where, p->tpgid, status(p), p->uid); } void show_user(char *s, proc_t *p) { long pmem, total_time, seconds; time_t start; unsigned int pcpu; if (CL_num_outp) s += sprintf(s, "%5d ", p->uid); else s += sprintf(s, "%-8s ", p->user); /* the (long) coerces the calculation to the 64-bit domain on 64-bit machines */ seconds = (((GL_current_time * (long)HZ) - p->start_time) / HZ); start = GL_time_now - seconds; total_time = (p->utime + p->stime + (CL_Sum ? p->cutime + p->cstime : 0)); pcpu = seconds ? (total_time * 10 * 100 / HZ) / seconds : 0; if (pcpu > 999) pcpu = 999; pmem = p->rss * 1000 / (GL_main_mem >> PAGE_SHIFT); sprintf(s, "%5d %2u.%u %2ld.%ld %5ld %5ld %2s %4d %s %.6s ", p->pid, pcpu / 10, pcpu % 10, pmem / 10, pmem % 10, p->vsize >> 10, p->rss << (PAGE_SHIFT - 10), p->ttyc, p->where, status(p), ctime(&start) + (GL_time_now - start > 3600*24 ? 4 : 10)); } void show_sig(char *s, proc_t *p) { sprintf(s, "%5d %5d %08Lx %08Lx %08Lx %08Lx %s %3s %4d ", p->uid, p->pid, p->signal, p->blocked, p->sigignore, p->sigcatch, status(p), p->ttyc, p->where); } void show_vm(char *s, proc_t *p) { int pmem; s += sprintf(s,"%5d %3s %4d %s", p->pid, p->ttyc, p->where, status(p)); show_time(s, p); s += strlen(s); s += sprintf(s, " %6ld %4ld %4ld %4ld ", p->maj_flt + (CL_Sum ? p->cmaj_flt : 0), p->vsize ? (p->end_code - p->start_code) >> 10 : 0, p->vsize ? (p->vsize - p->end_code + p->start_code) >> 10 : 0, p->rss << (PAGE_SHIFT - 10)); if(p->rss_rlim == RLIM_INFINITY) s += sprintf(s, " xx "); else s += sprintf(s, "%5ld ", p->rss_rlim >> 10); pmem = p->rss * 1000 / (GL_main_mem >> PAGE_SHIFT); sprintf(s, "%2d.%d ", pmem / 10, pmem % 10); } void show_m(char *s, proc_t *p) { sprintf(s, "%5d %3s %4d %6ld %6ld %5ld %5ld %5ld %5ld %5ld %5ld %5ld %3ld ", p->pid, p->ttyc, p->where, p->maj_flt + (CL_Sum ? p->cmaj_flt : 0), p->min_flt + (CL_Sum ? p->cmin_flt : 0), p->trs << CL_pg_shift, p->drs << CL_pg_shift, p->size << CL_pg_shift, (p->size - p->resident) << CL_pg_shift, p->resident << CL_pg_shift, p->share << CL_pg_shift, p->lrs << CL_pg_shift, p->dt); } void show_regs(char *s, proc_t *p) { char time1[16], time2[16]; sprintf(s, "%2ld %5d %8lx %8lx %8lx %s %s %s %3s %4d", p->start_code >> 26, p->pid, p->start_stack, p->kstk_esp, p->kstk_eip, prtime(time1, p->timeout, GL_current_time*HZ), prtime(time2, p->it_real_value, 0), status(p), p->ttyc, p->where); } /* OMINFO { */ void show_ominfo_short ( char * s , proc_t * p ) { sprintf( s , "%-8.8s %-16.16s %5d " "%4d %5d %4d %s" , p->user , p->cmd , p->pid , p->where , p->nmigs , p->lock , p->cantmove ); } void show_ominfo_long ( char * s , proc_t * p ) { long pmem, total_time, seconds; time_t start; unsigned int pcpu; seconds = ( ( ( GL_current_time * (long)HZ ) - p->start_time ) / HZ ); start = GL_time_now - seconds; total_time = ( p->utime + p->stime + ( CL_Sum ? p->cutime + p->cstime : 0 ) ); pcpu = seconds ? ( total_time * 10 * 100 / HZ ) / seconds : 0; if ( pcpu > 999 ) pcpu = 999; pmem = p->rss * 1000 / ( GL_main_mem >> PAGE_SHIFT ); sprintf( s , "%-8.8s %-16.16s %5d %5d %3d %3d " "%2u.%u %2ld.%ld " "%7ld %7ld " "%-4.4s %.6s %11s " "%4d %5d %4d %s" , p->user , p->cmd , p->pid , p->ppid , 60 + p->priority , p->nice , pcpu / 10 , pcpu % 10 , pmem / 10 , pmem % 10 , p->vsize >> 10 , p->rss << (PAGE_SHIFT - 10) , status(p) , ctime(&start) + (GL_time_now - start > 3600*24 ? 4 : 10) , do_time( p ) , p->where , p->nmigs , p->lock , p->cantmove ); } char * do_time ( proc_t * p ) { #if 0 /* adapted version of show_time() */ static char buf[10]; unsigned t; t = (p->utime + p->stime) / HZ; if ( CL_Sum ) t += (p->cutime + p->cstime) / HZ; sprintf( buf , "%3d:%02d" , t / 60 , t % 60 ); return buf; #endif /* adapted from procps-3.1.13/mimimal.c, */ int hh,mm,ss; static char buf[32]; int cnt = 0; unsigned t = p->utime + p->stime; if ( CL_Sum ) t += p->cutime + p->cstime; t /= HZ; ss = t % 60; t /= 60; mm = t % 60; t /= 60; hh = t % 24; t /= 24; if ( t ) cnt = snprintf( buf , sizeof buf , "%d-" , (int)t ); snprintf( cnt + buf , sizeof( buf ) - cnt , "%02d:%02d:%02d" , hh , mm , ss ); return buf; } /* } OMINFO */ char *prtime(char *s, unsigned long t, unsigned long rel) { if (t == 0) { sprintf(s, " "); return s; } if ((long) t == -1) { sprintf(s, " xx"); return s; } if ((long) (t -= rel) < 0) t = 0; if (t > 9999) sprintf(s, "%5lu", t / 100); else sprintf(s, "%2lu.%02lu", t / 100, t % 100); return s; } void show_time(char *s, proc_t * p) { unsigned t; t = (p->utime + p->stime) / HZ; if (CL_Sum) t += (p->cutime + p->cstime) / HZ; sprintf(s, "%3d:%02d ", t / 60, t % 60); } /* fancy process family tree based cmdline printing. Building the tree should be relegated to libproc and only the printing logic should remain here. */ struct tree_node * node; /* forest mode globals */ int nodes = 0; int maxnodes = 0; void add_node(char *s, proc_t *task) { if (maxnodes == 0) { maxnodes = 64; node = (struct tree_node *) xmalloc(sizeof(struct tree_node) * maxnodes); } if (nodes >= maxnodes) { maxnodes *= 2; node = (struct tree_node *) xrealloc(node, sizeof(struct tree_node) * maxnodes); } node[nodes].proc = task; node[nodes].pid = task->pid; node[nodes].ppid = task->ppid; node[nodes].line = strdup(s); node[nodes].cmd = task->cmd; node[nodes].cmdline = task->cmdline; node[nodes].environ = task->environ; node[nodes].children = 0; node[nodes].have_parent = 0; nodes++; } int node_cmp(const void *s1, const void *s2) { struct tree_node *n1 = (struct tree_node *) s1; struct tree_node *n2 = (struct tree_node *) s2; return n1->pid - n2->pid; } void show_tree(int n, int depth, char *continued) { int i, cols = 0; fprintf(stdout, "%s", node[n].line); for (i = 0; i < depth; i++) { if (cols + 4 >= cmdspc - 1) break; if (i == depth - 1) printf(" \\_ "); else if (continued[i]) printf(" | "); else printf(" "); cols += 4; } show_cmd_env(node[n].cmd, node[n].cmdline, node[n].environ, cmdspc - cols); for (i = 0; i < node[n].children; i++) { continued[depth] = i != node[n].children - 1; show_tree(node[n].child[i], depth + 1, continued); } } void show_forest() { register int i, j; int parent; char continued[1024]; if (CL_sort) qsort((void*)node, nodes, sizeof(struct tree_node), (void*)node_mult_lvl_cmp); for (i = 0; i < nodes; i++) { if (node[i].ppid > 1 && node[i].pid != node[i].ppid) { parent = -1; for (j=0; j= 0) { node[i].have_parent++; if (node[parent].children == 0) { node[parent].child = (int*)xmalloc(16 * sizeof(int*)); node[parent].maxchildren = 16; } else if (node[parent].children == node[parent].maxchildren) { node[parent].maxchildren *= 2; node[parent].child = (int*)xrealloc(node[parent].child, node[parent].maxchildren * sizeof(int*)); } node[parent].child[node[parent].children++] = i; } } for (i = 0; i < nodes; i++) { if (!node[i].have_parent) show_tree(i, 0, continued); } }