et-prompt rebranded to over-prompt, no other changes

This commit is contained in:
Overwatch 2014-09-15 22:39:24 +02:00
commit 04c4330f99
4 changed files with 635 additions and 0 deletions

78
bash-init Executable file
View file

@ -0,0 +1,78 @@
#! /bin/bash
# *very* dirty, but this inferior excuse for a shell deserves nothing better
OVER_PROMPT_CFG="/etc/over/prompt.cfg"
if [ -a "$OVER_PROMPT_CFG" ]; then
source "$OVER_PROMPT_CFG"
fi
function precmd()
{
EXITUS=$?
RAW_DATA="$(/usr/share/over-prompt/data $OVER_PROMPT_OPTS)"
if [ $EXITUS = 0 ]; then
CROSS="|CYAN|\\$|NONE|"
else
CROSS="|RED|${EXITUS}|NONE|:|RED|\\$|NONE|"
fi
if [ $EUID = 0 ]; then
USER_PART="|RED|\\u|NONE|"
else
USER_PART="|GREEN|\\u|NONE|"
fi
home_len=${#HOME}
if [ "${PWD:0:$home_len}" = "$HOME" ]; then
pwd="~${PWD:$home_len}"
else
pwd="$PWD"
fi
if [ -n "$RAW_DATA" ]; then
LOGIN_PART="${RAW_DATA%:::*}"
STATS_PART="[ ${RAW_DATA#*:::} "
pwd_len=${#pwd}
user_len=${#USER}
host_len=${#HOSTNAME}
win_len=${#WINDOW}
STATS_PART_="$(echo ${STATS_PART}|sed 's/|[A-Z]*|//g')"
stats_len=${#STATS_PART_}
max_pwd_len=$((${COLUMNS}-${user_len}-${host_len}-${win_len}-${stats_len}-5))
# 1 kB of spaces... that should be enough. DIRTY, YEAH!
spaces=" "
padding=""
if [ ${pwd_len} -gt ${max_pwd_len} ]; then
pwd="<${pwd:(($pwd_len - $max_pwd_len)):$pwd_len}"
elif [ ${pwd_len} -lt ${max_pwd_len} ]; then
padto=$((${max_pwd_len} - ${pwd_len} + 1))
padding="${spaces:0:$padto}"
fi
PS1="${USER_PART}@${LOGIN_PART} ${padding}${STATS_PART}\n$CROSS "
else
PS1="${USER_PART}@|HOST| |CWD| [|RFLASH|data gathering error|NONE|]\n$CROSS "
fi
PS1="${PS1//|NONE|/\[\033[0m\]}"
PS1="${PS1//|RED|/\[\033[1;31m\]}"
PS1="${PS1//|RFLASH|/\[\033[5;31m\]}"
PS1="${PS1//|GREEN|/\[\033[1;32m\]}"
PS1="${PS1//|DARKGREEN|/\[\033[0;32m\]}"
PS1="${PS1//|BLUE|/\[\033[1;34m\]}"
PS1="${PS1//|YELLOW|/\[\033[1;33m\]}"
PS1="${PS1//|CYAN|/\[\033[1;36m\]}"
PS1="${PS1//|HOST|/$HOSTNAME}"
PS1="${PS1//|TTY|/$WINDOW}"
PS1="${PS1//|CWD|/$pwd}"
unset -v LOGIN_PART STATS_PART pwd_len user_len host_len win_len STATS_PART_ stats_len max_pwd_len spaces padding pwd padto padding
}
PROMPT_COMMAND="precmd"
unset OVER_PROMPT_CFG

455
data.cpp Executable file
View file

@ -0,0 +1,455 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <sys/vfs.h>
#include <libgen.h>
#include <ctype.h>
class Space {
public:
Space(const char *name, float available, float total) {
if (strlen(name) > 15) {
strcpy(this->name, "!!!");
} else {
strcpy(this->name, name);
}
this->available = available;
if (total > 0) {
this->percent = available*100 / total;
this->active = true;
} else {
this->percent = 100;
this->active = false;
}
}
char name[16];
float available;
float percent;
bool active;
};
typedef struct {
float load;
char proc[16];
unsigned int color;
} Sysload;
typedef struct {
unsigned short int m1, m2;
bool long_names;
} Config;
/// Data gatherers
void get_available_space(std::vector<Space> &space);
void get_memswap(std::vector<Space> &space);
void get_sysload(Sysload &sysload);
/// Formatting and parsing
void append(char *output, const char *string, int color=-1);
const char *float_to_str(float value, char *output);
int space_to_color(Space &space);
float grepf(const std::string &data, const char *marker);
std::vector<std::string> read_lines_from_file(std::string filename);
bool column_contains_substr(std::string line, unsigned int column, std::string substr, int position=0);
std::string get_column(std::string line, unsigned int column);
bool str_in_list(std::string str, std::vector<std::string> list);
/// Global
std::map<int, std::string> color_map;
Config config;
std::vector<std::string> ignored;
enum {
COL_GREEN,
COL_DARKGREEN,
COL_YELLOW,
COL_BLUE,
COL_RED,
COL_RFLASH,
COL_NONE
};
int main(int argc, char **argv) {
char output[512];
char tmp[16];
std::vector<Space> space;
Sysload sysload;
output[0] = 0;
color_map[COL_GREEN] = "|GREEN|";
color_map[COL_DARKGREEN] = "|DARKGREEN|";
color_map[COL_YELLOW] = "|YELLOW|";
color_map[COL_BLUE] = "|BLUE|";
color_map[COL_RED] = "|RED|";
color_map[COL_RFLASH] = "|RFLASH|";
color_map[COL_NONE] = "|NONE|";
ignored.push_back("rootfs");
ignored.push_back("proc");
ignored.push_back("sysfs");
ignored.push_back("udev");
ignored.push_back("devpts");
ignored.push_back("shm");
ignored.push_back("usbfs");
ignored.push_back("binfmt_misc");
ignored.push_back("fusectl");
if (argc == 4) {
config.m1 = atoi(argv[1]);
config.m2 = atoi(argv[2]);
if (strcmp(argv[3], "long") == 0) {
config.long_names = true;
} else {
config.long_names = false;
}
} else {
config.m1 = 20;
config.m2 = 10;
config.long_names = false;
}
get_available_space(space);
get_memswap(space);
get_sysload(sysload);
append(output, "|HOST|", sysload.color);
append(output, ":");
append(output, "|TTY|", COL_DARKGREEN);
append(output, " ");
append(output, "|CWD|", COL_DARKGREEN);
append(output, ":::");
for (unsigned int i = 0; i < space.size(); i++) {
append(output, space[i].name);
// TODO Active only
append(output, float_to_str(space[i].available, tmp), space_to_color(space[i]));
append(output, " ");
}
append(output, "| ");
append(output, float_to_str(sysload.load, tmp), sysload.color);
append(output, " ");
append(output, sysload.proc, sysload.color);
std::cout << output;
return 0;
}
void append(char *output, const char *input, int color) {
unsigned int len_o, len_i, len_c = 0;
len_o = strlen(output);
len_i = strlen(input);
if (color != -1) len_c = strlen(color_map[color].c_str());
if (color != -1) strcpy(output + len_o, color_map[color].c_str());
strcpy(output + len_o + len_c, input);
if (color != -1) strcpy(output + len_o + len_i + len_c, color_map[COL_NONE].c_str());
}
const char *float_to_str(float value, char *output) {
char suffix;
output[0] = 0;
if (value > 2*1099511627776.0) { // 2T
value /= 1099511627776.0;
suffix = 'T';
} else if (value > 2*1073741824.0) { // 2G
value /= 1073741824.0;
suffix = 'G';
} else if (value > 2*1048576.0) { // 2M
value /= 1048576.0;
suffix = 'M';
} else if (value > 2*1024.0) { // 2k
value /= 1024.0;
suffix = 'k';
} else {
suffix = 0;
}
sprintf(output, "%.2f%c", value, suffix);
return output;
}
int space_to_color(Space &space) {
if (space.percent >= config.m1) {
return COL_GREEN;
} else if (space.percent >= config.m2) {
return COL_YELLOW;
} else if (space.percent > (config.m2/32.0)) {
return COL_RED;
} else {
return COL_RFLASH;
}
}
void get_sysload(Sysload &sysload) {
float dump;
std::ifstream in;
in.open("/proc/loadavg", std::ifstream::in);
in >> sysload.load;
in >> dump;
in >> dump;
in >> sysload.proc;
in.close();
if (sysload.load > 12) {
sysload.color = COL_RFLASH;
} else if (sysload.load > 8) {
sysload.color = COL_RED;
} else if (sysload.load > 4) {
sysload.color = COL_YELLOW;
} else {
sysload.color = COL_GREEN;
}
}
float grepf(const std::string &data, const char *marker) {
unsigned int pos, offset;
char value[16];
float out;
offset = 0;
pos = data.find(marker) + strlen(marker);
// skip through spaces
while (data[pos] == ' ') {
pos++;
}
// read the number
for (; data[pos] != ' '; pos++, offset++) {
value[offset] = data[pos];
}
// terminator
value[offset] = 0;
out = atof(value);
return out;
}
void get_memswap(std::vector<Space> &space) {
std::string s;
std::ifstream in;
char c;
in.open("/proc/meminfo", std::ifstream::in);
while (true) {
c = in.get();
if (in.good()) {
s += c;
} else {
break;
}
}
in.close();
// memory
space.push_back(Space("mem", grepf(s, "MemFree:")*1024 + grepf(s, "Buffers:")*1024 + grepf(s, "Cached:")*1024, grepf(s, "MemTotal:")*1024));
// swap
space.push_back(Space("swp", grepf(s, "SwapFree:")*1024.0, grepf(s, "SwapTotal:")*1024.0));
}
void get_available_space(std::vector<Space> &space) {
// Go through physical RW mounts and query them re: free space
// Ignore /dev and everything below it
// Generate a unique name: mountpoint basename? or maybe shortened to smallest unique part.
std::vector<std::string> mounts;
struct statfs stats;
std::string fullname, name, mountpoint;
long free_space, total_space;
int ptr, len;
size_t str_pos;
bool OK;
mounts = read_lines_from_file("/etc/mtab");
for (unsigned int i = 0; i < mounts.size(); i++) {
if (column_contains_substr(mounts[i], 3, "rw") and column_contains_substr(mounts[i], 0, "/dev", -1)) {
// no CDs - loops have rw set
if (column_contains_substr(mounts[i], 2, "iso9660")) {
continue;
}
// no binds
if (column_contains_substr(mounts[i], 3, "bind")) {
continue;
}
mountpoint = get_column(mounts[i], 1);
// handle spaces in mount point names - /proc/mounts uses a 4-byte '\\040' sequence to signify a space
while (true) {
str_pos = mountpoint.find("\\040");
if (str_pos == std::string::npos) {
break;
} else {
mountpoint.replace(str_pos, 4, " ");
}
}
// generate name FIXME basenames are unique on systems with obsessive-compulsive roots only
fullname = basename((char*)mountpoint.c_str());
if (config.long_names or fullname == "/") {
name = fullname;
} else {
ptr = 1;
len = fullname.size();
name = fullname.substr(0, ptr);
OK = false;
while (not OK) {
OK = true;
for (unsigned int j = 0; j < space.size(); j++) {
if (name == space[j].name) {
OK = false;
if (ptr == len) {
// too bad, we give up
name += "!";
OK = true;
} else {
name = fullname.substr(0, ++ptr);
}
break;
}
}
}
}
// figure out free and total space
statfs(mountpoint.c_str(), &stats);
free_space = stats.f_bavail * stats.f_bsize;
total_space = stats.f_blocks * stats.f_bsize;
// pass it on
space.push_back(Space(name.c_str(), free_space, total_space));
}
}
}
std::string get_column(std::string line, unsigned int column) {
std::string output;
unsigned int col_counter;
bool eating_chars; // else eating whitespace
char c;
col_counter = -1; // Dirty? Maybe. It's called 'creative' where I come from.
eating_chars = false; // ignore leading whitespace
// start eating bytes
for (unsigned int i = 0; i < line.size(); i++) {
c = line[i];
if (eating_chars and isspace(c)) { // end of a column
eating_chars = false;
if (col_counter == column) {
break;
}
} else if (not (eating_chars or isspace(c))) { // beginning of a column
eating_chars = true;
col_counter++;
}
if (col_counter == column) {
output += c;
}
}
return output;
}
bool column_contains_substr(std::string line, unsigned int column, std::string substr, int position) {
std::string str;
str = get_column(line, column);
switch (position) {
case -1:
// beginning
if (str.substr(0, substr.size()) == substr) {
return true;
} else {
return false;
}
break;
case 0:
// anywhere
if (str.find(substr) == std::string::npos) {
return false;
} else {
return true;
}
break;
case 1:
// end
if (str.substr(str.size() - substr.size() - 1, substr.size()) == substr) {
return true;
} else {
return false;
}
break;
};
return false;
}
std::vector<std::string> read_lines_from_file(std::string filename) {
char buf[512];
std::ifstream in;
std::vector<std::string> lines;
in.open(filename.c_str(), std::ifstream::in);
while (in.good()) {
in.getline(buf, 512);
if (in.gcount()) {
lines.push_back(buf);
}
}
return lines;
}
bool str_in_list(std::string str, std::vector<std::string> list) {
for (unsigned int i = 0; i < list.size(); i++) {
if (list[i] == str) {
return true;
}
}
return false;
}

41
over-prompt-9999.ebuild Normal file
View file

@ -0,0 +1,41 @@
# Copyright 1999-2014 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: $
EAPI=5
inherit git-2
DESCRIPTION="A nonintrusive shell prompt that provides: username, hostname, tty name, cwd, return value of the last command, current time, disk space, and system load. Zsh is preferred, although bash is also somewhat supported."
HOMEPAGE="http://overtech.cz/"
SRC_URI=""
EGIT_REPO_URI="git://overtech.cz/over-prompt.git"
LICENSE="AOJSL"
SLOT="0"
KEYWORDS="x86 amd64"
src_compile() {
g++ -ansi -Wall -o data data.cpp
}
src_install() {
insinto /usr/share/${PN}
insopts -m755
doins data
doins *-init
}
pkg_postinst() {
einfo "Source /usr/share/${PN}/bash-init or /usr/share/${PN}/zsh-init"
einfo "(depending on which shell you're running) to enable the prompt."
einfo "If you wish, write the following into /etc/over/prompt.cfg:"
einfo ""
einfo "export OVER_PROMPT_OPTS=\"A B C\""
einfo ""
einfo "where A is the percentage at which your free space indicator turns"
einfo "yellow, B is the threshold for red color, and C is either 'short' or"
einfo "'long' (without the quotes), indicating whether you want to display"
einfo "short or long mount point names. The default is \"20 10 short\"."
}

61
zsh-init Executable file
View file

@ -0,0 +1,61 @@
#! /bin/zsh
OVER_PROMPT_CFG="/etc/over/prompt.cfg"
if [[ -a "$OVER_PROMPT_CFG" ]]; then
source "$OVER_PROMPT_CFG"
fi
function tprompt_filter {
PRT="$1"
PRT="${PRT//|HOST|/%m}"
PRT="${PRT//|TTY|/%l}"
COLORLESS="$(print -P "%n@${PRT}"|sed 's/|[A-Z]*|//g')"
PRT="${PRT//|GREEN|/\%\{\e[1;32m%\}}"
PRT="${PRT//|RED|/\%\{\e[1;31m%\}}"
PRT="${PRT//|RFLASH|/\%\{\e[5;31m%\}}"
PRT="${PRT//|DARKGREEN|/\%\{\e[0;32m%\}}"
PRT="${PRT//|YELLOW|/\%\{\e[1;33m%\}}"
# PRT="${PRT//|BLUE|/\%\{\e[1;34m%\}}" # not used
PRT="${PRT//|CYAN|/\%\{\e[1;36m%\}}"
PRT="${PRT//|NONE|/\%\{\e[0m%\}}"
((OFFSET = ${COLUMNS} - ${#COLORLESS} + 9))
PRT="${PRT//|CWD|/%${OFFSET}<\\\\\\\\\\\\\\\<<%~%<<}" # 16x\ protoze to jde exponencialne pres 4 printy (kazdej jich polovinu sezere :)
print "$PRT"
}
function precmd {
local OVER_OPTS
set -A OVER_OPTS ${(s. .)OVER_PROMPT_OPTS}
RAW_DATA="$(/usr/share/over-prompt/data $OVER_OPTS[1] $OVER_OPTS[2] $OVER_OPTS[3])"
if [ -n "$RAW_DATA" ]; then
set -A DATA ${(s.:::.)RAW_DATA}
LOGIN_PART=${DATA[1]}
STATS_PART=${DATA[2]}
PS1a="%(!.|RED|.|GREEN|)%n|NONE|@$LOGIN_PART"
PS1b="%(?.|CYAN|.|RED|%?|NONE|:|RED|)%(!.#.$)|NONE| "
RPS1="[ |CYAN|%T|NONE| | $STATS_PART"
# vyrenderovat
PS1a="$(print "$(tprompt_filter "$PS1a")")"
PS1b="$(print "$(tprompt_filter "$PS1b")")"
RPS1="$(print "$(tprompt_filter "$RPS1")")"
PS1="$(print "$PS1a\n$PS1b")"
RPS1="$(print $RPS1)"
else
PS1a="%(!.|RED|.|GREEN|)%n|NONE|@|HOST|:|DARKGREEN||TTY| |CWD|"
PS1b="%(?.|CYAN|.|RED|%?|NONE|:|RED|)%(!.#.$)|NONE| "
RPS1="[ |CYAN|%T|NONE| | |RFLASH|data gathering error|NONE|"
PS1a="$(print "$(tprompt_filter "$PS1a")")"
PS1b="$(print "$(tprompt_filter "$PS1b")")"
PS1="$(print "${PS1a}\n${PS1b}")"
RPS1="$(print "$(tprompt_filter "$RPS1")")"
fi
}
unset OVER_PROMPT_CFG