#include "globals.h"
#include "vendor.h"
#ifndef X_NOT_POSIX
#include <dirent.h>
#else
#ifdef SYSV
#include <dirent.h>
#else
#ifdef USG
#include <dirent.h>
#else
#include <sys/dir.h>
#ifndef dirent
#define dirent direct
#endif
#endif
#endif
#endif
#ifdef DEBUG
static char error_buf[BUFSIZ];
#endif
static void AddToCurrentSection(Manual * local_manual, char * path);
static void InitManual(Manual * l_manual, char * label);
static void ReadCurrentSection(Manual * local_manual, char * path);
static void ReadMandescFile(SectionList ** section_list, char * path);
static void SortAndRemove(Manual *man, int number);
static void SortList(SectionList ** list);
#define SECT_ERROR -1
#ifndef Byte
#define Byte unsigned char
#endif
#ifndef reg
#define reg register
#endif
static void sortstrs (Byte *data[], int size, Byte *otherdata[]);
static void sortstrs_block (Byte **, Byte **, int, Byte, Byte **, Byte **);
static void sortstrs_block_oo (Byte **, Byte **, int, Byte, int *, int *, Byte **, Byte **);
int
Man(void)
{
SectionList *list = NULL;
char *ptr, *lang = 0, manpath[BUFSIZ], buf[BUFSIZ], *path, *current_label;
int sect, num_alloced;
*manpath = '\0';
if ((ptr = getenv("MANPATH")) != NULL)
strcpy(manpath, ptr);
if (ptr == NULL || streq(ptr , "") || ptr[strlen(ptr) - 1] == ':') {
lang = getenv("LANG");
#ifdef MANCONF
if (!ReadManConfig(manpath + strlen(manpath)))
#endif
{
#ifdef MANCONF
if (manpath[strlen(manpath) - 1] != ':')
strcat(manpath, ":");
#endif
strcat(manpath, SYSMANPATH);
#ifdef LOCALMANPATH
strcat(manpath, ":");
strcat(manpath, LOCALMANPATH);
#endif
}
}
for ( path = manpath ; (ptr = index(path , ':')) != NULL ; path = ++ptr) {
*ptr = '\0';
if (lang != 0) {
strcpy(buf, path);
strcat(buf, "/");
strncat(buf, lang, sizeof(buf) - strlen(path) + 1);
buf[sizeof(buf) - strlen(path) + 1] = '\0';
ReadMandescFile(&list, buf);
}
ReadMandescFile(&list, path);
}
if (lang != 0) {
strcpy(buf, path);
strcat(buf, "/");
strncat(buf, lang, sizeof(buf) - strlen(path) + 1);
buf[sizeof(buf) - strlen(path) + 1] = '\0';
ReadMandescFile(&list, buf);
}
ReadMandescFile(&list, path);
SortList(&list);
sect = 0;
num_alloced = SECTALLOC;
manual = (Manual *) XtMalloc( sizeof(Manual) * num_alloced );
InitManual( manual, list->label );
manual[sect].flags = list->flags;
current_label = NULL;
while ( list != NULL ) {
SectionList * old_list;
if ( current_label == NULL || streq(list->label, current_label) )
AddToCurrentSection( manual + sect, list->directory);
else {
if (manual[sect].nentries == 0) {
XtFree(manual[sect].blabel);
manual[sect].blabel = list->label;
manual[sect].flags = list->flags;
}
else {
if ( ++sect >= num_alloced ) {
num_alloced += SECTALLOC;
manual = (Manual *) XtRealloc ( (char *) manual,
(sizeof(Manual) * num_alloced));
if (manual == NULL)
PrintError("Could not allocate memory for manual sections.");
}
InitManual( manual + sect, list->label );
manual[sect].flags = list->flags;
}
AddToCurrentSection( manual + sect, list->directory);
}
current_label = list->label;
old_list = list;
list = list->next;
XtFree((char *) old_list);
}
if (manual[sect].nentries != 0)
sect++;
SortAndRemove(manual, sect);
#ifdef notdef
DumpManual(sect);
#endif
if (sect == 0)
PrintError("No manual pages found.");
manual = (Manual *) XtRealloc( (char *) manual, (sizeof(Manual) * sect));
if (manual == NULL)
PrintError("Could not allocate memory for manual sections.");
return(sect);
}
static void
SortList(SectionList ** list)
{
SectionList * local;
SectionList *head, *last, *inner, *old;
if (*list == NULL)
PrintError("No manual sections to read, exiting.");
last = NULL;
for ( local = *list ; local->next != NULL ; local = local->next) {
if ( local->flags ) {
if ( local == *list )
break;
head = local;
for (old = 0 ; (local->next != NULL) && (local->flags)
; old = local, local = local->next);
if (old != 0) {
last->next = old->next;
old->next = *list;
*list = head;
}
break;
}
last = local;
}
for (local = *list; local->next != NULL; local = local->next) {
head = local;
old = inner = local->next;
while (inner != NULL) {
if (streq(inner->label, local->label)) {
if (old != inner) {
old->next = inner->next;
last = inner->next;
inner->next = head->next;
head->next = inner;
head = inner;
old = inner = last;
continue;
}
else
head = inner;
}
old = inner;
inner = inner->next;
}
}
}
static void
ReadMandescFile(SectionList ** section_list, char * path)
{
char mandesc_file[BUFSIZ];
FILE * descfile;
char string[BUFSIZ], local_file[BUFSIZ];
Boolean use_defaults = TRUE;
char *cp;
snprintf(mandesc_file, sizeof(mandesc_file), "%s/%s", path, MANDESC);
if ( (descfile = fopen(mandesc_file, "r")) != NULL) {
while ( fgets(string, BUFSIZ, descfile) != NULL) {
string[strlen(string)-1] = '\0';
if ( streq(string, NO_SECTION_DEFAULTS) ) {
use_defaults = FALSE;
continue;
}
if ((cp = index(string,'\t')) != NULL) {
char *s;
*cp++ = '\0';
strcpy(local_file, MAN);
strcat(local_file, string);
if ((s = index(cp,'\t')) != NULL) {
*s++ = '\0';
if (streq(s, SUFFIX))
AddNewSection(section_list, path, local_file, cp, MSUFFIX);
else if (streq(s, FOLD))
AddNewSection(section_list, path, local_file, cp, MFOLD);
else if (streq(s, FOLDSUFFIX))
AddNewSection(section_list, path, local_file, cp, MFOLDSUFFIX);
else
AddNewSection(section_list, path, local_file, cp, MNULL);
} else
AddNewSection(section_list, path, local_file, cp, MNULL);
} else {
snprintf(local_file, sizeof(local_file), "%s%c", MAN, string[0]);
AddNewSection(section_list, path, local_file, (string + 1), FALSE );
#ifdef SEARCHOTHER
snprintf(local_file, sizeof(local_file), "%s%c", SEARCHOTHER, string[0]);
AddNewSection(section_list, path, local_file, (string + 1), FALSE);
#endif
}
}
fclose(descfile);
}
if (use_defaults)
AddStandardSections(section_list, path);
}
void
AddNewSection(
SectionList **list,
char * path, char * file, char * label,
int flags)
{
SectionList * local_list, * end;
char full_path[BUFSIZ];
local_list = (SectionList *) XtMalloc(sizeof(SectionList));
if (*list != NULL) {
for ( end = *list ; end->next != NULL ; end = end->next );
end->next = local_list;
}
else
*list = local_list;
local_list->next = NULL;
local_list->label = StrAlloc(label);
snprintf(full_path, sizeof(full_path), "%s/%s", path, file);
local_list->directory = StrAlloc(full_path);
local_list->flags = flags;
}
static void
AddToCurrentSection(Manual * local_manual, char * path)
{
char temp_path[BUFSIZ];
#if defined(__OpenBSD__) || defined(__NetBSD__)
snprintf(temp_path, sizeof(temp_path), "%s/%s", path, MACHINE);
ReadCurrentSection(local_manual, temp_path);
#endif
ReadCurrentSection(local_manual, path);
snprintf(temp_path, sizeof(temp_path), "%s.%s", path, COMPRESSION_EXTENSION);
ReadCurrentSection(local_manual, temp_path);
}
static void
ReadCurrentSection(Manual * local_manual, char * path)
{
DIR * dir;
register struct dirent *dp;
register int nentries;
register int nalloc;
char full_name[BUFSIZ], *ptr;
if((dir = opendir(path)) == NULL) {
#ifdef DEBUG
snprintf(error_buf, sizeof(error_buf), "Can't open directory %s", path);
PopupWarning(NULL, error_buf);
#endif
return;
}
if ( (ptr = rindex(path, '.')) != NULL) {
#if !defined(__SCO__) && !defined(ISC)
if (streq(ptr + 1, COMPRESSION_EXTENSION))
#else
if (strpbrk(ptr + 1, COMPRESSION_EXTENSIONS) != NULL)
#endif
*ptr = '\0';
#ifdef GZIP_EXTENSION
else if (streq(ptr + 1, GZIP_EXTENSION))
*ptr = '\0';
#endif
}
nentries = local_manual->nentries;
nalloc = local_manual->nalloc;
while( (dp = readdir(dir)) != NULL ) {
char * name = dp->d_name;
if (name[0] == '.')
continue;
#ifndef CRAY
if (index(name, '.') == NULL)
continue;
#endif
if( nentries >= nalloc ) {
nalloc += ENTRYALLOC;
local_manual->entries =(char **) XtRealloc((char *)local_manual->entries,
nalloc * sizeof(char *));
local_manual->entries_less_paths =
(char **) XtRealloc((char *)local_manual->entries_less_paths,
nalloc * sizeof(char *));
}
snprintf(full_name, sizeof(full_name), "%s/%s", path, name);
if ( (ptr = rindex(full_name, '.')) != NULL) {
#if !defined(__SCO__) && !defined(ISC)
if (streq(ptr + 1, COMPRESSION_EXTENSION))
#else
if (strpbrk(ptr + 1, COMPRESSION_EXTENSIONS) != NULL)
#endif
*ptr = '\0';
#ifdef GZIP_EXTENSION
else if (streq(ptr + 1, GZIP_EXTENSION))
*ptr = '\0';
#endif
#ifdef IGNORE_EXTENSION
else if (streq(ptr + 1, IGNORE_EXTENSION)) {
continue;
}
#endif
}
local_manual->entries[nentries] = StrAlloc(full_name);
local_manual->entries_less_paths[nentries] =
rindex(local_manual->entries[nentries], '/');
if ( local_manual->entries_less_paths[nentries] == NULL )
PrintError("Internal error while cataloging manual pages.");
++ nentries;
}
local_manual->nentries = nentries;
local_manual->nalloc = nalloc;
closedir(dir);
}
static void
SortAndRemove(Manual *man, int number)
{
int i;
char *l1, *l2, **s1;
for ( i = 0; i < number; man++, i++) {
register int i2 = 0;
#ifdef DEBUG
printf("sorting section %d - %s\n", i, man->blabel);
#endif
s1 = (char **)malloc(man->nentries * sizeof(char *));
for (i2=0; i2<man->nentries; i2++)
if ((s1[i2] = rindex(man->entries_less_paths[i2], '.')) != NULL)
*s1[i2] = '\0';
sortstrs ( (Byte **)man->entries_less_paths, man->nentries, (Byte **)man->entries );
for (i2=0; i2<man->nentries; i2++)
if (s1[i2] != NULL) *s1[i2] = '.';
free(s1);
#ifdef DEBUG
printf("removing from section %d.\n", i);
#endif
{
register int j, k, nent, nentm1;
int j2;
nent = man -> nentries;
nentm1 = nent - 1;
j = 0;
l2 = man->entries_less_paths[j++];
if ( l2 == NULL )
PrintError("Internal error while removing duplicate manual pages.");
while ( j < nentm1 )
{
l1 = l2;
l2 = man->entries_less_paths[j++];
if ( l2 == NULL )
PrintError("Internal error while removing duplicate manual pages."
);
if ( streq(l1,l2) )
{
j2 = j-1;
k = j2;
while ( j < nent )
{
man -> entries_less_paths[k] = man -> entries_less_paths[j];
man -> entries[k++] = man -> entries[j++];
}
j = j2;
-- man -> nentries;
-- nent;
-- nentm1;
}
}
}
}
}
static void
sortstrs (Byte *data[], int size, Byte *otherdata[])
{
Byte **sp, **ep;
Byte **othersp, **otherep;
int *origorder;
origorder = (int *) calloc (size, sizeof(int));
if ( origorder )
{
reg int i;
for ( i=0; i < size; ++i )
origorder[i] = i;
}
sp = data;
ep = &data[size-1];
othersp = otherdata;
otherep = &otherdata[size-1];
if ( origorder )
{
sortstrs_block_oo ( sp, ep, 0, 0x80, origorder, &origorder[size-1],
othersp, otherep );
free (origorder);
}
else
sortstrs_block ( sp, ep, 0, 0x80, othersp, otherep );
}
static void
sortstrs_block (
Byte **start,
Byte **end,
int offset,
Byte mask,
Byte **otherstart,
Byte **otherend)
{
reg Byte **sp, **ep;
reg Byte m;
reg int off;
reg Byte *t;
reg int curstrlen;
int maxstrlen;
Byte **othersp, **otherep;
#define newstring(ptr) \
{ \
t = *ptr; \
curstrlen = 0; \
while ( *t++ ) ++ curstrlen; \
if ( curstrlen > maxstrlen ) maxstrlen = curstrlen; \
t = *ptr; \
}
maxstrlen = 0;
sp = start;
ep = end;
off = offset;
m = mask;
othersp = otherstart;
otherep = otherend;
while (1)
{
newstring(sp)
while (((sp != ep) && ((curstrlen < off) || ((t[off] & m) == 0))))
{
++ sp;
++ othersp;
newstring(sp)
}
if ( sp == ep )
break;
newstring(ep);
while (((sp != ep) && (curstrlen >= off) && ((t[off] & m) != 0)))
{
-- ep;
-- otherep;
newstring(ep)
}
if ( sp == ep )
break;
t = *sp;
*sp = *ep;
*ep = t;
t = *othersp;
*othersp = *otherep;
*otherep = t;
}
t = *sp;
if ((curstrlen < off) || ((t[off] & m) == 0))
{
if ( ep != end )
{
++ ep;
++ otherep;
}
}
else
{
if ( sp != start )
{
-- sp;
-- othersp;
}
}
m >>= 1;
if ( m == 0 )
{
m = 0x80;
if ( ++off >= maxstrlen )
return;
}
if ( sp != start )
sortstrs_block ( start, sp, off, m, otherstart, othersp );
if ( ep != end )
sortstrs_block ( ep, end, off, m, otherep, otherend );
}
static void
sortstrs_block_oo (
Byte **start,
Byte **end,
int offset,
Byte mask,
int *ostart,
int *oend,
Byte **otherstart,
Byte **otherend)
{
reg Byte **sp, **ep;
reg int *osp, *oep;
reg Byte m;
reg int off;
reg Byte *t;
reg int u;
reg int curstrlen;
int maxstrlen;
Byte **othersp, **otherep;
#define newstring(ptr) \
{ \
t = *ptr; \
curstrlen = 0; \
while ( *t++ ) ++ curstrlen; \
if ( curstrlen > maxstrlen ) maxstrlen = curstrlen; \
t = *ptr; \
}
maxstrlen = 0;
sp = start;
ep = end;
osp = ostart;
oep = oend;
off = offset;
m = mask;
othersp = otherstart;
otherep = otherend;
while (1)
{
newstring(sp)
while (((sp != ep) && ((curstrlen < off) || ((t[off] & m) == 0))))
{
++ sp;
++ osp;
++ othersp;
newstring(sp)
}
if ( sp == ep )
break;
newstring(ep);
while (((sp != ep) && (curstrlen >= off) && ((t[off] & m) != 0)))
{
-- ep;
-- oep;
-- otherep;
newstring(ep)
}
if ( sp == ep )
break;
t = *sp;
*sp = *ep;
*ep = t;
t = *othersp;
*othersp = *otherep;
*otherep = t;
u = *osp;
*osp = *oep;
*oep = u;
}
t = *sp;
if ((curstrlen < off) || ((t[off] & m) == 0))
{
if ( ep != end )
{
++ ep;
++ oep;
++ otherep;
}
}
else
{
if ( sp != start )
{
-- sp;
-- osp;
-- othersp;
}
}
m >>= 1;
if ( m == 0 )
{
m = 0x80;
if ( ++off >= maxstrlen )
{
reg Byte **cp;
reg int *ocp;
Byte **othercp;
if ( sp != start )
{
cp = start;
ocp = ostart;
othercp = otherstart;
while ( cp != sp )
{
if ( *ocp > *(ocp+1) )
{
t = *(cp+1);
*(cp+1) = *cp;
*cp = t;
t = *(othercp+1);
*(othercp+1) = *othercp;
*othercp = t;
u = *(ocp+1);
*(ocp+1) = *ocp;
*ocp = u;
if ( cp != start )
{
-- cp;
-- ocp;
-- othercp;
continue;
}
}
++ cp;
++ ocp;
++ othercp;
}
}
if ( ep != end )
{
cp = ep;
ocp = oep;
othercp = otherep;
while ( cp != end )
{
if ( *ocp > *(ocp+1) )
{
t = *(cp+1);
*(cp+1) = *cp;
*cp = t;
t = *(othercp+1);
*(othercp+1) = *othercp;
*othercp = t;
u = *(ocp+1);
*(ocp+1) = *ocp;
*ocp = u;
if ( cp != ep )
{
-- cp;
-- ocp;
-- othercp;
continue;
}
}
++ cp;
++ ocp;
++ othercp;
}
}
return;
}
}
if ( sp != start )
sortstrs_block_oo ( start, sp, off, m, ostart, osp, otherstart, othersp );
if ( ep != end )
sortstrs_block_oo ( ep, end, off, m, oep, oend, otherep, otherend );
}
static void
InitManual(Manual * l_manual, char * label)
{
bzero( l_manual, sizeof(Manual) );
l_manual->blabel = label;
}
#if defined(DEBUG)
void
DumpManual(int number)
{
register int i,j;
for ( i = 0; i < number; i++) {
printf("label: %s\n", manual[i].blabel);
for (j = 0; j < manual[i].nentries; j++)
printf("%s\n", manual[i].entries[j]);
}
}
#endif
#ifdef MANCONF
#if defined(MANCONFIGSTYLE_FreeBSD)
Bool
ReadManConfig(char manpath[])
{
FILE *fp;
char line[BUFSIZ];
char *path;
Bool firstpath = TRUE;
if (!(fp = fopen(MANCONF, "r")))
return(FALSE);
while (fgets(line, sizeof(line), fp)) {
path = strtok(line, " \t\n");
if (!path || *path == '#')
continue;
if (strcmp(path, "MANPATH_MAP") == 0)
path = strtok((char *)NULL, " \t\n");
else if (strcmp(path, "MANDATORY_MANPATH") != 0 &&
strcmp(path, "OPTIONAL_MANPATH") != 0)
return(FALSE);
path = strtok((char *)NULL, " \t\n");
if (!path || *path == '#')
return FALSE;
if (firstpath) {
strcpy(manpath, path);
firstpath = FALSE;
}
else if (!strstr(manpath,path)) {
strcat(manpath, ":");
strcat(manpath, path);
}
}
fclose(fp);
return(!firstpath);
}
#elif defined(MANCONFIGSTYLE_Linux)
Bool
ReadManConfig(char manpath[])
{
FILE *fp;
char line[BUFSIZ];
char *path;
Bool firstpath = TRUE;
if (!(fp = fopen(MANCONF, "r")))
return(FALSE);
while (fgets(line, sizeof(line), fp)) {
path = strtok(line, " \t\n");
if (!path || *path == '#' || (strcmp(path, "MANPATH") != 0))
continue;
path = strtok((char *)NULL, " \t\n");
if (!path || *path == '#')
return FALSE;
if (firstpath) {
strcpy(manpath, path);
firstpath = FALSE;
}
else {
strcat(manpath, ":");
strcat(manpath, path);
}
}
fclose(fp);
return(!firstpath);
}
#elif defined(MANCONFIGSTYLE_OpenBSD)
#include <glob.h>
Bool
ReadManConfig(char manpath[])
{
FILE *fp;
char line[BUFSIZ];
char *path;
Bool firstpath = TRUE;
glob_t gs;
int i;
if (!(fp = fopen(MANCONF, "r")))
return(FALSE);
while (fgets(line, sizeof(line), fp)) {
path = strtok(line, " \t\n");
if (!path || *path == '#')
continue;
if (strcmp(path, "_default")) {
continue;
}
memset(&gs, 0, sizeof(glob_t));
while ((path = strtok((char *)NULL, " \t\n"))) {
if (glob(path, GLOB_BRACE, NULL, &gs) < 0) {
fclose(fp);
return FALSE;
}
}
for (i = 0; i < gs.gl_pathc; i++) {
if (firstpath) {
strcpy(manpath, gs.gl_pathv[i]);
firstpath = FALSE;
}
else {
strcat(manpath, ":");
strcat(manpath, gs.gl_pathv[i]);
}
}
globfree(&gs);
}
fclose(fp);
return(!firstpath);
}
#elif defined(MANCONFIGSTYLE_BSD)
Bool
ReadManConfig(manpath)
char manpath[];
{
FILE *fp;
char line[BUFSIZ];
char *path;
Bool firstpath = TRUE;
if (!(fp = fopen(MANCONF, "r")))
return(FALSE);
while (fgets(line, sizeof(line), fp)) {
path = strtok(line, " \t\n");
if (!path || *path == '#' || strcmp(path, "_default"))
continue;
while ((path = strtok((char *)NULL, " \t\n"))) {
if (firstpath) {
strcpy(manpath, path);
firstpath = FALSE;
}
else {
strcat(manpath, ":");
strcat(manpath, path);
}
}
}
fclose(fp);
return(!firstpath);
}
#else
#error "MANCONF defined (in vendor.h) for unknown operating system."
#endif
#endif