@@ -1,12 +1,14 @@ | |||
DEBUG:=1 | |||
GCC:=0 | |||
LIBS:=ncurses readline history | |||
CC:=gcc | |||
CFLAGS:=-Wall -Wextra -Wpedantic | |||
CFLAGS +=$(if $(DEBUG),-O0 -ggdb,-O3 -flto=auto -fomit-frame-pointer) | |||
CFLAGS +=$(if $(SAN),-fsanitize=${SAN}) | |||
CPPFLAGS:=-I config/ -I ${CHDRD} ${shell pkg-config --cflags ncurses readline} | |||
LDLIBS=${shell pkg-config --libs ncurses readline} | |||
CPPFLAGS:=-I config/ -I ${CHDRD} ${shell pkg-config --cflags ${LIBS}} | |||
LDLIBS=${shell pkg-config --libs ${LIBS}} | |||
LEX:=flex | |||
LEXD:=src/ | |||
@@ -26,7 +26,7 @@ Csope is alive and well. | |||
# Interface | |||
<-- Tab --> | |||
+------------Message-------------+ +--------------------------------+ | |||
+--Version-----------------Case--+ +--------------------------------+ | |||
A |+--------------+---------------+| |+------------------------------+| | |||
| || Input Window | Result window || || || | |||
| |+--------------+ || ? || || | |||
@@ -36,7 +36,7 @@ Csope is alive and well. | |||
| || | || || || | |||
| || | || || || | |||
V |+--------------+---------------+| |+------------------------------+| | |||
+-----------Tool Tips------------+ +--------------------------------+ | |||
+---------------------Tool Tips--+ +--------------------------------+ | |||
# Usacases | |||
Csope shines at exploring stranger and obsecure code bases due to its TUI. | |||
@@ -49,21 +49,29 @@ fixing it would have been a lost cause, if not for Cscope itself. Well, Csope no | |||
# Improvements/Changes | |||
## User side | |||
+ renamed the program, because "cscope" is annoying to type | |||
+ improved gui | |||
+ GNU Readline integration (ie. VI/EMACS mode, command history) /*pending*/ | |||
+ Renamed the program, because "cscope" is annoying to type | |||
+ Improved tui | |||
+ GNU Readline/History integration | |||
## To the code | |||
+ nuked autoconf, replaced with single Makefile | |||
+ reorganized main() | |||
+ encapsulated changes to the TUI into display.c | |||
+ encapsulated searching into find.c | |||
+ removed "scanner.l" which seems to be an anchient version (and redundant copy) of "fscanner.l" forgotten by all | |||
+ removed macro hell put in place to allow compiling on a dead badger | |||
+ replaced repeated inline #ifdef KEY_\*-s with guaranteed definitions | |||
+ removed random commets giving tips for and refering to specific issues | |||
+ use stdbool instead of YES/NO macros | |||
+ saved kilobytes by stripping trailing whitespace | |||
+ FILE\* refsfound used to be rewind()-ed everytime the reads were not sequencial | |||
+ Nuked autoconf, replaced with single Makefile | |||
+ Reorganized the control flow | |||
+ Encapsulated changes to the TUI into display.c | |||
+ Encapsulated searching into find.c | |||
+ Removed "scanner.l" which seems to be an anchient version (and redundant copy) of "fscanner.l" forgotten by all | |||
+ Removed macro hell put in place to allow compiling on a dead badger | |||
+ Use stdbool instead of YES/NO macros | |||
+ Saved kilobytes by stripping trailing whitespace | |||
+ ...and much more | |||
# Installation | |||
You will have to compile from source. | |||
After you made sure you have the following (dev) libraries installed: | |||
ncurses | |||
GNU Readline | |||
GNU History (should come with Readline) | |||
Just run: | |||
make | |||
This will yield the executable "scope", which you are free to do whatever with. | |||
# Control flow | |||
... | |||
@@ -86,26 +94,25 @@ fixing it would have been a lost cause, if not for Cscope itself. Well, Csope no | |||
| int window_change | Bit mask type of the CH_\* macros. Keeps track of the windows to be refresed on the next run of display(). Could be better utalized. | |||
# TODO /*move soon*/ | |||
+ sort out constants.h | |||
+ scrollbar() uses magic int literals? | |||
+ Ordering function declarations in global.h by alpha order is not smart | |||
+ lineflagafterfile is stupid | |||
+ library.h...; "private library", in a program using 90 globals; ffs | |||
+ sort out the global hell | |||
+ was there really ever a scrollbar? | |||
+ handle resizing | |||
+ a search struct could be great for caching and could easy the global situation | |||
+ sort out constants.h | |||
+ scrollbar() uses magic int literals? | |||
+ lineflagafterfile is stupid | |||
+ library.h...; "private library", in a program using 90 globals; ffs | |||
+ sort out the global hell | |||
+ was there really ever a scrollbar? | |||
+ handle resizing | |||
+ a search struct could be great for caching and could ease the global situation | |||
## Original | |||
+ Same capabilities as interactive in non interactive (one shot) mode | |||
+ Provide some how-do-I-use-this-thing doc. | |||
# BUGS | |||
+ Changing text double frees: | |||
free(): double free detected in tcache 2 | |||
Aborted | |||
+ Changing text can crash without replacing text and leaving the console ncursed | |||
+ After an attempted change malloc *can* cry and crash | |||
+ Changing text double frees: | |||
free(): double free detected in tcache 2 | |||
Aborted | |||
+ Changing text can crash without replacing text and leaving the console ncursed | |||
+ After an attempted change malloc *can* cry and crash | |||
# Future features / contributor wishlist | |||
+ providing support for other languages by integrating new lexers (e.g. ctag's) |
@@ -35,6 +35,8 @@ | |||
# define COLOR_HELP_BG -1 | |||
# define COLOR_TOOLTIP_FG COLOR_WHITE | |||
# define COLOR_TOOLTIP_BG COLOR_GREEN | |||
# define COLOR_CASE_FG COLOR_GREEN | |||
# define COLOR_CASE_BG -1 | |||
# define COLOR_MESSAGE_FG COLOR_WHITE | |||
# define COLOR_MESSAGE_BG COLOR_BLACK | |||
# define COLOR_PATTERN_FG COLOR_WHITE | |||
@@ -78,6 +80,7 @@ enum color_pairs{ | |||
COLOR_PAIR_FIELD_SELECTED, | |||
COLOR_PAIR_HELP, | |||
COLOR_PAIR_TOOLTIP, | |||
COLOR_PAIR_CASE, | |||
COLOR_PAIR_PATTERN, | |||
COLOR_PAIR_MESSAGE, | |||
COLOR_PAIR_TABLE_HEADER, | |||
@@ -94,6 +94,7 @@ WINDOW *wresult; | |||
WINDOW *whelp; | |||
/* Non-Selectable windows */ | |||
WINDOW *wtooltip; | |||
WINDOW *wcase; | |||
/* Selected window pointer */ | |||
WINDOW **current_window; | |||
static WINDOW **last_window; | |||
@@ -113,6 +114,7 @@ static inline void display_cursor(void); | |||
static inline void display_help(void); | |||
static inline void display_frame(const bool border_only); | |||
static inline void display_mode(void); | |||
static inline void display_case(void); | |||
static inline void display_command_field(void); | |||
static inline void display_results(void); | |||
static inline void display_tooltip(void); | |||
@@ -161,6 +163,7 @@ void dispinit(void) { | |||
easy_init_pair(FIELD_SELECTED); | |||
easy_init_pair(HELP); | |||
easy_init_pair(TOOLTIP); | |||
easy_init_pair(CASE); | |||
easy_init_pair(MESSAGE); | |||
easy_init_pair(PATTERN); | |||
easy_init_pair(TABLE_HEADER); | |||
@@ -189,6 +192,7 @@ void dispinit(void) { | |||
mdisprefs = result_window_height - (WRESULT_TABLE_BODY_START + 1); | |||
tooltip_width = | |||
MAX(MAX(strlen(tooltip_winput), strlen(tooltip_wmode)), strlen(tooltip_wresult)); | |||
static int case_width = sizeof("Case: XXX")-1; | |||
if(mdisprefs <= 0) { | |||
postfatal(PROGRAM_NAME ": screen too small\n"); | |||
@@ -203,11 +207,12 @@ void dispinit(void) { | |||
rlinit(); | |||
/* initialize windows */ | |||
winput = newwin(input_window_height, first_col_width, 1, 1); | |||
wmode = newwin(mode_window_height, first_col_width, input_window_height + 1 + 1, 1); | |||
wresult = newwin(result_window_height, second_col_width, 1, first_col_width + 1 + 1); | |||
whelp = newwin(LINES - 2, COLS - 2, 1, 1); | |||
winput = newwin(input_window_height, first_col_width, 1, 1); | |||
wmode = newwin(mode_window_height, first_col_width, input_window_height + 1 + 1, 1); | |||
wresult = newwin(result_window_height, second_col_width, 1, first_col_width + 1 + 1); | |||
whelp = newwin(LINES - 2, COLS - 2, 1, 1); | |||
wtooltip = newwin(1, tooltip_width, LINES - 1, COLS - (tooltip_width + 4)); | |||
wcase = newwin(1, case_width, 0, COLS - case_width - 4); | |||
refresh(); | |||
current_window = &winput; | |||
@@ -261,6 +266,13 @@ static inline void display_help() { | |||
do_press_any_key = true; | |||
} | |||
static inline void display_case(){ | |||
wmove(wcase, 0, 0); | |||
wattron(wcase, COLOR_PAIR(COLOR_PAIR_CASE)); | |||
waddstr(wcase, (caseless ? "Case: OFF" : "Case: ON")); | |||
wattroff(wcase, COLOR_PAIR(COLOR_PAIR_CASE)); | |||
} | |||
static inline void display_frame(const bool border_only) { | |||
wattron(stdscr, COLOR_PAIR(COLOR_PAIR_FRAME)); | |||
@@ -273,12 +285,6 @@ static inline void display_frame(const bool border_only) { | |||
#else | |||
wprintw(stdscr, PROGRAM_NAME " version %d%s", FILEVERSION, FIXVERSION); | |||
#endif | |||
wmove(stdscr, 0, COLS - (int)sizeof("Case: XXX") - 4); | |||
if(caseless) { | |||
waddstr(stdscr, "Case: ON"); | |||
} else { | |||
waddstr(stdscr, "Case: OFF"); | |||
} | |||
/* --- */ | |||
if(!border_only) { | |||
/* Vertical line */ | |||
@@ -805,6 +811,7 @@ void display(void) { | |||
lstwin = *current_window; | |||
display_tooltip(); | |||
} | |||
if(window_change & CH_CASE) { display_case(); } | |||
if(window_change & CH_INPUT) { display_command_field(); } | |||
if(window_change & CH_RESULT) { display_results(); } | |||
if(window_change & CH_MODE) { display_mode(); } | |||
@@ -814,6 +821,7 @@ void display(void) { | |||
wrefresh(wmode); | |||
wrefresh(wresult); | |||
wrefresh(wtooltip); | |||
wrefresh(wcase); | |||
} | |||
window_change = CH_NONE; | |||
@@ -80,8 +80,9 @@ enum { | |||
CH_RESULT = 0x0001 << 0, | |||
CH_INPUT = 0x0001 << 1, | |||
CH_MODE = 0x0001 << 2, | |||
CH_HELP = 0x0001 << 3, /* do NOT add to CH_ALL */ | |||
CH_ALL = CH_RESULT | CH_INPUT | CH_MODE | |||
CH_CASE = 0x0001 << 3, | |||
CH_HELP = 0x0001 << 4, /* do NOT add to CH_ALL */ | |||
CH_ALL = CH_RESULT | CH_INPUT | CH_MODE | CH_CASE | |||
}; | |||
enum { | |||
@@ -252,7 +253,6 @@ void PCS_reset(void); | |||
void rlinit(void); | |||
void addcmd(int f, char *s); | |||
void addsrcfile(char *path); | |||
void askforchar(void); | |||
void askforreturn(void); | |||
@@ -295,7 +295,6 @@ void posterr(char *msg, ...); | |||
void postfatal(const char *msg, ...); | |||
void putposting(char *term, int type); | |||
void fetch_string_from_dbase(char *, size_t); | |||
void resetcmd(void); | |||
void shellpath(char *out, int limit, char *in); | |||
void sourcedir(char *dirlist); | |||
void myungetch(int c); | |||
@@ -309,9 +308,6 @@ bool writerefsfound(void); | |||
int findinit(const char *pattern_); | |||
MOUSE *getmouseaction(char leading_char); | |||
struct cmd *currentcmd(void); | |||
struct cmd *prevcmd(void); | |||
struct cmd *nextcmd(void); | |||
int egrep(char *file, FILE *output, char *format); | |||
int hash(char *ss); | |||
@@ -1,95 +0,0 @@ | |||
/*=========================================================================== | |||
Copyright (c) 1998-2000, The Santa Cruz Operation | |||
All rights reserved. | |||
Redistribution and use in source and binary forms, with or without | |||
modification, are permitted provided that the following conditions are met: | |||
*Redistributions of source code must retain the above copyright notice, | |||
this list of conditions and the following disclaimer. | |||
*Redistributions in binary form must reproduce the above copyright notice, | |||
this list of conditions and the following disclaimer in the documentation | |||
and/or other materials provided with the distribution. | |||
*Neither name of The Santa Cruz Operation nor the names of its contributors | |||
may be used to endorse or promote products derived from this software | |||
without specific prior written permission. | |||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS | |||
IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT falseT LIMITED TO, | |||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE | |||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT falseT LIMITED TO, PROCUREMENT OF | |||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |||
INTERRUPTION) | |||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | |||
DAMAGE. | |||
=========================================================================*/ | |||
/* cscope - interactive C symbol or text cross-reference | |||
* | |||
* command history | |||
*/ | |||
#include "global.h" | |||
static struct cmd *tail, *current; | |||
/* add a cmd to the history list */ | |||
void addcmd(int f, char *s) /* field number and command text */ | |||
{ | |||
struct cmd *h; | |||
h = malloc(sizeof(struct cmd)); | |||
if(tail) { | |||
tail->next = h; | |||
h->next = 0; | |||
h->prev = tail; | |||
tail = h; | |||
} else { | |||
tail = h; | |||
h->next = h->prev = 0; | |||
} | |||
h->field = f; | |||
h->text = strdup(s); | |||
current = 0; | |||
} | |||
/* return previous history item */ | |||
struct cmd *prevcmd(void) { | |||
if(current) { | |||
if(current->prev) /* stay on first item */ | |||
return current = current->prev; | |||
else | |||
return current; | |||
} else if(tail) | |||
return current = tail; | |||
else | |||
return NULL; | |||
} | |||
/* return next history item */ | |||
struct cmd *nextcmd(void) { | |||
if(current) { | |||
if(current->next) /* stay on first item */ | |||
return current = current->next; | |||
else | |||
return current; | |||
} else | |||
return NULL; | |||
} | |||
/* reset current to tail */ | |||
void resetcmd(void) { | |||
current = 0; | |||
} | |||
struct cmd *currentcmd(void) { | |||
return current; | |||
} |
@@ -160,17 +160,14 @@ static int wmode_input(const int c) { | |||
case KEY_DOWN: | |||
case KEY_RIGHT: | |||
field = (field + 1) % FIELDS; | |||
resetcmd(); | |||
break; | |||
case ctrl('P'): /* go to previous input field */ | |||
case KEY_UP: | |||
case KEY_LEFT: | |||
field = (field + (FIELDS - 1)) % FIELDS; | |||
resetcmd(); | |||
break; | |||
case KEY_HOME: /* go to first input field */ | |||
field = 0; | |||
resetcmd(); | |||
break; | |||
case KEY_LL: /* go to last input field */ | |||
curdispline = disprefs; | |||
@@ -206,7 +203,6 @@ static int wresult_input(const int c) { | |||
break; | |||
case KEY_LL: | |||
field = FIELDS - 1; | |||
resetcmd(); | |||
break; | |||
default: | |||
if(c > mdisprefs) { goto noredisp; } | |||
@@ -230,12 +226,10 @@ static int global_input(const int c) { | |||
break; | |||
case ctrl('K'): | |||
field = (field + (FIELDS - 1)) % FIELDS; | |||
resetcmd(); | |||
window_change |= CH_MODE; | |||
break; | |||
case ctrl('J'): | |||
field = (field + 1) % FIELDS; | |||
resetcmd(); | |||
window_change |= CH_MODE; | |||
break; | |||
case ctrl('H'): /* display previous page */ | |||
@@ -361,6 +355,13 @@ static int global_input(const int c) { | |||
case ctrl('E'): /* edit all lines */ | |||
editall(); | |||
break; | |||
case ctrl('S'): // toggle caseless | |||
caseless = !caseless; | |||
egrepcaseless(caseless); | |||
window_change |= CH_CASE; | |||
break; | |||
case EOF: | |||
myexit(0); | |||
default: | |||
return 0; | |||
} | |||
@@ -1,6 +1,8 @@ | |||
#ifndef KEYS_H | |||
#define KEYS_H | |||
#include <ncurses.h> | |||
/* Key macros */ | |||
/* These macros are not guaranteed to be defined, | |||
* however we wish to test for these anyways while | |||
@@ -420,7 +420,6 @@ int process_mouse() { | |||
field = p->y1 - FLDLINE; | |||
/* force it into range */ | |||
if(field >= FIELDS) { field = FIELDS - 1; } | |||
resetcmd(); | |||
return (false); | |||
} | |||
@@ -1,18 +1,65 @@ | |||
#include <stdio.h> | |||
#include <readline/readline.h> | |||
#include <readline/history.h> | |||
#include "global.h" | |||
#include "build.h" | |||
#include <ncurses.h> | |||
static int input_available = 0; | |||
static char input_char; | |||
static int input_char; | |||
char input_line[PATLEN + 1]; | |||
/* used for saving a line not [Enter]-ed yet, | |||
* so its not lost if the user scrolls the history | |||
*/ | |||
static struct PARTIAL_LINE { | |||
bool is_active; | |||
char* line; | |||
int pos; | |||
} partial_line = { | |||
.line = NULL, | |||
.is_active = true | |||
}; | |||
static inline void previous_history_proxy(void); | |||
static inline void next_history_proxy(void); | |||
static inline int rebuild_reference(void); | |||
bool interpret(int c) { | |||
input_char = c; | |||
input_available = 1; | |||
rl_callback_read_char(); | |||
/* A switch is faster then binding Readline to a billion functions | |||
* and since KEY_* values can't be bound anyways (values too large | |||
* (because while the first argument of rl_bind_key() is an int, | |||
* only unsigned chars are valid)), handling everything here | |||
* creates consistency too. | |||
*/ | |||
switch(c){ | |||
case KEY_BACKSPACE: | |||
rl_rubout(1, 0); | |||
break; | |||
case KEY_UP: | |||
previous_history_proxy(); | |||
break; | |||
case KEY_DOWN: | |||
next_history_proxy(); | |||
break; | |||
case ctrl('R'): | |||
rebuild_reference(); | |||
break; | |||
case ESC: | |||
case ctrl('X'): | |||
process_mouse(); | |||
break; | |||
default: | |||
input_char = c; | |||
input_available = 1; | |||
rl_callback_read_char(); | |||
return 0; | |||
} | |||
//XXX: | |||
// rl_bind_key(ctrl('\\'), /**/); /* bypass bindings */ | |||
window_change |= CH_INPUT; | |||
return 0; | |||
} | |||
@@ -20,7 +67,7 @@ static int getc_function(FILE *ignore) { | |||
UNUSED(ignore); | |||
input_available = 0; | |||
return (int)input_char; | |||
return input_char; | |||
} | |||
static int input_available_hook() { | |||
@@ -34,6 +81,8 @@ static void redisplay_function() { | |||
static void callback_handler(char *line) { | |||
if(!line) { return; } | |||
add_history(line); | |||
switch(input_mode) { | |||
case INPUT_NORMAL: | |||
strncpy(input_line, line, PATLEN); | |||
@@ -62,24 +111,7 @@ static void callback_handler(char *line) { | |||
} | |||
} | |||
static int ctrl_z() { | |||
kill(0, SIGTSTP); | |||
return 0; | |||
} | |||
static int toggle_caseless() { | |||
if(caseless == false) { | |||
caseless = true; | |||
postmsg2("Caseless mode is now ON"); | |||
} else { | |||
caseless = false; | |||
postmsg2("Caseless mode is now OFF"); | |||
} | |||
egrepcaseless(caseless); /* turn on/off -i flag */ | |||
return 0; | |||
} | |||
static int rebuild_reference() { | |||
static inline int rebuild_reference() { | |||
if(isuptodate == true) { | |||
postmsg("The -d option prevents rebuilding the symbol database"); | |||
return (false); | |||
@@ -99,7 +131,41 @@ static int rebuild_reference() { | |||
return (true); | |||
} | |||
static inline void previous_history_proxy(){ | |||
HIST_ENTRY* i = previous_history(); | |||
if(i){ | |||
if(partial_line.is_active){ | |||
free(partial_line.line); | |||
partial_line = (struct PARTIAL_LINE){ | |||
.line = rl_line_buffer, | |||
.pos = rl_point, | |||
.is_active = false | |||
}; | |||
}else{ | |||
free(rl_line_buffer); | |||
} | |||
// | |||
rl_line_buffer = strdup(i->line); | |||
rl_point = strlen(rl_line_buffer); | |||
} | |||
} | |||
static inline void next_history_proxy(){ | |||
HIST_ENTRY* i = next_history(); | |||
if(i){ | |||
free(rl_line_buffer); | |||
rl_line_buffer = strdup(i->line); | |||
rl_point = strlen(rl_line_buffer); | |||
}else if(!partial_line.is_active){ | |||
rl_line_buffer = strdup(partial_line.line); | |||
rl_point = partial_line.pos; | |||
partial_line.is_active = true; | |||
} | |||
} | |||
void rlinit() { | |||
using_history(); | |||
rl_catch_signals = 0; | |||
rl_catch_sigwinch = 0; | |||
rl_prep_term_function = NULL; | |||
@@ -110,17 +176,4 @@ void rlinit() { | |||
rl_input_available_hook = input_available_hook; | |||
rl_redisplay_function = redisplay_function; | |||
rl_callback_handler_install("", callback_handler); | |||
rl_bind_key(7, | |||
rl_rubout); // XXX: 7 is backspace for some reason (on my system anyways?) | |||
rl_bind_key(KEY_BACKSPACE, rl_rubout); | |||
rl_bind_key(EOF, myexit); | |||
rl_bind_key(ctrl('Z'), ctrl_z); | |||
rl_bind_key(ctrl('Z'), toggle_caseless); | |||
rl_bind_key(ctrl('R'), rebuild_reference); | |||
rl_bind_key(ESC, process_mouse); /* possible unixpc mouse selection */ | |||
rl_bind_key(ctrl('X'), process_mouse); /* mouse selection */ | |||
// rl_bind_key(ctrl('\\'), /**/); /* bypass bindings */ | |||
} |