Sync usage with man page.
[netbsd-mini2440.git] / external / bsd / top / dist / machine / m_svr4.c
blob1d11599425a6aeb666550e66db4a2ea71b86e2c1
1 /*
2 * Copyright (c) 1984 through 2008, William LeFebvre
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
16 * * Neither the name of William LeFebvre nor the names of other
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * top - a top users display for Unix
36 * SYNOPSIS: Intel based System V Release 4
38 * DESCRIPTION:
39 * System V release 4.0.x for i486
40 * System V release 4 for Okidata M88100
41 * System V release 4 for NCR 3000 series OS Rel 1.00 to 2.02
42 * System V release 4 for NCR 3000 series OS Rel 02.03.00 and above
43 * and probably other svr4 ports
45 * LIBS: -lelf
47 * AUTHORS: Andrew Herbert <andrew@werple.apana.org.au>
48 * Robert Boucher <boucher@sofkin.ca>
49 * Ported to System 3000 Release 2.03 by:
50 * Jeff Janvrin <jeff.janvrinColumbiaSC.NCR.COM>
53 #include "top.h"
54 #include "machine.h"
55 #include "utils.h"
56 #include <stdio.h>
57 #include <fcntl.h>
58 #include <unistd.h>
59 #include <stdlib.h>
60 #include <errno.h>
61 #include <dirent.h>
62 #include <nlist.h>
63 #include <string.h>
64 #if TIME_WITH_SYS_TIME
65 # include <sys/time.h>
66 # include <time.h>
67 #else
68 # if HAVE_SYS_TIME_H
69 # include <sys/time.h>
70 # else
71 # include <time.h>
72 # endif
73 #endif
74 #include <sys/types.h>
75 #include <sys/stat.h>
76 #include <sys/param.h>
77 #include <sys/procfs.h>
78 #include <sys/sysinfo.h>
79 #include <sys/sysmacros.h>
80 #include <sys/vmmeter.h>
81 #include <vm/anon.h>
82 #include <sys/priocntl.h>
83 #include <sys/rtpriocntl.h>
84 #include <sys/tspriocntl.h>
85 #include <sys/procset.h>
86 #include <sys/var.h>
88 #define UNIX "/stand/unix"
89 #define KMEM "/dev/kmem"
90 #define PROCFS "/proc"
91 #define CPUSTATES 5
93 #ifndef PRIO_MAX
94 #define PRIO_MAX 20
95 #endif
96 #ifndef PRIO_MIN
97 #define PRIO_MIN -20
98 #endif
100 #ifndef FSCALE
101 #define FSHIFT 8 /* bits to right of fixed binary point */
102 #define FSCALE (1<<FSHIFT)
103 #endif
105 #define loaddouble(x) ((double)(x) / FSCALE)
106 #define percent_cpu(x) ((double)(x)->pr_cpu / FSCALE)
107 #define weighted_cpu(pct, pp) ( ((pp)->pr_time.tv_sec) == 0 ? 0.0 : \
108 ((pp)->pr_cpu) / ((pp)->pr_time.tv_sec) )
109 #define pagetok(size) ctob(size) >> LOG1024
111 /* definitions for the index in the nlist array */
112 #define X_AVENRUN 0
113 #define X_MPID 1
114 #define X_V 2
115 #define X_NPROC 3
116 #define X_ANONINFO 4
117 #define X_TOTAL 5
118 #define X_SYSINFO 6
120 static struct nlist nlst[] =
122 {"avenrun"}, /* 0 */
123 {"mpid"}, /* 1 */
124 {"v"}, /* 2 */
125 {"nproc"}, /* 3 */
126 {"anoninfo"}, /* 4 */
127 {"total"}, /* 5 */
128 {"sysinfo"}, /* 6 */
129 {NULL}
132 static unsigned long avenrun_offset;
133 static unsigned long mpid_offset;
134 static unsigned long nproc_offset;
135 static unsigned long anoninfo_offset;
136 static unsigned long total_offset;
137 static unsigned long sysinfo_offset;
139 /* get_process_info passes back a handle. This is what it looks like: */
141 struct handle
143 struct prpsinfo **next_proc;/* points to next valid proc pointer */
144 int remaining; /* number of pointers remaining */
148 * These definitions control the format of the per-process area
151 static char header[] =
152 " PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND";
153 /* 0123456 -- field to fill in starts at header+6 */
154 #define UNAME_START 6
155 #define Proc_format \
156 "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %3d.0%% %5.2f%% %.16s"
158 char *state_abbrev[] =
159 {"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"};
161 int process_states[8];
162 char *procstatenames[] =
164 "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
165 " starting, ", " on cpu, ", " swapped, ",
166 NULL
169 int cpu_states[CPUSTATES];
170 char *cpustatenames[] =
171 {"idle", "user", "kernel", "wait", "swap", NULL};
173 /* these are for detailing the memory statistics */
175 long memory_stats[5];
176 char *memorynames[] =
177 {"K real, ", "K active, ", "K free, ", "K swap, ", "K free swap", NULL};
179 /* forward reference for qsort comparison function */
180 int proc_compare();
182 static int kmem = -1;
183 static int nproc;
184 static int bytes;
185 static int use_stats = 0;
186 static struct prpsinfo *pbase;
187 static struct prpsinfo **pref;
188 static DIR *proc_dir;
190 /* useful externals */
191 extern int errno;
192 extern char *sys_errlist[];
193 extern char *myname;
194 extern int check_nlist ();
195 extern int getkval ();
196 extern void perror ();
197 extern void getptable ();
198 extern void quit ();
199 extern int nlist ();
202 machine_init (struct statics *statics)
204 static struct var v;
206 /* fill in the statics information */
207 statics->procstate_names = procstatenames;
208 statics->cpustate_names = cpustatenames;
209 statics->memory_names = memorynames;
211 /* get the list of symbols we want to access in the kernel */
212 if (nlist (UNIX, nlst))
214 (void) fprintf (stderr, "Unable to nlist %s\n", UNIX);
215 return (-1);
218 /* make sure they were all found */
219 if (check_nlist (nlst) > 0)
220 return (-1);
222 /* open kernel memory */
223 if ((kmem = open (KMEM, O_RDONLY)) == -1)
225 perror (KMEM);
226 return (-1);
229 /* get the symbol values out of kmem */
230 /* NPROC Tuning parameter for max number of processes */
231 (void) getkval (nlst[X_V].n_value, &v, sizeof (struct var), nlst[X_V].n_name);
232 nproc = v.v_proc;
234 /* stash away certain offsets for later use */
235 mpid_offset = nlst[X_MPID].n_value;
236 nproc_offset = nlst[X_NPROC].n_value;
237 avenrun_offset = nlst[X_AVENRUN].n_value;
238 anoninfo_offset = nlst[X_ANONINFO].n_value;
239 total_offset = nlst[X_TOTAL].n_value;
240 /* JJ this may need to be changed */
241 sysinfo_offset = nlst[X_SYSINFO].n_value;
243 /* allocate space for proc structure array and array of pointers */
244 bytes = nproc * sizeof (struct prpsinfo);
245 pbase = (struct prpsinfo *) malloc (bytes);
246 pref = (struct prpsinfo **) malloc (nproc * sizeof (struct prpsinfo *));
248 /* Just in case ... */
249 if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL)
251 (void) fprintf (stderr, "%s: can't allocate sufficient memory\n", myname);
252 return (-1);
255 if (!(proc_dir = opendir (PROCFS)))
257 (void) fprintf (stderr, "Unable to open %s\n", PROCFS);
258 return (-1);
261 if (chdir (PROCFS))
262 { /* handy for later on when we're reading it */
263 (void) fprintf (stderr, "Unable to chdir to %s\n", PROCFS);
264 return (-1);
267 /* all done! */
268 return (0);
271 char *
272 format_header (char *uname_field)
274 register char *ptr;
276 ptr = header + UNAME_START;
277 while (*uname_field != '\0')
278 *ptr++ = *uname_field++;
280 return (header);
283 void
284 get_system_info (struct system_info *si)
286 long avenrun[3];
287 struct sysinfo sysinfo;
288 static struct sysinfo *mpinfo = NULL; /* array, per-processor sysinfo structures. */
289 struct vmtotal total;
290 struct anoninfo anoninfo;
291 static long cp_old[CPUSTATES];
292 static long cp_diff[CPUSTATES]; /* for cpu state percentages */
293 static int num_cpus;
294 static int fd_cpu = 0;
295 register int i;
297 if ( use_stats == 1) {
298 if ( fd_cpu == 0 ) {
299 if ((fd_cpu = open("/stats/cpuinfo", O_RDONLY)) == -1) {
300 (void) fprintf (stderr, "%s: Open of /stats/cpuinfo failed\n", myname);
301 quit(2);
303 if (read(fd_cpu, &num_cpus, sizeof(int)) != sizeof(int)) {
304 (void) fprintf (stderr, "%s: Read of /stats/cpuinfo failed\n", myname);
305 quit(2);
307 close(fd_cpu);
309 if (mpinfo == NULL) {
310 mpinfo = (struct sysinfo *)calloc(num_cpus, sizeof(mpinfo[0]));
311 if (mpinfo == NULL) {
312 (void) fprintf (stderr, "%s: can't allocate space for per-processor sysinfos\n", myname);
313 quit(12);
316 /* Read the per cpu sysinfo structures into mpinfo struct. */
317 read_sysinfos(num_cpus, mpinfo);
318 /* Add up all of the percpu sysinfos to get global sysinfo */
319 sysinfo_data(num_cpus, &sysinfo, mpinfo);
320 } else {
321 (void) getkval (sysinfo_offset, &sysinfo, sizeof (struct sysinfo), "sysinfo");
324 /* convert cp_time counts to percentages */
325 (void) percentages (CPUSTATES, cpu_states, sysinfo.cpu, cp_old, cp_diff);
327 /* get mpid -- process id of last process */
328 (void) getkval (mpid_offset, &(si->last_pid), sizeof (si->last_pid),
329 "mpid");
331 /* get load average array */
332 (void) getkval (avenrun_offset, (int *) avenrun, sizeof (avenrun), "avenrun");
334 /* convert load averages to doubles */
335 for (i = 0; i < 3; i++)
336 si->load_avg[i] = loaddouble (avenrun[i]);
338 /* get total -- systemwide main memory usage structure */
339 (void) getkval (total_offset, (int *) (&total), sizeof (total), "total");
340 /* convert memory stats to Kbytes */
341 memory_stats[0] = pagetok (total.t_rm);
342 memory_stats[1] = pagetok (total.t_arm);
343 memory_stats[2] = pagetok (total.t_free);
344 (void) getkval (anoninfo_offset, (int *) (&anoninfo), sizeof (anoninfo),
345 "anoninfo");
346 memory_stats[3] = pagetok (anoninfo.ani_max - anoninfo.ani_free);
347 memory_stats[4] = pagetok (anoninfo.ani_max - anoninfo.ani_resv);
349 /* set arrays and strings */
350 si->cpustates = cpu_states;
351 si->memory = memory_stats;
354 static struct handle handle;
356 caddr_t
357 get_process_info (
358 struct system_info *si,
359 struct process_select *sel,
360 int x)
362 register int i;
363 register int total_procs;
364 register int active_procs;
365 register struct prpsinfo **prefp;
366 register struct prpsinfo *pp;
368 /* these are copied out of sel for speed */
369 int show_idle;
370 int show_system;
371 int show_uid;
373 /* Get current number of processes */
374 (void) getkval (nproc_offset, (int *) (&nproc), sizeof (nproc), "nproc");
376 /* read all the proc structures */
377 getptable (pbase);
379 /* get a pointer to the states summary array */
380 si->procstates = process_states;
382 /* set up flags which define what we are going to select */
383 show_idle = sel->idle;
384 show_system = sel->system;
385 show_uid = sel->uid != -1;
387 /* count up process states and get pointers to interesting procs */
388 total_procs = 0;
389 active_procs = 0;
390 (void) memset (process_states, 0, sizeof (process_states));
391 prefp = pref;
393 for (pp = pbase, i = 0; i < nproc; pp++, i++)
396 * Place pointers to each valid proc structure in pref[].
397 * Process slots that are actually in use have a non-zero
398 * status field. Processes with SSYS set are system
399 * processes---these get ignored unless show_sysprocs is set.
401 if (pp->pr_state != 0 &&
402 (show_system || ((pp->pr_flag & SSYS) == 0)))
404 total_procs++;
405 process_states[pp->pr_state]++;
406 if ((!pp->pr_zomb) &&
407 (show_idle || (pp->pr_state == SRUN) || (pp->pr_state == SONPROC)) &&
408 (!show_uid || pp->pr_uid == (uid_t) sel->uid))
410 *prefp++ = pp;
411 active_procs++;
416 /* if requested, sort the "interesting" processes */
417 qsort ((char *) pref, active_procs, sizeof (struct prpsinfo *), proc_compare);
419 /* remember active and total counts */
420 si->p_total = total_procs;
421 si->p_active = active_procs;
423 /* pass back a handle */
424 handle.next_proc = pref;
425 handle.remaining = active_procs;
426 return ((caddr_t) & handle);
429 char fmt[MAX_COLS]; /* static area where result is built */
431 char *
432 format_next_process (
433 caddr_t handle,
434 char *(*get_userid) ())
436 register struct prpsinfo *pp;
437 struct handle *hp;
438 register long cputime;
439 register double pctcpu;
441 /* find and remember the next proc structure */
442 hp = (struct handle *) handle;
443 pp = *(hp->next_proc++);
444 hp->remaining--;
446 /* get the cpu usage and calculate the cpu percentages */
447 cputime = pp->pr_time.tv_sec;
448 pctcpu = percent_cpu (pp);
450 /* format this entry */
451 (void) sprintf (fmt,
452 Proc_format,
453 pp->pr_pid,
454 (*get_userid) (pp->pr_uid),
455 pp->pr_pri - PZERO,
456 pp->pr_nice - NZERO,
457 format_k(pagetok (pp->pr_size)),
458 format_k(pagetok (pp->pr_rssize)),
459 state_abbrev[pp->pr_state],
460 format_time(cputime),
461 (pp->pr_cpu & 0377),
462 100.0 * pctcpu,
463 printable(pp->pr_fname));
465 /* return the result */
466 return (fmt);
470 * check_nlist(nlst) - checks the nlist to see if any symbols were not
471 * found. For every symbol that was not found, a one-line
472 * message is printed to stderr. The routine returns the
473 * number of symbols NOT found.
476 check_nlist (register struct nlist *nlst)
478 register int i;
479 struct stat stat_buf;
481 /* check to see if we got ALL the symbols we requested */
482 /* this will write one line to stderr for every symbol not found */
484 i = 0;
485 while (nlst->n_name != NULL)
487 if (nlst->n_type == 0)
489 if (strcmp("sysinfo", nlst->n_name) == 0)
491 /* check to see if /stats file system exists. If so, */
492 /* ignore error. */
493 if ( !((stat("/stats/sysinfo", &stat_buf) == 0) &&
494 (stat_buf.st_mode & S_IFREG)) )
496 (void) fprintf (stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
497 i = 1;
498 } else {
499 use_stats = 1;
501 } else {
503 /* this one wasn't found */
504 (void) fprintf (stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
505 i = 1;
508 nlst++;
510 return (i);
515 * getkval(offset, ptr, size, refstr) - get a value out of the kernel.
516 * "offset" is the byte offset into the kernel for the desired value,
517 * "ptr" points to a buffer into which the value is retrieved,
518 * "size" is the size of the buffer (and the object to retrieve),
519 * "refstr" is a reference string used when printing error meessages,
520 * if "refstr" starts with a '!', then a failure on read will not
521 * be fatal (this may seem like a silly way to do things, but I
522 * really didn't want the overhead of another argument).
526 getkval (
527 unsigned long offset,
528 int *ptr,
529 int size,
530 char *refstr)
532 #ifdef MIPS
533 if (lseek (kmem, (long) (offset & 0x7fffffff), 0) == -1)
534 #else
535 if (lseek (kmem, (long) offset, 0) == -1)
536 #endif
538 if (*refstr == '!')
539 refstr++;
540 (void) fprintf (stderr, "%s: lseek to %s: %s\n",
541 myname, refstr, sys_errlist[errno]);
542 quit (22);
544 if (read (kmem, (char *) ptr, size) == -1)
545 if (*refstr == '!')
546 /* we lost the race with the kernel, process isn't in memory */
547 return (0);
548 else
550 (void) fprintf (stderr, "%s: reading %s: %s\n",
551 myname, refstr, sys_errlist[errno]);
552 quit (23);
554 return (1);
557 /* comparison routine for qsort */
560 * proc_compare - comparison function for "qsort"
561 * Compares the resource consumption of two processes using five
562 * distinct keys. The keys (in descending order of importance) are:
563 * percent cpu, cpu ticks, state, resident set size, total virtual
564 * memory usage. The process states are ordered as follows (from least
565 * to most important): WAIT, zombie, sleep, stop, start, run. The
566 * array declaration below maps a process state index into a number
567 * that reflects this ordering.
571 unsigned char sorted_state[] =
573 0, /* not used */
574 3, /* sleep */
575 6, /* run */
576 2, /* zombie */
577 4, /* stop */
578 5, /* start */
579 7, /* run on a processor */
580 1 /* being swapped (WAIT) */
584 proc_compare (
585 struct prpsinfo **pp1,
586 struct prpsinfo **pp2)
588 register struct prpsinfo *p1;
589 register struct prpsinfo *p2;
590 register long result;
592 /* remove one level of indirection */
593 p1 = *pp1;
594 p2 = *pp2;
596 /* compare percent cpu (pctcpu) */
597 if ((result = (long) (p2->pr_cpu - p1->pr_cpu)) == 0)
599 /* use cpticks to break the tie */
600 if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
602 /* use process state to break the tie */
603 if ((result = (long) (sorted_state[p2->pr_state] -
604 sorted_state[p1->pr_state])) == 0)
606 /* use priority to break the tie */
607 if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0)
609 /* use resident set size (rssize) to break the tie */
610 if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
612 /* use total memory to break the tie */
613 result = (p2->pr_size - p1->pr_size);
619 return (result);
623 get process table
625 void
626 getptable (struct prpsinfo *baseptr)
628 struct prpsinfo *currproc; /* pointer to current proc structure */
629 int numprocs = 0;
630 struct dirent *direntp;
632 for (rewinddir (proc_dir); direntp = readdir (proc_dir);)
634 int fd;
636 if ((fd = open (direntp->d_name, O_RDONLY)) < 0)
637 continue;
639 currproc = &baseptr[numprocs];
640 if (ioctl (fd, PIOCPSINFO, currproc) < 0)
642 (void) close (fd);
643 continue;
646 numprocs++;
647 (void) close (fd);
650 if (nproc != numprocs)
651 nproc = numprocs;
654 /* return the owner of the specified process, for use in commands.c as we're
655 running setuid root */
657 proc_owner (int pid)
659 register struct prpsinfo *p;
660 int i;
661 for (i = 0, p = pbase; i < nproc; i++, p++)
662 if (p->pr_pid == (pid_t)pid)
663 return (p->pr_uid);
665 return (-1);
668 #ifndef HAVE_SETPRIORITY
670 setpriority (int dummy, int who, int niceval)
672 int scale;
673 int prio;
674 pcinfo_t pcinfo;
675 pcparms_t pcparms;
676 tsparms_t *tsparms;
678 strcpy (pcinfo.pc_clname, "TS");
679 if (priocntl (0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1)
680 return (-1);
682 prio = niceval;
683 if (prio > PRIO_MAX)
684 prio = PRIO_MAX;
685 else if (prio < PRIO_MIN)
686 prio = PRIO_MIN;
688 tsparms = (tsparms_t *) pcparms.pc_clparms;
689 scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri;
690 tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20;
691 pcparms.pc_cid = pcinfo.pc_cid;
693 if (priocntl (P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1)
694 return (-1);
696 return (0);
698 #endif
700 /****************************************************************
701 * read_sysinfos() - *
702 * Read all of the CPU specific sysinfo sturctures in from *
703 * the /stats file system. *
704 ****************************************************************/
705 read_sysinfos(num_cpus, buf)
706 int num_cpus;
707 struct sysinfo *buf;
710 static int fd1=0; /* file descriptor for /stats/sysinfo */
711 int read_sz;
713 /* Open /stats/sysinfo one time only and leave it open */
714 if (fd1==0) {
715 if ((fd1 = open("/stats/sysinfo", O_RDONLY)) == -1)
716 (void) fprintf (stderr, "%s: Open of /stats/sysinfo failed\n", myname);
718 /* reset the read pointer to the beginning of the file */
719 if (lseek(fd1, 0L, SEEK_SET) == -1)
720 (void) fprintf (stderr, "%s: lseek to beginning of /stats/sysinfo failed\n", myname);
721 read_sz = num_cpus * sizeof(buf[0]);
722 if (read(fd1, buf, read_sz) != read_sz)
723 (void) fprintf (stderr, "%s: Read of /stats/sysinfo failed\n", myname);
726 /****************************************************************
727 * sysinfo_data() - *
728 * Add up all of the CPU specific sysinfo sturctures to *
729 * make the GLOBAL sysinfo. *
730 ****************************************************************/
731 sysinfo_data(num_cpus, global_si, percpu_si)
732 int num_cpus;
733 struct sysinfo *global_si;
734 struct sysinfo *percpu_si;
736 struct sysinfo *percpu_p;
737 int cpu, i, *global, *src;
739 /* null out the global statistics from last sample */
740 memset(global_si, 0, sizeof(struct sysinfo));
742 percpu_p = (struct sysinfo *)percpu_si;
743 for(cpu = 0; cpu < num_cpus; cpu++) {
744 global = (int *)global_si;
745 src = (int *)percpu_p;
747 /* assume sysinfo ends on an int boundary */
748 /* Currently, all of the struct sysinfo members are the same
749 * size as an int. If that changes, we may not be able to
750 * do this. But this should be safe.
752 for(i=0; i<sizeof(struct sysinfo)/sizeof(int); i++) {
753 *global++ += *src++;
755 percpu_p++;