1837c5110SFrançois Revol/*
2837c5110SFrançois Revol * Copyright (C) 1996 Be, Inc.
3837c5110SFrançois Revol * All Rights Reserved
4837c5110SFrançois Revol *
5837c5110SFrançois Revol * This source code was published by Be, Inc. in the file gnu_x86.tar.gz for R3,
6837c5110SFrançois Revol * a mirror is at http://linux.inf.elte.hu/ftp/beos/development/gnu/r3.0/
7837c5110SFrançois Revol * needs to link to termcap.
8bdb2cb79SFrançois Revol * The GPL should apply here, since AFAIK termcap is GPLed.
9837c5110SFrançois Revol */
10837c5110SFrançois Revol
11837c5110SFrançois Revol/*
12837c5110SFrançois Revol * Top -- a program for finding the top cpu-eating threads
13837c5110SFrançois Revol */
14a43c663fSJérôme Duval#include <signal.h>
15837c5110SFrançois Revol#include <stdio.h>
16837c5110SFrançois Revol#include <stdlib.h>
17837c5110SFrançois Revol#include <string.h>
18837c5110SFrançois Revol#include <OS.h>
19837c5110SFrançois Revol#include <termios.h>
20837c5110SFrançois Revol
2195cace8dSAdrien Destugues#include <list>
2295cace8dSAdrien Destugues
2359b17060SAdrien Destugues#include "termcap.h"
2459b17060SAdrien Destugues
25837c5110SFrançois Revolstatic const char IDLE_NAME[] = "idle thread ";
26f1e0212dSPhilippe Saint-Pierrestatic bigtime_t lastMeasure = 0;
27837c5110SFrançois Revol
28837c5110SFrançois Revol/*
29a6e71543SFreeman Lou * Keeps track of a single thread's times
30837c5110SFrançois Revol */
3195cace8dSAdrien Destuguesstruct ThreadTime {
32837c5110SFrançois Revol	thread_id thid;
33837c5110SFrançois Revol	bigtime_t user_time;
34837c5110SFrançois Revol	bigtime_t kernel_time;
3595cace8dSAdrien Destugues
3695cace8dSAdrien Destugues	bigtime_t total_time() const {
3795cace8dSAdrien Destugues		return user_time + kernel_time;
3895cace8dSAdrien Destugues	}
3995cace8dSAdrien Destugues
4095cace8dSAdrien Destugues	bool operator< (const ThreadTime& other) const {
4195cace8dSAdrien Destugues		return total_time() > other.total_time();
4295cace8dSAdrien Destugues	}
4395cace8dSAdrien Destugues};
44837c5110SFrançois Revol
45837c5110SFrançois Revol/*
46837c5110SFrançois Revol * Keeps track of all the threads' times
47837c5110SFrançois Revol */
4895cace8dSAdrien Destuguestypedef std::list<ThreadTime> ThreadTimeList;
49837c5110SFrançois Revol
50837c5110SFrançois Revol
51a6e71543SFreeman Loustatic char *clear_string; /*output string for clearing the screen */
52a6e71543SFreeman Loustatic char *enter_ca_mode; /* output string for switching screen buffer */
53a6e71543SFreeman Loustatic char *exit_ca_mode; /* output string for releasing screen buffer */
54a6e71543SFreeman Loustatic char *cursor_home;	/* Places cursor back to (1,1) */
55a6e71543SFreeman Loustatic char *restore_cursor;
56a6e71543SFreeman Loustatic char *save_cursor;
57a6e71543SFreeman Loustatic int columns;	/* Columns on screen */
58837c5110SFrançois Revolstatic int rows;	/* how many rows on the screen */
59a43c663fSJérôme Duvalstatic int screen_size_changed = 0;	/* tells to refresh the screen size */
60837c5110SFrançois Revolstatic int cpus;	/* how many cpus we are runing on */
61837c5110SFrançois Revol
62a43c663fSJérôme Duval/* SIGWINCH handler */
63a43c663fSJérôme Duvalstatic void
64a43c663fSJérôme Duvalwinch_handler(int notused)
65a43c663fSJérôme Duval{
66a43c663fSJérôme Duval	(void) notused;
67a43c663fSJérôme Duval	screen_size_changed = 1;
68a43c663fSJérôme Duval}
69a43c663fSJérôme Duval
70a6e71543SFreeman Lou
71a6e71543SFreeman Lou/* SIGINT handler */
72a6e71543SFreeman Loustatic void
7395cace8dSAdrien Destuguessigint_handler(int)
74a6e71543SFreeman Lou{
75a6e71543SFreeman Lou	printf(exit_ca_mode);
76a6e71543SFreeman Lou	printf(restore_cursor);
77a6e71543SFreeman Lou	exit(1);
78a6e71543SFreeman Lou}
79a6e71543SFreeman Lou
80a6e71543SFreeman Lou
81a6e71543SFreeman Loustatic void
82a6e71543SFreeman Louinit_term()
83a6e71543SFreeman Lou{
8495cace8dSAdrien Destugues	static char buf[2048];
8595cace8dSAdrien Destugues	char *entries = &buf[0];
8695cace8dSAdrien Destugues
87a6e71543SFreeman Lou	tgetent(buf, getenv("TERM"));
88a6e71543SFreeman Lou	exit_ca_mode = tgetstr("te", &entries);
89a6e71543SFreeman Lou	enter_ca_mode = tgetstr("ti", &entries);
90a6e71543SFreeman Lou	cursor_home = tgetstr("ho", &entries);
91a6e71543SFreeman Lou	clear_string = tgetstr("cl", &entries);
92a6e71543SFreeman Lou	restore_cursor = tgetstr("rc", &entries);
93a6e71543SFreeman Lou	save_cursor = tgetstr("sc", &entries);
94a6e71543SFreeman Lou
95a6e71543SFreeman Lou	printf(save_cursor);
96a6e71543SFreeman Lou	printf(enter_ca_mode);
97a6e71543SFreeman Lou	printf(clear_string);
98a6e71543SFreeman Lou}
99a6e71543SFreeman Lou
100a6e71543SFreeman Lou
101837c5110SFrançois Revol/*
102837c5110SFrançois Revol * Calculate the cpu percentage used by a given thread
103837c5110SFrançois Revol * Remember: for multiple CPUs, multiply the interval by # cpus
104837c5110SFrançois Revol * when calculating
105837c5110SFrançois Revol */
1063ddc1cdbSFrançois Revolstatic float
1073ddc1cdbSFrançois Revolcpu_perc(bigtime_t spent, bigtime_t interval)
108837c5110SFrançois Revol{
109837c5110SFrançois Revol	return ((100.0 * spent) / (cpus * interval));
110837c5110SFrançois Revol}
111837c5110SFrançois Revol
112837c5110SFrançois Revol
113837c5110SFrançois Revol/*
114837c5110SFrançois Revol * Compare an old snapshot with the new one
115837c5110SFrançois Revol */
1163ddc1cdbSFrançois Revolstatic void
117837c5110SFrançois Revolcompare(
11895cace8dSAdrien Destugues		ThreadTimeList *old,
11995cace8dSAdrien Destugues		ThreadTimeList *newList,
120837c5110SFrançois Revol		bigtime_t uinterval,
121837c5110SFrançois Revol		int refresh
122837c5110SFrançois Revol		)
123837c5110SFrançois Revol{
124837c5110SFrançois Revol	bigtime_t oldtime;
125837c5110SFrançois Revol	bigtime_t newtime;
12695cace8dSAdrien Destugues	ThreadTimeList times;
127837c5110SFrançois Revol	thread_info t;
128837c5110SFrançois Revol	team_info tm;
129837c5110SFrançois Revol	bigtime_t total;
130837c5110SFrançois Revol	bigtime_t utotal;
131837c5110SFrançois Revol	bigtime_t ktotal;
132837c5110SFrançois Revol	bigtime_t gtotal;
133837c5110SFrançois Revol	bigtime_t idletime;
134837c5110SFrançois Revol	int newthread;
135837c5110SFrançois Revol	int ignore;
136837c5110SFrançois Revol	int linecount;
1373ddc1cdbSFrançois Revol	//system_info info;
138837c5110SFrançois Revol	char *p;
139837c5110SFrançois Revol
140837c5110SFrançois Revol	/*
141837c5110SFrançois Revol	 * First, merge both old and new lists, computing the differences in time
142837c5110SFrançois Revol	 * Threads in only one list are dropped.
143837c5110SFrançois Revol	 * Threads with no elapsed time are dropped too.
144837c5110SFrançois Revol	 */
145837c5110SFrançois Revol	gtotal = 0;
146837c5110SFrançois Revol	utotal = 0;
147837c5110SFrançois Revol	ktotal = 0;
14895cace8dSAdrien Destugues	ThreadTimeList::iterator it;
14995cace8dSAdrien Destugues	ThreadTimeList::iterator itOld;
15095cace8dSAdrien Destugues	ThreadTime entry;
15195cace8dSAdrien Destugues
15295cace8dSAdrien Destugues	for (it = newList->begin(); it != newList->end(); it++) {
153837c5110SFrançois Revol		newthread = 1;
154837c5110SFrançois Revol		ignore = 0;
15595cace8dSAdrien Destugues		for (itOld = old->begin(); itOld != old->end(); itOld++) {
15695cace8dSAdrien Destugues			if (itOld->thid != it->thid) {
157837c5110SFrançois Revol				continue;
158837c5110SFrançois Revol			}
159837c5110SFrançois Revol			newthread = 0;
16095cace8dSAdrien Destugues			oldtime = itOld->total_time();
16195cace8dSAdrien Destugues			newtime = it->total_time();
162837c5110SFrançois Revol			if (oldtime == newtime) {
163837c5110SFrançois Revol				ignore = 1;
164837c5110SFrançois Revol				break;
165837c5110SFrançois Revol			}
16695cace8dSAdrien Destugues			entry.thid = it->thid;
16795cace8dSAdrien Destugues			entry.user_time = (it->user_time - itOld->user_time);
16895cace8dSAdrien Destugues			entry.kernel_time = (it->kernel_time - itOld->kernel_time);
169837c5110SFrançois Revol		}
170837c5110SFrançois Revol		if (newthread) {
17195cace8dSAdrien Destugues			entry.thid = it->thid;
17295cace8dSAdrien Destugues			entry.user_time = it->user_time;
17395cace8dSAdrien Destugues			entry.kernel_time = it->kernel_time;
174837c5110SFrançois Revol		}
175837c5110SFrançois Revol		if (!ignore) {
17695cace8dSAdrien Destugues			times.push_back(entry);
17795cace8dSAdrien Destugues
17895cace8dSAdrien Destugues			total = entry.total_time();
179837c5110SFrançois Revol			gtotal += total;
18095cace8dSAdrien Destugues			utotal += entry.user_time;
18195cace8dSAdrien Destugues			ktotal += entry.kernel_time;
182837c5110SFrançois Revol		}
183837c5110SFrançois Revol	}
184837c5110SFrançois Revol
185837c5110SFrançois Revol	/*
186837c5110SFrançois Revol	 * Sort what we found and print
187837c5110SFrançois Revol	 */
18895cace8dSAdrien Destugues	times.sort();
189837c5110SFrançois Revol
190a6e71543SFreeman Lou	printf("%6s %7s %7s %7s %4s %16s %-16s \n", "THID", "TOTAL", "USER", "KERNEL",
191a6e71543SFreeman Lou		"%CPU", "TEAM NAME", "THREAD NAME");
192837c5110SFrançois Revol	linecount = 1;
19339f6ced0SPawel Dziepak	idletime = 0;
194837c5110SFrançois Revol	gtotal = 0;
195837c5110SFrançois Revol	ktotal = 0;
196837c5110SFrançois Revol	utotal = 0;
19795cace8dSAdrien Destugues	for (it = times.begin(); it != times.end(); it++) {
198837c5110SFrançois Revol		ignore = 0;
19995cace8dSAdrien Destugues		if (get_thread_info(it->thid, &t) < B_NO_ERROR) {
200837c5110SFrançois Revol			strcpy(t.name, "(unknown)");
201837c5110SFrançois Revol			strcpy(tm.args, "(unknown)");
202837c5110SFrançois Revol		} else {
203837c5110SFrançois Revol			if (strncmp(t.name, IDLE_NAME, strlen(IDLE_NAME)) == 0) {
204837c5110SFrançois Revol				ignore++;
205837c5110SFrançois Revol			}
206837c5110SFrançois Revol			if (get_team_info(t.team, &tm) < B_NO_ERROR) {
207837c5110SFrançois Revol				strcpy(tm.args, "(unknown)");
208837c5110SFrançois Revol			} else {
2093ddc1cdbSFrançois Revol				if ((p = strrchr(tm.args, '/'))) {
2109c1f9724SHo Tuan Kiet					strlcpy(tm.args, p + 1, sizeof(tm.args));
211837c5110SFrançois Revol				}
212837c5110SFrançois Revol			}
213837c5110SFrançois Revol		}
214837c5110SFrançois Revol
215837c5110SFrançois Revol		tm.args[16] = 0;
216a6e71543SFreeman Lou
217a6e71543SFreeman Lou		if (columns <= 80)
218a6e71543SFreeman Lou			t.name[16] = 0;
2192b0dad2cSPhilippe Saint-Pierre		else if (columns - 64 < sizeof(t.name))
220a6e71543SFreeman Lou			t.name[columns - 64] = 0;
221a6e71543SFreeman Lou
22295cace8dSAdrien Destugues		total = it->total_time();
223837c5110SFrançois Revol		if (ignore) {
224837c5110SFrançois Revol			idletime += total;
225837c5110SFrançois Revol		} else {
226837c5110SFrançois Revol			gtotal += total;
22795cace8dSAdrien Destugues			ktotal += it->kernel_time;
22895cace8dSAdrien Destugues			utotal += it->user_time;
229837c5110SFrançois Revol		}
230837c5110SFrançois Revol		if (!ignore && (!refresh || (linecount < (rows - 1)))) {
231837c5110SFrançois Revol
232a76e1eb3SMurai Takashi			printf("%6" B_PRId32 " %7.2f %7.2f %7.2f %4.1f %16s %s \n",
23395cace8dSAdrien Destugues				it->thid,
234a6e71543SFreeman Lou				total / 1000.0,
23595cace8dSAdrien Destugues				(double)(it->user_time / 1000),
23695cace8dSAdrien Destugues				(double)(it->kernel_time / 1000),
237a6e71543SFreeman Lou				cpu_perc(total, uinterval),
238a6e71543SFreeman Lou				tm.args,
239a6e71543SFreeman Lou				t.name);
240837c5110SFrançois Revol			linecount++;
241837c5110SFrançois Revol		}
242837c5110SFrançois Revol	}
24395cace8dSAdrien Destugues
244837c5110SFrançois Revol	printf("------ %7.2f %7.2f %7.2f %4.1f%% TOTAL (%4.1f%% idle time, %4.1f%% unknown)",
245a6e71543SFreeman Lou		(double) (gtotal / 1000),
246a6e71543SFreeman Lou		(double) (utotal / 1000),
247a6e71543SFreeman Lou		(double) (ktotal / 1000),
248a6e71543SFreeman Lou		cpu_perc(gtotal, uinterval),
249a6e71543SFreeman Lou		cpu_perc(idletime, uinterval),
250a6e71543SFreeman Lou		cpu_perc(cpus * uinterval - (gtotal + idletime), uinterval));
251837c5110SFrançois Revol	fflush(stdout);
252a6e71543SFreeman Lou	printf(clear_string);
253a6e71543SFreeman Lou	printf(cursor_home);
254837c5110SFrançois Revol}
255837c5110SFrançois Revol
256a6e71543SFreeman Lou
257837c5110SFrançois Revolstatic int
258a6e71543SFreeman Louadjust_term(bool onlyRows)
259837c5110SFrançois Revol{
260837c5110SFrançois Revol	struct winsize ws;
261837c5110SFrançois Revol
262837c5110SFrançois Revol	if (ioctl(1, TIOCGWINSZ, &ws) < 0) {
263837c5110SFrançois Revol		return (0);
264837c5110SFrançois Revol	}
265837c5110SFrançois Revol	if (ws.ws_row <= 0) {
266837c5110SFrançois Revol		return (0);
267837c5110SFrançois Revol	}
268a6e71543SFreeman Lou	columns = ws.ws_col;
269837c5110SFrançois Revol	rows = ws.ws_row;
270a43c663fSJérôme Duval	if (onlyRows)
271a43c663fSJérôme Duval		return 1;
272a6e71543SFreeman Lou
273837c5110SFrançois Revol	return (1);
274837c5110SFrançois Revol}
275837c5110SFrançois Revol
276a6e71543SFreeman Lou
277837c5110SFrançois Revol/*
27895cace8dSAdrien Destugues * Gather up thread data since previous run
279837c5110SFrançois Revol */
28095cace8dSAdrien Destuguesstatic ThreadTimeList
28195cace8dSAdrien Destuguesgather(ThreadTimeList *old, int refresh)
282837c5110SFrançois Revol{
283837c5110SFrançois Revol	int32		tmcookie;
284837c5110SFrançois Revol	int32		thcookie;
285837c5110SFrançois Revol	thread_info	t;
286837c5110SFrançois Revol	team_info	tm;
28795cace8dSAdrien Destugues	ThreadTimeList times;
288f1e0212dSPhilippe Saint-Pierre	bigtime_t	oldLastMeasure;
289837c5110SFrançois Revol
290837c5110SFrançois Revol	tmcookie = 0;
291f1e0212dSPhilippe Saint-Pierre	oldLastMeasure = lastMeasure;
292f1e0212dSPhilippe Saint-Pierre	lastMeasure = system_time();
293f1e0212dSPhilippe Saint-Pierre
294837c5110SFrançois Revol	while (get_next_team_info(&tmcookie, &tm) == B_NO_ERROR) {
295837c5110SFrançois Revol		thcookie = 0;
296837c5110SFrançois Revol		while (get_next_thread_info(tm.team, &thcookie, &t) == B_NO_ERROR) {
29795cace8dSAdrien Destugues			ThreadTime entry;
29895cace8dSAdrien Destugues			entry.thid = t.thread;
29995cace8dSAdrien Destugues			entry.user_time = t.user_time;
30095cace8dSAdrien Destugues			entry.kernel_time = t.kernel_time;
30195cace8dSAdrien Destugues			times.push_back(entry);
302837c5110SFrançois Revol		}
303837c5110SFrançois Revol	}
304837c5110SFrançois Revol	if (old != NULL) {
305a43c663fSJérôme Duval		if (screen_size_changed) {
306a6e71543SFreeman Lou			adjust_term(true);
307a43c663fSJérôme Duval			screen_size_changed = 0;
308a43c663fSJérôme Duval		}
30939f6ced0SPawel Dziepak		compare(old, &times, system_time() - oldLastMeasure, refresh);
31095cace8dSAdrien Destugues		old->clear();
311837c5110SFrançois Revol	}
312837c5110SFrançois Revol	return (times);
313837c5110SFrançois Revol}
314837c5110SFrançois Revol
315a6e71543SFreeman Lou
316837c5110SFrançois Revol/*
317837c5110SFrançois Revol * print usage message and exit
318837c5110SFrançois Revol */
3193ddc1cdbSFrançois Revolstatic void
320837c5110SFrançois Revolusage(const char *myname)
321837c5110SFrançois Revol{
322837c5110SFrançois Revol	fprintf(stderr, "usage: %s [-d] [-i interval] [-n ntimes]\n", myname);
323a6e71543SFreeman Lou	fprintf(stderr,
324837c5110SFrançois Revol			" -d,          do not clear the screen between displays\n");
325a6e71543SFreeman Lou	fprintf(stderr,
326837c5110SFrançois Revol			" -i interval, wait `interval' seconds before displaying\n");
327a6e71543SFreeman Lou	fprintf(stderr,
328837c5110SFrançois Revol			" -n ntimes,   display `ntimes' times before exiting\n");
329837c5110SFrançois Revol	fprintf(stderr, "Default is clear screen, interval=5, ntimes=infinite\n");
330837c5110SFrançois Revol	exit(1);
331837c5110SFrançois Revol}
332837c5110SFrançois Revol
333a6e71543SFreeman Lou
334a8e7c82fSFrançois Revolint
335837c5110SFrançois Revolmain(int argc, char **argv)
336837c5110SFrançois Revol{
33795cace8dSAdrien Destugues	ThreadTimeList baseline;
338837c5110SFrançois Revol	int i;
339837c5110SFrançois Revol	int iters = -1;
340837c5110SFrançois Revol	int interval = 5;
341837c5110SFrançois Revol	int refresh = 1;
342837c5110SFrançois Revol	system_info sysinfo;
343837c5110SFrançois Revol	bigtime_t uinterval;
344837c5110SFrançois Revol	bigtime_t elapsed;
345837c5110SFrançois Revol	char *myname;
346837c5110SFrançois Revol
347837c5110SFrançois Revol	get_system_info (&sysinfo);
348837c5110SFrançois Revol	cpus = sysinfo.cpu_count;
349837c5110SFrançois Revol
350837c5110SFrançois Revol	myname = argv[0];
351837c5110SFrançois Revol	for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) {
352837c5110SFrançois Revol		if (strcmp(argv[0], "-i") == 0) {
353837c5110SFrançois Revol			argc--, argv++;
354837c5110SFrançois Revol			if (argc == 0) {
355837c5110SFrançois Revol				usage(myname);
356837c5110SFrançois Revol			}
357837c5110SFrançois Revol			interval = atoi(argv[0]);
358837c5110SFrançois Revol		} else if (strcmp(argv[0], "-n") == 0) {
359837c5110SFrançois Revol			argc--, argv++;
360837c5110SFrançois Revol			if (argc == 0) {
361837c5110SFrançois Revol				usage(myname);
362837c5110SFrançois Revol			}
363837c5110SFrançois Revol			iters = atoi(argv[0]);
364837c5110SFrançois Revol		} else if (strcmp(argv[0], "-d") == 0) {
365837c5110SFrançois Revol			refresh = 0;
366837c5110SFrançois Revol		} else {
367837c5110SFrançois Revol			usage(myname);
368837c5110SFrançois Revol		}
369837c5110SFrançois Revol	}
370837c5110SFrançois Revol	if (argc) {
371837c5110SFrançois Revol		usage(myname);
372837c5110SFrançois Revol	}
373a6e71543SFreeman Lou
374a6e71543SFreeman Lou	init_term();
375a6e71543SFreeman Lou
376837c5110SFrançois Revol	if (refresh) {
377a6e71543SFreeman Lou		if (!adjust_term(false)) {
378837c5110SFrançois Revol			refresh = 0;
379837c5110SFrançois Revol		}
380837c5110SFrançois Revol	}
381a6e71543SFreeman Lou	if (iters >= 0) {
382a6e71543SFreeman Lou		printf("Starting: %d interval%s of %d second%s each\n", iters,
383a6e71543SFreeman Lou			(iters == 1) ? "" : "s", interval,
384a6e71543SFreeman Lou			(interval == 1) ? "" : "s");
385837c5110SFrançois Revol	}
386a6e71543SFreeman Lou
387a43c663fSJérôme Duval	signal(SIGWINCH, winch_handler);
388a6e71543SFreeman Lou	signal(SIGINT, sigint_handler);
389a6e71543SFreeman Lou
390f1e0212dSPhilippe Saint-Pierre	lastMeasure = system_time();
391e4700f2eSIngo Weinhold	if (iters < 0) {
392e4700f2eSIngo Weinhold		// You will only have to wait half a second for the first iteration.
393e4700f2eSIngo Weinhold		uinterval = 1 * 1000000 / 2;
39439f6ced0SPawel Dziepak		baseline = gather(NULL, refresh);
395f1e0212dSPhilippe Saint-Pierre		elapsed = system_time() - lastMeasure;
396f1e0212dSPhilippe Saint-Pierre		if (elapsed < uinterval)
397e4700f2eSIngo Weinhold			snooze(uinterval - elapsed);
398a6e71543SFreeman Lou		// then = system_time();
39939f6ced0SPawel Dziepak		baseline = gather(&baseline, refresh);
400f1e0212dSPhilippe Saint-Pierre
401e4700f2eSIngo Weinhold	} else
40239f6ced0SPawel Dziepak		baseline = gather(NULL, refresh);
403e4700f2eSIngo Weinhold
404837c5110SFrançois Revol	uinterval = interval * 1000000;
405837c5110SFrançois Revol	for (i = 0; iters < 0 || i < iters; i++) {
406f1e0212dSPhilippe Saint-Pierre		elapsed = system_time() - lastMeasure;
407f1e0212dSPhilippe Saint-Pierre		if (elapsed < uinterval)
408837c5110SFrançois Revol			snooze(uinterval - elapsed);
40939f6ced0SPawel Dziepak		baseline = gather(&baseline, refresh);
410837c5110SFrançois Revol	}
411a6e71543SFreeman Lou
412a6e71543SFreeman Lou	printf(exit_ca_mode);
413a6e71543SFreeman Lou	printf(restore_cursor);
414a6e71543SFreeman Lou
415837c5110SFrançois Revol	exit(0);
416837c5110SFrançois Revol}