home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Atari FTP
/
ATARI_FTP_0693.zip
/
ATARI_FTP_0693
/
Mint
/
mintman4.zoo
/
mintman
/
man.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-15
|
25KB
|
756 lines
/******************************************************************/
/* man.c - a simple-minded man program for MiNT and TOS */
/* Copyright (c) 1992 by HPP Biersma (schuller@dutiag.tudelft.nl) */
/* This program comes under the GNU public license - */
/* see the file "copying" for details. */
/******************************************************************/
/* bugs: - not UN*X compatible, either in source-code or behavior */
/* - string operations are not safe, ie not limits checked */
/******************************************************************/
/* version: 0.4 (fourth released version), December 15, 1992 */
/* written for: - GCC version 2.3.1, patchlevel 1 */
/* - mintlib patchlevel 25 */
/* compile with: gcc -o man.ttp man.c -mbaserel -mpcrel -O2 */
/******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stat.h>
#include <dirent.h> /* used for opendir(), readdir(), closedir() */
#include <mintbind.h>
/* Comment out the following definitions if you want subprograms */
/* (nroff, less and cat) to get UN*X (not GEMDOS-like) filenames. */
#define SUBPROGRAMS_GET_GEMDOS_NAMES /* the longest #define yet ? */
/* Define a enumerated type used to indicated mode of operation */
typedef enum {MODE_UNKNOWN, MODE_MAN, MODE_MAN_F, MODE_MAN_K} mode_type;
/* Maximum length of path + file name */
#define NAME_LENGTH 256
/* prototypes for local (static) functions */
static void volatile option_error(const int error);
static int search_man_path(const char *section, const char *title);
static int search_man_dir(const char *section, const char *title,
const char *dir_name);
static int search_section_specific(const char *section, const char *title,
const char *dir_name);
static int search_section_general(const char section_code, const char *title,
const char *dir_name);
static int find_best_file(const char *dir_name, const char *title,
const char section_code,
char *result_basename, char *result_fullname);
static void display_file(const char *filename);
static void format_file(const char *mandir_name, const char *manfile_name,
const char *catfile_name);
static int check_directory_name(const char *dirname);
static void build_catdir_name(const char *dirname, const char section_code,
char *catdir_name);
static void build_mandir_name(const char *dirname, const char section_code,
char *mandir_name);
static int handle_whatis(const char *name, const mode_type mode);
static void strip_filename(const char *name, char *stripped_name);
static int print_apropos(const char *mandir, const char *name);
/* Extern and local variables */
static char man_path[NAME_LENGTH] = "/usr/man";
static char pager[NAME_LENGTH] = "less -r -e";
static char macro_package[NAME_LENGTH] = "an";
static int use_pager = 1; /* Use a pager unless '-' option or not a tty */
void main(int argc, char *argv[])
{
char *section = NULL, *ptr;
mode_type use_mode = MODE_UNKNOWN;
int real_parameter_found = 0;
/* Set the search path for the man pages*/
if ((ptr = getenv("MANPATH")) != NULL)
strncpy(man_path, ptr, NAME_LENGTH - 1);
/* Set the name of the pager used to show the man pages */
if ((ptr = getenv("PAGER")) != NULL)
strncpy(pager, ptr, NAME_LENGTH - 1);
/* We are very forgiving in reading the command-line; if an option is */
/* given several times, the last one given counts at the time it is */
/* used. Also, several section names may be given without a title in */
/* between. We only give an error message if no real parameter is */
/* given, when an unknown or incomplete option is given, or when */
/* options of different modes of operation are combined. Options that */
/* take a parameter may have a space between the option and the value.*/
argv += 1;
ptr = argv[0];
while (argc > 1)
{
if (*ptr == '-')
{
switch(ptr[1])
{
case 0x00:
/* Option '-' given: do not use a pager; use `cat' instead */
if ((use_mode == MODE_MAN_F) || (use_mode == MODE_MAN_K))
option_error(1); /* doesn't return */
use_mode = MODE_MAN;
use_pager = 0;
break;
case 'M':
/* Option '-M' given: alternative manual directory search path */
if (ptr[2] != 0x00)
strcpy(man_path, &(ptr[2]));
else
{
/* space used after '-M') */
if (argc == 2)
{
fprintf(stderr, "man: -M option needs an argument\n");
exit(1);
}
else
{
argv += 1;
strcpy(man_path, argv[0]);
argc -= 1;
}
} /* End of else (space after -M option) */
break;
case 'T':
/* Option '-T' given: alternative macro package to be used by nroff */
if ((use_mode == MODE_MAN_F) || (use_mode == MODE_MAN_K))
option_error(1); /* doesn't return */
use_mode = MODE_MAN;
if (ptr[2] != 0x00)
strcpy(macro_package, &(ptr[2]));
else
{
/* space used after '-T') */
if (argc == 2)
{
fprintf(stderr, "man: -T option needs an argument\n");
exit(1);
}
else
{
argv += 1;
strcpy(macro_package, argv[0]);
argc -= 1;
}
} /* End of else (space after -T option) */
break;
case 'k':
if ((use_mode == MODE_MAN) || (use_mode == MODE_MAN_F))
option_error(1); /* doesn't return */
use_mode = MODE_MAN_K;
break;
case 'f':
if ((use_mode == MODE_MAN) || (use_mode == MODE_MAN_K))
option_error(1); /* doesn't return */
use_mode = MODE_MAN_F;
break;
default:
fprintf(stderr, "man: no option %s supported\n", ptr);
option_error(0); /* doesn't return */
} /* End of switch (letter after `-') */
} /* End of if (option) */
else if ((strlen(ptr) < 3) && (*ptr >= '1') && (*ptr <= '8') &&
(use_mode != MODE_MAN_F) && (use_mode != MODE_MAN_K))
{
/* A section has been found */
section = ptr;
use_mode = MODE_MAN;
}
else
{
/* A real parameter has been found */
real_parameter_found = 1;
switch(use_mode)
{
case MODE_UNKNOWN:
use_mode = MODE_MAN;
case MODE_MAN:
if (search_man_path(section, ptr) == 0)
{
if (section == NULL)
fprintf(stderr, "man: %s not found\n", ptr);
else
fprintf(stderr, "man: %s (%s) not found\n", ptr, section);
}
break;
case MODE_MAN_F:
case MODE_MAN_K:
if (handle_whatis(ptr, use_mode) == 0)
fprintf(stderr, "man: %s not found in whatis database\n", ptr);
break;
} /* End of switch(use_mode) */
} /* End of else (parameter or title found) */
argc -= 1;
argv += 1;
ptr = argv[0];
}
if (real_parameter_found == 0)
option_error(0); /* no real parameters given during use */
exit(0);
} /* End of main() */
/* Either no real parameter was given (just options: error == 0) */
/* or options for different modes were combined (error == 1). */
static void volatile option_error(const int error)
{
if (error == 1)
fprintf(stderr, "man: options for different modes of operation cannot be combined\n");
fprintf(stderr, "usage: man [-] [-M path] [-T macro-package] [[section] title ...] ...\n");
fprintf(stderr, " man [-M path] -k keyword ...\n");
fprintf(stderr, " man [-M path] -f command ...\n");
exit(1);
} /* End of option_exit() */
/* Search all man directories until ready or end of path */
static int search_man_path(const char *section, const char *title)
{
char this_path[NAME_LENGTH], *path_ptr;
int title_done;
title_done = 0;
path_ptr = man_path;
while ((*path_ptr != 0x00) && (title_done == 0))
{
int done;
char *ptr;
strcpy(this_path, path_ptr);
ptr = this_path;
/* Find first path separator or end of path */
done = 0;
while (!done)
{
if ((*path_ptr == 0x00) ||
(*path_ptr == ';') ||
(*path_ptr == ','))
done = 1;
else
{
path_ptr += 1;
ptr += 1;
}
}
if (*path_ptr != 0x00)
path_ptr += 1;
*ptr = 0x00;
/* We now have a properly terminated search path in this_path */
/* while path_ptr points to the rest of the manual search path. */
title_done = search_man_dir(section, title, this_path);
} /* End of while (not end of manual search path && title not done) */
return(title_done);
} /* End of search_man_path */
/* Search a directory containing man?, cat? directories */
static int search_man_dir(const char *section, const char *title,
const char *dir_name)
{
int done;
char section_counter;
if (check_directory_name(dir_name) == 0)
{
fprintf(stderr, "man: directory %s on search path not found\n",
dir_name);
return(0);
}
if (section != NULL)
{
/* check whether a general or specific section is named */
if (section[1] == 0x00)
return(search_section_general(section[0], title, dir_name));
else
return(search_section_specific(section, title, dir_name));
}
for (section_counter = '1', done = 0;
section_counter <= '8' && done == 0;
section_counter++)
{
done = search_section_general(section_counter, title, dir_name);
}
return(done);
} /* End of search_man_dir() */
/* Search a section directory of the man dir for a specific man page */
/* This isused when the section name is of the form number+code. */
/* We only look for files with the name "dir/title.section" */
static int search_section_specific(const char *section, const char *title,
const char *dir_name)
{
char catdir_name[NAME_LENGTH],
catfile_name[NAME_LENGTH],
mandir_name[NAME_LENGTH],
manfile_name[NAME_LENGTH],
basename[NAME_LENGTH];
int catdir_exists, catfile_exists, manfile_exists;
struct stat catfile_stat, manfile_stat;
strcpy(basename, title);
strcat(basename, ".");
strcat(basename, section);
build_catdir_name(dir_name, section[0], catdir_name);
catdir_exists = check_directory_name(catdir_name);
strcpy(catfile_name, catdir_name);
strcat(catfile_name, "/");
strcat(catfile_name, basename);
catfile_exists = !stat(catfile_name, &catfile_stat);
build_mandir_name(dir_name, section[0], mandir_name);
strcpy(manfile_name, mandir_name);
strcat(manfile_name, "/");
strcat(manfile_name, basename);
manfile_exists = !stat(manfile_name, &manfile_stat);
if (catfile_exists == 1)
{
if (manfile_exists == 0)
{
/* Only the catfile exists */
display_file(catfile_name);
return(1);
}
else if (catfile_stat.st_mtime > manfile_stat.st_mtime)
{
/* Both files exist, catfile is up-to-date */
display_file(catfile_name);
return(1);
}
else
{
/* Both file exist, catfile is not up-of-date */
format_file(mandir_name, basename, catfile_name);
display_file(catfile_name);
return(1);
}
}
else /* catfile does not exist */
{
if (manfile_exists == 0)
return(0);
if (catdir_exists == 1)
{
/* Make a formatted version of the manfile in the catdir, */
/* then display the formatted manpage. */
format_file(mandir_name, basename, catfile_name);
display_file(catfile_name);
return(1);
}
else
{
/* Make a formatted version of the manfile in a temporary file, */
/* then display the temporary file. */
tmpnam(catfile_name);
format_file(mandir_name, basename, catfile_name);
display_file(catfile_name);
unlink(catfile_name);
return(1);
}
} /* End of else (catfile doesn't exist) */
return(0); /* This statement shouldn't be reached - better be safe */
} /* End of search_section_specific() */
/* Search a manual section looking for the best matching manual page */
static int search_section_general(const char section_code, const char *title,
const char *dir_name)
{
char catdir_name[NAME_LENGTH], mandir_name[NAME_LENGTH];
char catfile_basename[NAME_LENGTH], catfile_fullname[NAME_LENGTH];
char manfile_basename[NAME_LENGTH], manfile_fullname[NAME_LENGTH];
int catdir_exists, mandir_exists;
int catfile_type, manfile_type;
build_catdir_name(dir_name, section_code, catdir_name);
catdir_exists = check_directory_name(catdir_name);
build_mandir_name(dir_name, section_code, mandir_name);
mandir_exists = check_directory_name(mandir_name);
if (catdir_exists == 1)
catfile_type = find_best_file(catdir_name, title, section_code,
catfile_basename, catfile_fullname);
else
catfile_type = 0; /* indicates no catfile found */
if (mandir_exists == 1)
manfile_type = find_best_file(mandir_name, title, section_code,
manfile_basename, manfile_fullname);
else
manfile_type = 0; /* indicates no nanfile found */
if ((catfile_type == 0) && (manfile_type == 0)) /* no files found */
return(0);
if (catfile_type > manfile_type) /* better type catfile than manfile */
{
display_file(catfile_fullname);
return(1);
}
else if (catfile_type < manfile_type) /* better manfile than catfile */
{
if (catdir_exists == 1)
{
/* result from format is new catfile */
strcpy(catfile_fullname, catdir_name);
strcat(catfile_fullname, "/");
strcat(catfile_fullname, manfile_basename);
}
else
{
/* result from format is a temporary file */
tmpnam(catfile_fullname);
}
format_file(mandir_name, manfile_basename, catfile_fullname);
display_file(catfile_fullname);
if (catdir_exists == 0)
unlink(catfile_fullname);
return(1);
} /* End of else (better manfile than catfile) */
else /* catfile and manfile same quality */
{
if (strcmp(catfile_basename, manfile_basename) == 0)
{
/* Files have the same name - check if catfile up-to-date */
struct stat catfile_stat, manfile_stat;
stat(catfile_fullname, &catfile_stat);
stat(manfile_fullname, &manfile_stat);
if (catfile_stat.st_mtime > manfile_stat.st_mtime)
{
/* catfile is up-to-date */
display_file(catfile_fullname);
return(1);
}
else
{
/* manfile is newer than catfile */
format_file(mandir_name, manfile_basename, catfile_fullname);
display_file(catfile_fullname);
return(1);
}
} /* end of if (same basename) */
else
{
/* differing catfile and manfile - always take the catfile */
display_file(catfile_fullname);
return(1);
}
}
return(0); /* should never be reached - better be safe */
} /* End of search_section_general() */
/* Find the best matching file in a directory */
static int find_best_file(const char *dir_name, const char *title,
const char section_code,
char *result_basename, char *result_fullname)
{
/* Note on the return codes: higher number is better */
/* code 5: short section extension (.3 in xxxxcat3) */
/* code 4: extension .man */
/* code 3: extension .nr */
/* code 2: long section extension (.3x in xxxxcat3) */
/* code 1: any other extension (first found is kept) */
/* code 0: no file found at all */
int return_code;
DIR *search_dir;
struct dirent *this_entry;
char name_prefix[NAME_LENGTH];
size_t prefix_size;
return_code = 0;
strcpy(name_prefix, title);
strcat(name_prefix, ".");
prefix_size = strlen(name_prefix);
if ((search_dir = opendir(dir_name)) == (DIR *)NULL)
return(0);
while ((this_entry = readdir(search_dir)) != (struct dirent *)NULL)
{
if (strncmp(name_prefix, this_entry->d_name, prefix_size) == 0)
{
/* prefix matches - now check for type */
int this_type = 0;
char *extension;
if (strlen(this_entry->d_name) > prefix_size)
{
extension = &(this_entry->d_name[prefix_size]);
if ((extension[0] == section_code) &&
(extension[1] == 0x00))
this_type = 5;
else if (strcmp(extension, "man") == 0)
this_type = 4;
else if (strcmp(extension, "nr") == 0)
this_type = 3;
else if ((extension[0] == section_code) &&
/*(extension[1] != 0x00) && - we know this already */
(extension[2] == 0x00))
this_type = 2;
else
this_type = 1;
}
if (this_type > return_code)
{
return_code = this_type;
strcpy(result_basename, this_entry->d_name);
}
} /* end of if (prefix matches) */
} /* end of while (not end of directory) */
closedir(search_dir);
if (return_code > 0)
{
/* build result_fullname */
strcpy(result_fullname, dir_name);
strcat(result_fullname, "/");
strcat(result_fullname, result_basename);
}
return(return_code);
} /* End of find_best_file() */
/* Display a file. Use pager for tty, cat for file or pipe */
static void display_file(const char *filename)
{
char command_line[NAME_LENGTH], dosname[NAME_LENGTH];
#ifdef SUBPROGRAMS_GET_GEMDOS_NAMES
_unx2dos(filename, dosname); /* Send valid GEMDOS name through system */
#else
strcpy(dosname, filename);
#endif
if ((isatty(1) == 1) && /* GEMDOS handle 1 is stdout */
(use_pager == 1)) /* No option '-' given */
{
/* Use a pager to display on a terminal */
strcpy(command_line, pager);
strcat(command_line, " ");
strcat(command_line, dosname);
if (system(command_line) == 0) /* 0: successful execution */
return;
fprintf(stderr, "man: error executing pager - aborted (sorry)\n");
exit(1);
}
else
{
/* Use cat for anything but a tty or when '-' option is given */
strcpy(command_line, "cat ");
strcat(command_line, dosname);
if (system(command_line) == 0) /* 0: successful execution */
return;
fprintf(stderr, "man: error executing cat - aborted (sorry)\n");
exit(1);
}
} /* End of display_file() */
/* Format (nroff) a file and write the result to another file */
static void format_file(const char *mandir, const char *manfile,
const char *catfile)
{
char command_line[NAME_LENGTH], cwd[NAME_LENGTH];
#ifdef SUBPROGRAMS_GROK_GEMDOS_NAMES
char dosname[NAME_LENGTH];
#endif
getcwd(cwd, NAME_LENGTH);
if (chdir(mandir) != 0)
{
/* Note that the existence of the directory has been checked before */
fprintf(stderr, "man: change directory to %s failed\n", mandir);
exit(1);
}
strcpy(command_line, "nroff -m");
strcat(command_line, macro_package);
strcat(command_line, " ");
#ifdef SUBPROGRAMS_GROK_GEMDOS_NAMES
_unx2dos(manfile, dosname); /* send nroff a proper GEMDOS name */
strcat(command_line, dosname);
#else
strcat(command_line, manfile);
#endif
strcat(command_line, " >");
#ifdef SUBPROGRAMS_GROK_GEMDOS_NAMES
_unx2dos(catfile, dosname); /* redirect to a proper GEMDOS name */
strcat(command_line, dosname);
#else
strcat(command_line, catfile);
#endif
fprintf(stderr, "Formatting file. Wait...");
if (system(command_line) == 0) /* successful execution */
{
fprintf(stderr, "Done\n");
return;
}
else
{
fprintf(stderr, "Aborted (sorry)\n");
exit(1);
}
if (chdir(cwd) != 0)
{
fprintf(stderr, "man: cannot change back to directory %s\n", cwd);
exit(1);
}
} /* End of format_file() */
/* Check if a dirname is a valid directory */
static int check_directory_name(const char *dirname)
{
struct stat dir_stat;
if (stat(dirname, &dir_stat) == -1)
return(0);
else if (dir_stat.st_mode & S_IFDIR == 0)
{
fprintf(stderr, "man: %s is not a directory\n", dirname);
return(0);
}
else
return(1);
} /* End of check_directory_name() */
/* Build the name of cat dir #section_code within the man dir */
static void build_catdir_name(const char *dirname, const char section_code,
char *catdir_name)
{
static char extension[] = "/cat0";
strcpy(catdir_name, dirname);
extension[4] = section_code;
strcat(catdir_name, extension);
} /* End of build_catdir_name() */
/* Build the name of man dir #section_code within the man dir */
static void build_mandir_name(const char *dirname, const char section_code,
char *mandir_name)
{
static char extension[] = "/man0";
strcpy(mandir_name, dirname);
extension[4] = section_code;
strcat(mandir_name, extension);
} /* End of build_mandir_name() */
/* Handle the `man -k' (apropos) and `man -f' (whatis) options */
static int handle_whatis(const char *name, const mode_type mode)
{
char *path_ptr, name_used[NAME_LENGTH];
int did_print;
/* `man -f' (whatis) needs to have the filename stripped first */
if (mode == MODE_MAN_F)
strip_filename(name, name_used);
else
strcpy(name_used, name);
did_print = 0;
/* walk along manual path and check whatis database */
path_ptr = man_path;
while (*path_ptr != 0x00)
{
char mandir[NAME_LENGTH], *manptr;
/* Copy one directory from the manual search path into mandir */
manptr = mandir;
while ((*path_ptr != 0x00) && (*path_ptr != ';') &&
(*path_ptr != ','))
*manptr++ = *path_ptr++;
*manptr = 0x00;
if (*path_ptr != 0x00)
path_ptr += 1;
if (check_directory_name(mandir) == 0)
{
fprintf(stderr, "man: directory %s on search path not found\n",
mandir);
continue; /* go on to next part of manual search path */
}
did_print |= print_apropos(mandir, name_used);
} /* End of while (walk along manual search path) */
return(did_print);
} /* End of handle_whatis() */
/* Strip the leading path-name and extension components from a filename. */
/* This is needed for the `man -f' (whatis) option, which is the same as */
/* `man -k' (apropos), except the whatis version needs a stripped name. */
static void strip_filename(const char *name, char *stripped_name)
{
char buffer[NAME_LENGTH], *ptr1, *ptr2;
strcpy(buffer, name);
ptr1 = ptr2 = buffer;
while (*ptr2 != 0x00)
{
if ((*ptr2 == '/') || (*ptr2 == '\\'))
ptr1 = ptr2 + 1;
ptr2 += 1;
}
ptr2 = ptr1;
while ((*ptr2 != 0x00) && (*ptr2 != '.'))
ptr2 += 1;
*ptr2 = 0x00;
strcpy(stripped_name, ptr1);
} /* End of strip_filename() */
/* Handle the `man -k' (apropos) search within a specific directory. */
/* This is similar to the `man -f' (whatis) search, which also makes */
/* use of this routine. The whatis argument must be stripped first. */
static int print_apropos(const char *mandir, const char *name)
{
FILE *whatis;
char line_read[NAME_LENGTH], line_buffer[NAME_LENGTH],
name_buffer[NAME_LENGTH];
int did_print;
strcpy(name_buffer, mandir);
strcat(name_buffer, "/whatis");
if ((whatis = fopen(name_buffer, "r")) == NULL)
{
fprintf(stderr, "%s: no such file\n", name_buffer);
return(0);
}
/* No case-insensitive strstr exists. Improvise with all-lowercase. */
strcpy(name_buffer, name);
strlwr(name_buffer);
did_print = 0;
while (!feof(whatis))
{
fgets(line_read, NAME_LENGTH - 1, whatis);
strcpy(line_buffer, line_read);
strlwr(line_buffer);
if (strstr(line_buffer, name_buffer) != NULL)
{
fputs(line_read, stdout);
did_print = 1;
}
}
fclose(whatis);
return(did_print);
} /* End of print_apropos() */