elks-enhanced
public
Read
Owner: themaster
Branch: master
Commits: 6893
Updated: 2026-04-19 00:15
Git CLI clone URL
git clone https://www.xt-emporium.com/git/elks-enhanced.git
Fullscreen desktop URL
Code
Commits
History
Branches
Bug Reports
Discussions
Compare
Settings
elks-enhanced
/
elkscmd
/
busyelks
/
cmd
/
man.c
File editor
/* (C) Robert de Bath 1997 under the terms of the GPL. * * This is a manual pager program, it will search for and format manual * pages which it then pipes to more. * * The program understands manual pages that have been compressed with * either 'compress' or 'gzip' and will decompress them on the fly. * * The environment is checked for these variables: * MANSECT=1:2:3:4:5:6:7:8:9 # Manual section search order. * MANPATH=/usrlocal/man:/usr/man # Directorys to search for man tree. * PAGER=more # pager progam to use. * PATH=... # Search for gzip/uncompress * * The program will display documents that are either in it's own "nroff -man" * like format or in "catman" format, it will not correctly display pages in * the BSD '-mdoc' format. * * Neither nroff nor any similar program is needed as this program has it's * own built in _man_ _page_ _formatter_. This is NOT an nroff clone and will * not (for instance) understand macros or tbl constructs. * * Unlike groff this is small, fast and picky! */ #define DONT_SPLATTER /* Lots of messages out */ #include <stdio.h> #ifdef __STDC__ #include <stdlib.h> #include <unistd.h> #endif #include <ctype.h> #include <string.h> FILE * ofd = stdout; FILE * ifd = stdin; int ifd_class = 0; /* Type of ifd, 0=stdin, 1=file, 2=pipe */ char whitespace[256]; char word[80]; /* Current word */ int no_nl=1; /* Next NL in input file is ignored */ int catmode = 1; /* Have we seen a '.XX' command ? */ int keep_nl = 0; /* How many nl to keep til eof */ int optional_keep=0; /* Is the next keep optional ? */ int pending_nl=0; /* Is there a pending newline on output? */ int no_fill = 0; /* Disable 'filling' of lines */ int right_adjust=1; /* Adjust right margin */ int standard_tab = 5; /* Amount left margin stepped by */ int left_indent = 0; /* Current step of left margin */ int old_para_indent=0; /* Indent to go back to after this paragraph */ int next_line_indent=-1; /* Indent after next line_break */ int page_no = 1; /* Page number */ int input_tab = 8; /* Tab width in input file */ int right_margin = 65; /* Don't print past this column */ int page_length = 66; /* Lines on page */ int current_line = 0; /* Line number = ? */ int gaps_on_line = 0; /* Gaps on line for adjustments */ int *line_ptr = 0; int line[256]; /* Buffer for building output line */ int cur_font = 0x100; /* Current font, 1 == Roman */ char line_header[256] = ""; /* Page header line */ char line_footer[256] = ""; /* Page footer line */ char little_header[256] = ""; /* Mini header for tty mode */ char man_file[256] = ""; int flg_w = 0; int verbose = 1; /**************************************************************************** * Main routine, hunt down the manpage. */ man_main(argc, argv) int argc; char ** argv; { int do_pclose_ofd = 0; int ar; char * mansect = 0; char * manname = 0; for(ar=1; ar<argc; ar++) if( argv[ar][0] == '-' ) { char * p; for(p=argv[ar]+1; *p; p++) switch(*p) { case 'w': flg_w=1; break; case 'v': verbose=1; break; case 'q': verbose=0; break; } } else if( isdigit(argv[ar][0]) ) mansect = argv[ar]; else if( manname == 0 ) manname = argv[ar]; else if( mansect == 0 ) { mansect = manname; manname = argv[ar]; } else { fprintf(stderr, "Ignoring argument %s\n", argv[ar]); break; } if( manname == 0 ) { fprintf(stderr, "Which manpage ?\n"); exit(1); } if( find_page(manname, mansect) < 0 ) { if( mansect > 0 ) fprintf(stderr, "No entry for %s in section %s of the manual.\n", manname, mansect); else fprintf(stderr, "No manual entry for %s\n", manname); exit(1); } if( flg_w ) exit(0); /* ifd is now the file - display it */ if( isatty(1) ) /* If writing to a tty do it to a pager */ { ofd = popen(getenv("PAGER"), "w"); if( ofd == 0 ) ofd = popen("more", "w"); if( ofd == 0 ) ofd = stdout; else { do_pclose_ofd=1; page_length = 0; right_margin=78; } } do_file(); /* Close files */ if( do_pclose_ofd ) pclose(ofd); close_page(); exit(0); } find_page(name, sect) char * name; char * sect; { static char defpath[] = "/usr/local/man:/usr/man"; static char defsect[] = "1:2:3:4:5:6:7:8:9"; static char defsuff[] = ":.gz:.Z"; static char manorcat[] = "man:cat"; char fbuf[256]; char *manpath; char *mansect = sect; char *mansuff; char *mc, *mp, *ms, *su, *nmc, *nmp, *nms, *nsu; int rv = -1; manpath = getenv("MANPATH"); if( !manpath ) manpath = defpath; if( !mansect ) mansect = getenv("MANSECT"); if( !mansect ) mansect = defsect; mansuff = defsuff; if( strchr(name, '/') ) { for(su=nsu=mansuff,step(&su,&nsu); su; step(&su, &nsu)) { strcpy(fbuf, name); strcat(fbuf, su); if( (rv=open_page(fbuf)) >= 0 ) break; } *man_file = 0; return rv; } /* SEARCH!! */ for(mc=nmc=manorcat,step(&mc,&nmc); mc; step(&mc, &nmc)) for(ms=nms=mansect,step(&ms,&nms); ms; step(&ms, &nms)) for(mp=nmp=manpath,step(&mp,&nmp); mp; step(&mp, &nmp)) for(su=nsu=mansuff,step(&su,&nsu); su; step(&su, &nsu)) { sprintf(fbuf, "%s/%s%s/%s.%s%s", mp, mc, ms, name, ms, su); /* Got it ? */ if( access(fbuf, 0) < 0 ) continue; if( flg_w ) { printf("%s\n", fbuf); rv = 0; continue; } /* Try it ! */ if( (rv=open_page(fbuf)) >= 0 ) { char * p; strcpy(man_file, fbuf); p = strrchr(man_file, '/'); if(p) *p = 0; p = strrchr(man_file, '/'); if(p) p[1] = 0; return 0; } } return rv; } step(pcurr, pnext) char **pcurr, **pnext; { char * curr = *pcurr; char * next = *pnext; if( curr == 0 ) return; if( curr == next ) { next = strchr(curr, ':'); if( next ) *next++ = 0; } else { curr=next; if( curr ) { curr[-1] = ':'; next = strchr(curr, ':'); if( next ) *next++ = 0; } } *pcurr = curr; *pnext = next; } open_page(name) char * name; { char *p, *command = 0; char buf[256]; if( access(name, 0) < 0 ) return -1; p = strrchr(name, '.'); if( p ) { if( strcmp(p, ".gz") == 0 ) command = "gzip -dc "; if( strcmp(p, ".Z") == 0 ) command = "uncompress -c "; } if( command ) { strcpy(buf, command); strcat(buf, name); ifd = popen(buf, "r"); if( ifd == 0 ) return -1; ifd_class = 2; return 0; } ifd = fopen(name, "r"); if( ifd == 0 ) return -1; ifd_class = 1; return 0; } close_page() { switch(ifd_class) { case 1: fclose(ifd); break; case 2: pclose(ifd); break; } ifd_class = 0; } /**************************************************************************** * Accepted nroff commands and executors. */ struct cmd_list_s { char cmd[3]; int class; int id; } cmd_list[] = { { "\\\"", 0, 0 }, { "nh", 0, 0 }, /* This program never inserts hyphens */ { "hy", 0, 0 }, /* This program never inserts hyphens */ { "PD", 0, 0 }, /* Inter-para distance is 1 line */ { "DT", 0, 0 }, /* Default tabs, they can't be non-default! */ { "IX", 0, 0 }, /* Indexing for some weird package */ { "Id", 0, 0 }, /* Line for RCS tokens */ { "BY", 0, 0 }, /* I wonder where this should go ? */ { "nf", 0, 1 }, /* Line break, Turn line fill off */ { "fi", 0, 2 }, /* Line break, Turn line fill on */ { "sp", 0, 3 }, /* Line break, line space (arg for Nr lines) */ { "br", 0, 4 }, /* Line break */ { "bp", 0, 5 }, /* Page break */ { "PP", 0, 6 }, { "LP", 0, 6 }, { "P", 0, 6 }, /* Paragraph */ { "RS", 0, 7 }, /* New Para + Indent start */ { "RE", 0, 8 }, /* New Para + Indent end */ { "HP", 0, 9 }, /* Begin hanging indent (TP without arg?) */ { "ad", 0, 10 }, /* Line up right margin */ { "na", 0, 11 }, /* Leave right margin unaligned */ { "ta", 0, 12 }, /* Changes _input_ tab spacing, right? */ { "TH", 1, 1 }, /* Title and headers */ { "SH", 1, 2 }, /* Section */ { "SS", 1, 3 }, /* Subsection */ { "IP", 1, 4 }, /* New para, indent except argument 1 */ { "TP", 1, 5 }, /* New para, indent except line 1 */ { "B", 2, 22 }, /* Various font fiddles */ { "BI", 2, 23 }, { "BR", 2, 21 }, { "I", 2, 33 }, { "IB", 2, 32 }, { "IR", 2, 31 }, { "RB", 2, 12 }, { "RI", 2, 13 }, { "SB", 2, 42 }, { "SM", 2, 44 }, { "C", 2, 22 }, /* PH-UX manual pages! */ { "CI", 2, 23 }, { "CR", 2, 21 }, { "IC", 2, 32 }, { "RC", 2, 12 }, { "so", 3, 0 }, { "\0\0", 0 } }; /**************************************************************************** * ifd is the manual page, ofd is the 'output' file or pipe, format it! */ do_file() { int nl; ungetc('\r', ifd); while((nl=fetch_word())>=0) { #ifdef SPLATTER fprintf(ofd, ">WS='%s',", whitespace); fprintf(ofd, "catmode=%d,", catmode); fprintf(ofd, "nl=%d,", nl); fprintf(ofd, "no_nl=%d,", no_nl); fprintf(ofd, "no_fill=%d,", no_fill); fprintf(ofd, "keep_nl=%d,", keep_nl); fprintf(ofd, "opt_keep=%d,", optional_keep); fprintf(ofd, "WD='%s',", word); fprintf(ofd, "\n"); #endif if( catmode ) { if( strcmp(word, "'\\\"") == 0 || strcmp(word, "'''") == 0 ) { /* This is a marker sometimes used for opening subprocesses like * tbl and equ; this program ignores it. */ do_skipeol(); } else if( *whitespace == '\r' ) fprintf(ofd, "%s%s", whitespace+1, word); else fprintf(ofd, "%s%s", whitespace, word); } else { if(keep_nl && nl && !no_nl) { if( optional_keep ) { optional_keep = 0; if( line_ptr == 0 || next_line_indent < 0 || left_indent + (line_ptr-line) + 1 > next_line_indent ) line_break(); else if( line_ptr != 0 && next_line_indent > 0 ) { while(left_indent + (line_ptr-line) + 1 <= next_line_indent) *line_ptr++ = cur_font + ' '; } } else line_break(); if(keep_nl>0) keep_nl--; } if( nl == 1 && ( word[0] == '.' || (word[0] == '\'' && strcmp(word, "'\\\"") == 0) || (word[0] == '\'' && strcmp(word, "'''") == 0) )) { no_nl=1; if( do_command() < 0 ) break; } else { if( nl == 1 && no_fill ) line_break(); if( *whitespace ) print_word(whitespace); print_word(word); no_nl=0; } } } page_break(); } fetch_word() { static int col = 0; char * p; int ch, nl; nl = 0; *(p = whitespace) = 0; if( !catmode && !no_fill ) p++; while((ch=fgetc(ifd)) != EOF && isspace(ch)) { if( nl && no_fill && ch != '.' && ch != '\n' ) break; if( nl && !catmode && ch == '\n' ) { *whitespace=0; strcpy(word, ".sp"); ungetc(ch, ifd); return 1; } nl = (ch == '\n' || ch == '\r'); if(nl) col=0; else col++; if( no_fill && nl && *whitespace ) { *word=0; ungetc(ch, ifd); return 0; } if(p<whitespace+sizeof(whitespace)-1 && (!nl || catmode)) *p++ = ch; if(ch == '\t' && !catmode) { p[-1] = ' '; while(col % input_tab) { if(p<whitespace+sizeof(whitespace)-1) *p++ = ' '; col++; } } if( !catmode && !no_fill && nl ) *(p = whitespace) = 0; } *p=0; if( catmode && ch == '.' && nl ) catmode = 0; *(p = word) = 0; if( ch == EOF || p > word+sizeof(word)/2 ) { if( p!=word ) { ungetc(ch, ifd); *p = 0; return nl; } return -1; } ungetc(ch, ifd); while((ch=fgetc(ifd)) != EOF && !isspace(ch)) { if(p<word+sizeof(word)-1) *p++ = ch; col++; if( ch == '\\' ) { if((ch=fgetc(ifd)) == EOF) break; /* if( ch == ' ' ) ch = ' ' + 0x80; /* XXX Is this line needed? */ if(p<word+sizeof(word)-1) *p++ = ch; col++; } } *p = 0; ungetc(ch, ifd); return (nl != 0); } do_command() { char * cmd; int ch, i; char lbuf[10]; cmd=word+1; /* Comments don't need the space */ if( strncmp(cmd, "\\\"", 2) == 0 ) cmd="\\\""; for(i=0; cmd_list[i].cmd[0]; i++) { if( strcmp(cmd_list[i].cmd, cmd) == 0 ) break; } if( cmd_list[i].cmd[0] == 0 ) { if( verbose ) { strncpy(lbuf, cmd, 3); lbuf[3] = 0; line_break(); i=left_indent; left_indent=0; strcpy(word, "**** Unknown formatter command: ."); strcat(word, lbuf); print_word(word); line_break(); left_indent=i; } i=0; /* Treat as comment */ } switch( cmd_list[i].class ) { case 1: /* Parametered commands */ return do_argvcmd(cmd_list[i].id); case 2: /* Font changers */ return do_fontwords(cmd_list[i].id/10, cmd_list[i].id%10, 0); case 3: /* .so */ fetch_word(); strcat(man_file, word); close_page(); if( find_page(man_file, (char*)0) < 0 ) { fprintf(stderr, "Cannot open .so file %s\n", word); return -1; } ungetc('\r', ifd); break; default: do_skipeol(); if( cmd_list[i].id ) return do_noargs(cmd_list[i].id); } return 0; } do_skipeol() { int ch; char * p = word; while((ch=fgetc(ifd)) != EOF && ch != '\n') if( p<word+sizeof(word)-1 ) *p++ =ch;; *p=0; ungetc(ch, ifd); } do_fontwords(this_font, other_font, early_exit) int this_font, other_font, early_exit; { static char ftab[] = " RBIS"; char *p=word; int i, ch; int in_quote = 0; no_nl=0; /* Line is effectivly been reprocessed so NL is visable */ for(;;) { if( p == word ) { strcpy(p, "\\f"); p[2]=ftab[this_font]; p+=3; } if( (ch=fgetc(ifd)) == EOF || ch == '\n' ) break; if( ch == '"' ) { in_quote = !in_quote; continue; } if( in_quote || !isspace(ch) ) { if( isspace(ch) && p > word+3 ) { strcpy(p, "\\fR"); p+=3; *p=0; print_word(word); p=word; if( no_fill ) print_word(" "); continue; } if( p<word+sizeof(word)-4 ) *p++ = ch; if( ch == '\\' ) { if( (ch=fgetc(ifd)) == EOF || ch == '\n' ) break; if( p<word+sizeof(word)-4 ) *p++ = ch; } continue; } if( p != word+3 ) { if( early_exit ) break; if( this_font == other_font ) { strcpy(p, "\\fR"); p+=3; *p=0; print_word(word); p=word; } i=this_font; this_font=other_font; other_font=i; if( p<word+sizeof(word)-4 ) { strcpy(p, "\\f"); p[2]=ftab[this_font]; p+=3; } } } ungetc(ch, ifd); if( p > word+3 ) { strcpy(p, "\\fR"); p+=3; *p=0; print_word(word); } return 0; } do_noargs(cmd_id) int cmd_id; { if( cmd_id < 10 ) line_break(); switch(cmd_id) { case 1: no_fill = 1; break; case 2: no_fill = 0; break; case 3: pending_nl = 1; break; case 4: break; case 5: page_break(); break; case 6: left_indent = old_para_indent; pending_nl = 1; break; case 7: pending_nl = 1; left_indent += standard_tab; old_para_indent += standard_tab; break; case 8: pending_nl = 1; left_indent -= standard_tab; old_para_indent -= standard_tab; break; case 10: right_adjust=1; break; case 11: right_adjust=0; break; case 12: input_tab=atoi(word); if(input_tab<=0) input_tab=8; break; } return 0; } do_argvcmd(cmd_id) int cmd_id; { int ch; line_break(); while( (ch=fgetc(ifd)) != EOF && (ch==' ' || ch=='\t') ) ; ungetc(ch, ifd); switch(cmd_id + 10*(ch=='\n')) { case 1: /* Title and headers */ page_break(); left_indent = old_para_indent = standard_tab; build_headers(); break; case 2: /* Section */ left_indent = 0; next_line_indent = old_para_indent = standard_tab; no_nl = 0; keep_nl = 1; pending_nl=1; do_fontwords(1,1,0); return 0; case 3: /* Subsection */ left_indent = standard_tab/2; next_line_indent = old_para_indent = standard_tab; no_nl = 0; keep_nl = 1; pending_nl=1; do_fontwords(1,1,0); break; case 15: case 5: /* New para, indent except line 1 */ do_skipeol(); next_line_indent = old_para_indent + standard_tab; left_indent = old_para_indent; pending_nl=1; keep_nl = 1; optional_keep = 1; break; case 4: /* New para, indent except argument 1 */ next_line_indent = old_para_indent + standard_tab; left_indent = old_para_indent; pending_nl=1; keep_nl = 1; optional_keep = 1; do_fontwords(1,1,1); do_skipeol(); break; case 14: pending_nl = 1; left_indent = old_para_indent + standard_tab; break; } return 0; } build_headers() { char buffer[5][80]; int strno=0, stroff=0; int last_ch = 0, ch, in_quote=0; for(ch=0; ch<5; ch++) buffer[ch][0] = 0; for(;;) { if( (ch=fgetc(ifd)) == EOF || ch == '\n' ) break; if( ch == '"' ) { if( last_ch == '\\' ) { stroff--; break; } in_quote = !in_quote; continue; } last_ch = ch; if( in_quote || !isspace(ch) ) { /* Nb, this does nothing about backslashes, perhaps it should */ if( stroff< sizeof(buffer[strno])-1 ) buffer[strno][stroff++] = ch; continue; } buffer[strno][stroff] = 0; if( buffer[strno][0] ) { strno++; stroff=0; if(strno == 5) break; } } if( strno < 5 ) buffer[strno][stroff] = 0; ungetc(ch, ifd); /* Ok we should have upto 5 arguments build the header and footer */ memset(little_header, ' ', right_margin); memset(line_header, ' ', right_margin); strcpy(line_header, buffer[0]); strcat(line_header, "("); strcat(line_header, buffer[1]); strcat(line_header, ")"); ch = strlen(line_header); strcpy(line_header+right_margin-ch, line_header); line_header[ch] = ' '; ch = strlen(buffer[4]); if( ch > right_margin ) ch = right_margin-12; memcpy(line_header+right_margin/2-ch/2, buffer[4], ch); memcpy(little_header, line_header, right_margin/2+ch/2+1); strcpy(little_header+right_margin-strlen(buffer[2]), buffer[2]); memset(line_footer, ' ', right_margin-6); line_footer[right_margin-6] = 0; memcpy(line_footer, buffer[3], strlen(buffer[3])); ch = strlen(buffer[2]); if( ch > right_margin ) ch = right_margin-12; memcpy(line_footer+right_margin/2-ch/2, buffer[2], ch); do_skipeol(); } print_word(pword) char * pword; { /* Eat \& \a .. \z and \A .. \Z * \fX Switch to font X (R B I S etc) * \(XX Special character XX * \X Print as X */ char *s; int *d, ch=0; int length=0; int wword[256]; int sp_font = cur_font; /* Eat and translate characters. */ for(s=pword,d=wword; *s; s++) { ch=0; if( *s == '\n' ) continue; if( *s != '\\' ) { *d++ = (ch= *s) + cur_font; length++; } else { if(s[1] == 0) break; s++; if(*s == 'f') { if( s[1] ) { static char fnt[] = " RBI"; char * p = strchr(fnt, *++s); if( p == 0 ) cur_font = 0x100; else cur_font = 0x100*(p-fnt); } continue; } else if(*s == 's') { /* Font size adjusts - strip */ while(s[1] && strchr("+-0123456789", s[1])) s++; continue; } else if(isalpha(*s) || strchr("!&^[]|~", *s) ) continue; else if( *s == '(' || *s == '*' ) { /* XXX Humm character xlate */ if( *s == '*' ) if( s[1] ) ++s; if( s[1] ) ++s; if( s[1] ) ++s; *d++ = '*' + cur_font; length++; continue; } *d++ = *s + cur_font; length++; } } *d=0; #ifdef SPLATTER { int *x; fprintf(ofd, ">WORD:"); for(x=wword; *x; x++) fputc(*x, ofd); fprintf(ofd, ":\n"); } #endif if( *wword == 0 ) return; if( line_ptr ) if( line_ptr + ((line_ptr[-1]&0xFF) == '.') - line + length >= right_margin - left_indent ) { right_adjust = -right_adjust; line_break(); } if( line_ptr == 0 ) line_ptr = line; else if( !no_fill && (line_ptr[-1]&0xFF) > ' ' ) { if( (line_ptr[-1]&0xFF) == '.' ) *line_ptr++ = cur_font + ' '; *line_ptr++ = sp_font; gaps_on_line++; } memcpy(line_ptr, wword, length*sizeof(int)); line_ptr += length; } line_break() { int * d, ch; int spg=1, rspg=1, spgs=0, gap=0; if( line_ptr == 0 ) return ; if( current_line == 0 ) print_header(); if( page_length > 12 && current_line+pending_nl > page_length-6 ) { print_footer(); print_header(); } if( current_line ) current_line += 1+pending_nl; for(;pending_nl>0; pending_nl--) fprintf(ofd, "\n"); if( right_adjust<0 ) { int over = right_margin-left_indent-(line_ptr-line); #ifdef SPLATTER fprintf(ofd, ">Gaps=%d, Over=%d, ", gaps_on_line, over); #endif if( gaps_on_line && over ) { spg=rspg= 1+over/gaps_on_line; over = over%gaps_on_line; if( over ) { if( current_line % 2 ) { spgs = over; spg++; } else { spgs = gaps_on_line-over; rspg++; } } } #ifdef SPLATTER fprintf(ofd, " (%d,%d) sw=%d\n", spg, rspg, spgs); #endif right_adjust=1; } *line_ptr = 0; if( *line ) for(ch=left_indent; ch>0; ch--) fputc(' ', ofd); for(d=line; *d; d++) { ch = *d; if( (ch & 0xFF) == 0 ) { int i; if( gap++ < spgs ) i=spg; else i=rspg; for(;i>0;i--) fputc(' ', ofd); } else switch(ch >> 8) { case 2: fputc(ch&0xFF, ofd); fputc('\b', ofd); fputc(ch&0xFF, ofd); break; case 3: fputc('_', ofd); fputc('\b', ofd); fputc(ch&0xFF, ofd); break; default: fputc(ch&0xFF, ofd); break; } } fputc('\n', ofd); line_ptr = 0; if( next_line_indent > 0 ) left_indent = next_line_indent; next_line_indent = -1; gaps_on_line=0; } page_break() { line_break(); if( current_line ) print_footer(); } print_header() { pending_nl = 0; if( *line_header && page_length ) { current_line = 7; fprintf(ofd, "\n\n\n%s\n\n\n", line_header); } else if( *little_header && !page_length ) { current_line = 1; fprintf(ofd, "%s\n\n", little_header); } } print_footer() { if( !page_length ) return; while(current_line <= page_length-3) { fputc('\n', ofd); current_line++; } fprintf(ofd, "%s%6d\n\n\n", line_footer, page_no++); current_line = 0; }
Commit message
This repository is read-only for this account.
Repository snapshot
Current branch
master
Visibility
public
Your access
Read
Remote
Configured
File activity
View file history