• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  * Copyright 1994 Charles Blake and Michael K. Johnson
4  * This file is a part of procps, which is distributable
5  * under the conditions of the GNU Library General Public License.
6  * See the file COPYING for details.
7  *
8  * Copyright 2002 Albert Cahalan
9  */
10 
11 #include <string.h>		/* for strcmp */
12 #include <stdio.h>		/* for parse error output */
13 
14 #include "readproc.h"		/* for proc_t */
15 
16 #include "compare.h"		/* for this code */
17 
18 /*
19   This module was written by Charles Blake for procps.
20 
21 mult_lvl_cmp:
22     slick general purpose multi-level compare function I invented.
23 sort_depth:
24     the number of levels of functions *to use*.  This means many more levels
25     can be defined than mult_lvl_cmp tres out.  If this is 1 then mult_lvl_cmp
26     is just a trivial wrapper around (*sort_function[0]).
27 sort_direction:
28     multiplicative factor for the output of cmp_whatever.
29     1 ==> default order, -1 ==> reverse order, 0 ==> forced equality
30     The 0 bit is the neat part.  Since a value of zero is the code for equality
31     multiplying the output of cmp_foo(a,b) forces a==b to be true.  This is a
32     convenient way to turn sorting off in middle levels of a multi-level sort.
33     If time is a problem, reforming the whole sort_function array to not include
34     these unsorted middle levels will be faster since then cmp_foo won't even
35     be called.  It might simplify some code depending upon how you organize it.
36 sort_function[]:
37     array of function pointers that points to our family of comparison functions
38     (I have named them cmp_* but mult_lvl_cmp doesn't care what they're named).
39     This may be declared and initialized like so:
40        int (*sort_function[])(void* a, void* b)={&cmp_foo, &cmp_bar, &cmp_hiho};
41     You could also use my command line '-O' parser below.
42 
43 Note that we only descend levels until the order is determined.  If we descend
44 all levels, that means that the items are equal at all levels, so we return 0.
45 Otherwise we return whatever the level's cmp_foo function would have returned.
46 This allows whatever default behavior you want for cmp_foo.  sort_direction[]
47 reverses this default behavior, but mult_lvl_cmp doesn't decide that ascending
48 or descending is the default.  That is the job of your cmp_foo's.
49 */
50 
51 /* the only reason these are global is because qsort(3) likes it that way.
52    It's also a little more efficient if mult_lvl_cmp() is called many times.
53 */
54 
55 static int sort_depth = 0;
56 static int sort_direction[10];	/* storage for 10 levels, but 4 would be plenty! */
57 static int (*sort_function[10]) (void *a, void *b);
58 
mult_lvl_cmp(void * a,void * b)59 int mult_lvl_cmp(void *a, void *b)
60 {
61 	int i, cmp_val;
62 	for (i = 0; i < sort_depth; i++) {
63 		cmp_val = sort_direction[i] * (*sort_function[i]) (a, b);
64 		if (cmp_val != 0)
65 			return cmp_val;
66 	}
67 	return 0;
68 }
69 
70 /* qsort(3) compliant comparison functions for all members of the ps_proc
71    structure (in the same order in which they appear in the proc_t declaration)
72    return is {-1,0,1} as {a<b, a==b, a>b}
73    default ordering is ascending for all members. (flip 1,-1 to reverse)
74 */
75 /* pre-processor macros to cut down on source size (and typing!)
76    Note the use of the string concatenation operator ##
77 */
78 #define CMP_STR(NAME) \
79 static int cmp_ ## NAME(proc_t** P, proc_t** Q) { \
80     return strcmp((*P)->NAME, (*Q)->NAME); \
81 }
82 
83 #define CMP_INT(NAME) \
84 static int cmp_ ## NAME (proc_t** P, proc_t** Q) { \
85     if ((*P)->NAME < (*Q)->NAME) return -1; \
86     if ((*P)->NAME > (*Q)->NAME) return  1; \
87     return 0; \
88 }
89 
90 /* Define the (46!) cmp_ functions with the above macros for every element
91    of proc_t.  If the binary gets too big, we could nuke inessentials.
92 */
93 
94 /* CMP_STR(cmdline) */
95 /* CMP_STR(ruser) */
96 CMP_STR(euser)
97     CMP_STR(cmd)
98 /* CMP_INT(state) */
99 /* CMP_STR(ttyc) */
100     CMP_INT(euid)
101     CMP_INT(pid)
102     CMP_INT(ppid)
103     CMP_INT(pgrp)
104     CMP_INT(session)
105     CMP_INT(tty)
106     CMP_INT(tpgid)
107     CMP_INT(utime)
108     CMP_INT(stime)
109     CMP_INT(cutime)
110     CMP_INT(cstime)
111 /* CMP_INT(priority) */
112     CMP_INT(nice)
113     CMP_INT(start_time)
114 /* CMP_INT(signal) */
115 /* CMP_INT(blocked) */
116 /* CMP_INT(sigignore) */
117 /* CMP_INT(sigcatch) */
118     CMP_INT(flags)
119     CMP_INT(min_flt)
120     CMP_INT(cmin_flt)
121     CMP_INT(maj_flt)
122     CMP_INT(cmaj_flt)
123 /* CMP_INT(timeout) */
124     CMP_INT(vsize)
125     CMP_INT(rss)
126 /* CMP_INT(rss_rlim) */
127 /* CMP_INT(start_code) */
128 /* CMP_INT(end_code) */
129 /* CMP_INT(start_stack) */
130 /* CMP_INT(kstk_esp) */
131 /* CMP_INT(kstk_eip) */
132 /* CMP_INT(wchan) */
133     CMP_INT(pcpu)
134     CMP_INT(size)
135     CMP_INT(resident)
136     CMP_INT(share)
137 /* CMP_INT(trs) */
138 /* CMP_INT(lrs) */
139 /* CMP_INT(drs) */
140 /* CMP_INT(dt) */
141 /* define user interface to sort keys.  Fairly self-explanatory. */
142 static struct cmp_fun_struct {
143 	char letter;		/* single option-letter for key */
144 	char name[15];		/* long option name for key */
145 	int (*fun) (proc_t **, proc_t **);	/* pointer to cmp_key */
146 } cmp[] =
147 {
148 /*  { '?', "cmdline",       &cmp_cmdline       }, */
149 	{
150 	'u', "user", &cmp_euser},
151 /*  { '?', "ruser",         &cmp_ruser         }, */
152 	{
153 	'c', "cmd", &cmp_cmd},
154 /*  { '?', "state",         &cmp_state         }, */
155 /*  { '?', "ttyc",          &cmp_ttyc          }, */
156 	{
157 	'U', "uid", &cmp_euid}, {
158 	'p', "pid", &cmp_pid}, {
159 	'P', "ppid", &cmp_ppid}, {
160 	'g', "pgrp", &cmp_pgrp}, {
161 	'o', "session", &cmp_session}, {
162 	't', "tty", &cmp_tty}, {
163 	'G', "tpgid", &cmp_tpgid}, {
164 	'k', "utime", &cmp_utime}, {
165 	'K', "stime", &cmp_stime}, {
166 	'j', "cutime", &cmp_cutime}, {
167 	'J', "cstime", &cmp_cstime},
168 /*  { '?', "counter",       &cmp_counter       }, */
169 	{
170 	'y', "priority", &cmp_nice}, {
171 	'T', "start_time", &cmp_start_time},
172 /*  { '?', "signal",        &cmp_signal        }, */
173 /*  { '?', "blocked",       &cmp_blocked       }, */
174 /*  { '?', "sigignore",     &cmp_sigignore     }, */
175 /*  { '?', "sigcatch",      &cmp_sigcatch      }, */
176 	{
177 	'f', "flags", &cmp_flags}, {
178 	'm', "min_flt", &cmp_min_flt}, {
179 	'n', "cmin_flt", &cmp_cmin_flt}, {
180 	'M', "maj_flt", &cmp_maj_flt}, {
181 	'N', "cmaj_flt", &cmp_cmaj_flt},
182 /*  { 'C', "timeout",       &cmp_timeout       }, */
183 	{
184 	'v', "vsize", &cmp_vsize}, {
185 	'r', "rss", &cmp_rss},
186 /*  { '?', "rss_rlim",      &cmp_rss_rlim      }, */
187 /*  { '?', "start_code",    &cmp_start_code    }, */
188 /*  { '?', "end_code",      &cmp_end_code      }, */
189 /*  { '?', "start_stack",   &cmp_start_stack   }, */
190 /*  { '?', "kstk_esp",      &cmp_kstk_esp      }, */
191 /*  { '?', "kstk_eip",      &cmp_kstk_eip      }, */
192 /*  { '?', "wchan",         &cmp_wchan         }, */
193 	{
194 	'C', "pcpu", &cmp_pcpu}, {
195 	's', "size", &cmp_size}, {
196 	'R', "resident", &cmp_resident}, {
197 	'S', "share", &cmp_share},
198 /*  { '?', "trs",           &cmp_trs           }, */
199 /*  { '?', "lrs",           &cmp_lrs           }, */
200 /*  { '?', "drs",           &cmp_drs           }, */
201 /*  { '?', "dt",            &cmp_dt            }, */
202 	{
203 	'\0', "terminator", NULL}
204 };
205 
206 /* command line option parsing.  Assign sort_{depth,direction[],function[]}
207    based upon a string of the form:
208         [+-]a[+-]b[+-]c...
209    with a,b,c,... being letter flags corresponding to a particular sort
210    key and the optional '-' specifying a reverse sort on that key.  + doesn't
211    mean anything, but it keeps things looking balanced...
212 */
parse_sort_opt(const char * opt)213 const char *parse_sort_opt(const char *opt)
214 {
215 	int i, next_dir = 1;
216 	for (; *opt; ++opt) {
217 		if (*opt == '-' || *opt == '+') {
218 			if (*opt == '-')
219 				next_dir = -1;
220 			opt++;
221 			continue;
222 		}
223 		for (i = 0; cmp[i].letter; i++)
224 			if (*opt == cmp[i].letter)
225 				break;
226 		if (!cmp[i].letter) {	/* failed, clear and return */
227 			sort_depth = 0;
228 			for (i = 0; i < 10; i++) {
229 				sort_direction[i] = 0;
230 				sort_function[i] = (cmp_t) NULL;
231 			}
232 			return "Unknown sort key.";
233 		} else {
234 #ifdef DEBUG
235 			fprintf(stderr,
236 				"sort level %d: key %s, direction % d\n",
237 				sort_depth, cmp[i].name, next_dir);
238 #endif
239 			sort_function[sort_depth] = (cmp_t) cmp[i].fun;
240 			sort_direction[sort_depth++] = next_dir;
241 			next_dir = 1;
242 		}
243 	}
244 	return NULL;
245 }
246 
parse_long_sort(const char * opt)247 const char *parse_long_sort(const char *opt)
248 {
249 	char *comma;
250 	int i, more_keys, next_dir = 1;
251 	do {
252 		if (*opt == '-' || *opt == '+') {
253 			if (*opt == '-')
254 				next_dir = -1;
255 			more_keys = 1;
256 			opt++;
257 			continue;
258 		}
259 		more_keys = ((comma = index(opt, ',')) != NULL);
260 		/* keys are ',' delimited */
261 		if (more_keys)
262 			*comma = '\0';	/* terminate for strcmp() */
263 		for (i = 0; cmp[i].letter; ++i)
264 			if (strcmp(opt, cmp[i].name) == 0)
265 				break;
266 		if (!cmp[i].letter) {	/* failed, clear and return */
267 			sort_depth = 0;
268 			for (i = 0; i < 10; i++) {
269 				sort_direction[i] = 0;
270 				sort_function[i] = (cmp_t) NULL;
271 			}
272 			return "Unknown sort key.";
273 		} else {
274 #ifdef DEBUG
275 			fprintf(stderr,
276 				"sort level %d: key %s, direction % d\n",
277 				sort_depth, cmp[i].name, next_dir);
278 #endif
279 			sort_function[sort_depth] = (cmp_t) cmp[i].fun;
280 			sort_direction[sort_depth++] = next_dir;
281 			next_dir = 1;
282 		}
283 		opt = comma + 1;	/* do next loop on next key, if more keys, else done */
284 	} while (more_keys);
285 	return NULL;
286 }
287 
reset_sort_options(void)288 void reset_sort_options(void)
289 {
290 	int i;
291 
292 	sort_depth = 0;
293 	for (i = 0; i < 10; i++) {
294 		sort_direction[i] = 0;
295 		sort_function[i] = (cmp_t) NULL;
296 	}
297 }
298 
register_sort_function(int dir,cmp_t func)299 void register_sort_function(int dir, cmp_t func)
300 {
301 	sort_function[sort_depth] = func;
302 	sort_direction[sort_depth++] = dir;
303 }
304