// File: /adm/daemons/aproposd.c
// - apropos daemon - maintains dictionary
//
// History:
// Created by Whiplash@TMI - 92/02/11
// Minor rewrite by Truilkan@TMI - 92/02/11
// Attic'd prior to TMI Release 1.0.1
// Rewritten by Robocoder@TMI-2 (04-Jul-93)
// Leto added some security checks (Dec-21 1994)
//
// See also: /cmds/xtra/_apropos.c, /cmds/adm/_aproposm.c
#include <config.h>
#include <logs.h>
#include <commands.h>
#include <man.h>
#include <uid.h>
#include <driver/origin.h>
#define APROPOS_DATA DATA_DIR "/adm/daemons/apropos"
// This mapping is indexed by phrase; the value is the name of the function
// subdirectory
mapping apropos_phrases;
static int already_updating;
// prototypes for local functions
void scan_file(string x, string y);
void hash(string x, string y);
void update_aplist(int x);
void process_dir(string x);
// initialization
void create() {
string s;
seteuid(ROOT_UID);
already_updating = 0;
if (!restore_object(APROPOS_DATA)) {
apropos_phrases = allocate_mapping(400);
save_object(APROPOS_DATA);
}
}
void query_update() {
write("Files left to process: " + already_updating + "\n");
}
// scan the doc subdirectories and [re]build apropos database
void update_aplist(int x) {
string *docdirs;
string tmp;
int i, s;
#if 0
tmp = base_name(previous_object());
if (!adminp(geteuid(previous_object())) &&
tmp != CMD_APROPOSM && tmp != CMD_APROPOS &&
tmp != file_name(this_object())) {
return;
}
#else
if (origin() != ORIGIN_CALL_OUT) {
tmp = base_name(previous_object());
if (!adminp(geteuid(previous_object())) &&
tmp != CMD_APROPOSM && tmp != CMD_APROPOS)
return;
}
#endif
if (already_updating) {
return;
}
already_updating = 1;
// process each subdirectory
// docdirs = MAN_DIRLIST;
docdirs = EXTENDED_MAN_DIR_LIST;
s = sizeof(docdirs);
for (i = 0; i < s; i++) {
// call_out("process_dir", 1, docdirs[i]);
process_dir(docdirs[i]);
}
already_updating--;
}
// apropos
// called by /cmds/xtra/_apropos.c
// returns an array of strings
// each string is of the form:
// doc file name: synopsis containing matching phrase
string *apropos(string str, int quiet) {
string *r;
mapping k;
int i, j, s;
if (base_name(previous_object()) != CMD_APROPOS) return ({ });
if (!str || str == "") {
#ifdef APROPOS_LOG
log_file(APROPOS_LOG, wrap("apropos(): invalid string, from: " +
"[" + file_name(previous_object()) + "] " +
(this_player() ?
capitalize(geteuid(this_player())) : "unknown") +
" " + extract(ctime(time()), 4, 15)));
#endif
return ({ });
}
if (!apropos_phrases || !sizeof(apropos_phrases)) {
if (!already_updating) {
call_out("update_aplist", 1, 0);
return ({
"Updating apropos database. This may take a while. Please try again, later."
});
} else {
return ({ "Update of apropos database in progress." });
}
}
// find matching phrases
r = regexp(keys(apropos_phrases), ".*" + str + ".*");
// prepend phrases with dirname
if (!quiet) {
s = sizeof(r);
for (i = 0; i < s; i++) {
r[i] = apropos_phrases[r[i]] + ": " + r[i];
}
}
return r;
}
// save_me
// called via call_out
void save_me() {
save_object(APROPOS_DATA);
already_updating = 0;
}
// process_dir
// called via call_out
void process_dir(string dirname) {
string *docfiles;
string *lines;
int i, s;
string tmp, tmp2;
// if (origin() != ORIGIN_CALL_OUT) return;
if (origin() != ORIGIN_LOCAL) return;
// check this is a directory
// tmp = man_root + "/" + dirname + "/";
tmp = dirname + "/";
if (file_size(tmp) != -2) {
return;
}
docfiles = get_dir(tmp);
s = sizeof(docfiles);
already_updating += s;
// process each file
for (i = 0; i < s; i++) {
tmp2 = tmp + docfiles[i];
if (file_size(tmp2) <= 0 || docfiles[i] == "README") {
// skip empty files, files I don't have read permission to,
// ".", ".." and any directories (which I don't know about)
already_updating--;
continue;
}
// process file
call_out("scan_file", 1, dirname, tmp2);
}
}
int process_file(string filename) {
string *p, tmp;
int i, j, k, s;
if (!filename || filename == "")
return 0;
if (already_updating) {
return 0;
}
already_updating = 1;
filename = resolv_path(this_player()->query("cwd"), filename);
j = strlen(filename);
// p = MAN_DIRLIST;
p = EXTENDED_MAN_DIR_LIST;
s = sizeof(p);
for (i = 0; i < s; i++) {
// tmp = man_root + "/" + p[i] + "/";
tmp = p[i] + "/";
k = strlen(tmp);
if (j >= k && tmp == filename[0..k-1])
break;
}
if (i == s) {
already_updating = 0;
return 0; // not found
}
scan_file( p[i], filename );
return 1;
}
void scan_file(string dirname, string pathname) {
string *lines;
string tmp;
int i, j, k, s;
tmp = read_file(pathname);
lines = (tmp) ? explode(tmp, "\n") : ({ });
s = sizeof(lines);
i = member_array("NAME", lines);
if (i == -1) {
i = member_array(".SH NAME", lines);
k = 1;
} else
k = 0;
if (i != -1) {
// concat NAME lines
tmp = "";
if (k)
// for (j = i+1; j < s && lines[j] != ""; j++) {
for (j = i+1; j < s && lines[j] != "" && lines[j] != " "; j++) {
if (lines[j] == ".nf") continue;
tmp += " " + lines[j];
}
else
for (j = i+1; j < s && lines[j] != "" && lines[j] != " "; j++) {
tmp += lines[j];
}
// this fix for words that get abbrev'd doesn't work if
// there's a proper hyphenation at the end of a line
tmp = replace_string(tmp, "- ", "");
call_out( "hash", 1, tmp, dirname );
} else
already_updating--; // else NAME not found or lines = ({ })
}
void hash(string tmp, string dirname) {
string *lines;
int i, k, s;
// strip spaces
lines = explode(tmp, " ");
s = sizeof(lines);
for (i = 0, k = 0; i < s; i++) {
if (lines[i] == "") continue;
lines[k++] = lines[i];
}
// compacting the array should to reduce iterations in the next loop
lines = exclude_array(lines, k, s);
s = sizeof(lines);
tmp = implode(lines, " ");
// add phrase to database
apropos_phrases[tmp] = dirname;
already_updating--;
if (already_updating == 0)
save_me();
}
string *query_phrases() {
return keys(apropos_phrases);
}
string *query_files() {
return values(apropos_phrases);
}
int compare(string x, string y) {
return strcmp(x, y);
}