/* renice.c - unixish renice command for BeOs
 * (c) 2001, 2002, François Revol (mmu_man) for OpenBeOS
 * released under the MIT licence.
 *
 * How did I live without it before ??? ;)
 * ChangeLog:
 * 04-25-2002 v1.2
 *  Cleanup for inclusion in OpenBeOS,
 *  Used the code to rewrite the 'prio' BeOS command for OpenBeOS.
 * 04-14-2002 v1.1
 *  Added -f upon suggestion from Idéfix on BeShare
 * 2001 v1.0
 *  Initial.
 */

#include <OS.h>
#include <stdio.h>
#include <string.h>

/*
   Notes:

From man renice:
/usr/sbin/renice [-n increment] [-p] [-g | -u] ID ... /usr/sbin/renice priority [-p] pid ... [-g pgrp ...] [-u user ...]


#define B_LOW_PRIORITY                  5
#define B_NORMAL_PRIORITY               10
#define B_DISPLAY_PRIORITY              15
#define B_URGENT_DISPLAY_PRIORITY       20
#define B_REAL_TIME_DISPLAY_PRIORITY    100
#define B_URGENT_PRIORITY               110
#define B_REAL_TIME_PRIORITY            120

BeOs priorities:
High Prio (realtime)            Default                  Low Prio
120                               10                        1 (0 only for idle_thread)

UNIX Priorities:
-20                               0                         20

Note that however this isn't perfect, since priorities 
beyond 100 in BeOS move the thread into the real-time class.
Linux for example, has a separate API for doing this, 
and even a process set to -20 can be in the normal scheduling class.

renice can be given more than one pid on the command line.
prio is locked into one pid, then the priority.

*/

// returns an equivalent UNIX priority for a given BeOS priority.
static int32 prio_be_to_unix(int32 prio)
{
	return (prio > 10)?(- (20 * (prio - 10)) / 110):(2 * (10 - prio));
}

// returns an equivalent BeOS priority for a given UNIX priority.
static int32 prio_unix_to_be(int32 prio)
{
	return (prio > 0)?(10 - (prio/2)):(10 + 110 * (-prio) / 20);
}

static status_t renice_thread(int32 prio, int32 increment, bool use_be_prio, thread_id th)
{
	thread_info thinfo;

	if(increment != 0) {
		get_thread_info(th, &thinfo);
		prio = thinfo.priority;
		if(!use_be_prio)
			prio = prio_be_to_unix(prio);
		prio += increment;
		if(!use_be_prio)
			prio = prio_unix_to_be(prio);
	}
	return set_thread_priority(th, prio);
}

int main(int argc, char **argv)
{
	thread_id th = -1;
	int32 prio, increment = 0;
	thread_info thinfo;
	bool use_be_prio = false;
	bool next_is_prio = true;
	bool next_is_increment = false;
	bool use_increment = false;
	bool find_by_name = false;
	int i = 0;
	int32 teamcookie = 0;
	team_info teaminfo;
	int32 thcookie = 0;
	int err = 1;
	char *thname;

	prio = 9; // default UNIX priority for nice
	// convert it to beos
	if (!use_be_prio)
	prio = prio_unix_to_be(prio);
	
	while (++i < argc) {
		if (!strcmp(argv[i], "-p")) { // ignored option
		} else if (!strcmp(argv[i], "-n")) {
			next_is_prio = false;
			next_is_increment = true;
			use_increment = true;
		} else if (!strcmp(argv[i], "-b")) {
			use_be_prio = true;
		} else if (!strcmp(argv[i], "-f")) {
			find_by_name = true;
		} else if (next_is_increment) {
			next_is_increment = false;
			sscanf(argv[i], "%ld", (long *)&increment);
		} else if (next_is_prio) {
			next_is_prio = false;
			sscanf(argv[i], "%ld", (long *)&prio);
			if (!use_be_prio)
				prio = prio_unix_to_be(prio);
		} else {
			if (!find_by_name) {
			sscanf(argv[i], "%ld", (long *)&th);
				return (renice_thread(prio, increment, use_be_prio, th) == B_OK)?0:1;
			}
			thname = argv[i];
			while (get_next_team_info(&teamcookie, &teaminfo) == B_OK) {
				thcookie = 0;
				while (get_next_thread_info(teaminfo.team, &thcookie, &thinfo) == B_OK) {
					if (!strncmp(thname, thinfo.name, B_OS_NAME_LENGTH)) {
						th = thinfo.thread;
						renice_thread(prio, increment, use_be_prio, th);
						err = 0;
					}
				}
			}
			return err;
		}
	}
	if (th == -1) {
		puts("Usage:");
		puts("renice [-b] [-n increment | prio] [-f thname thname...|thid thid ...]");
		puts("	-b : use BeOS priorities instead of UNIX priorities");
		puts("	-n : adds increment to the current priority instead of assigning it");
		puts("	-f : find threads by name instead of by id");
		return 1;
	}
	return 0;
}

