text: /* kilt.c - upskirt for man */
  
  /*
   * Copyright (c) 2009, Baptiste Daroussin and Natacha Porté
   *
   * Permission to use, copy, modify, and distribute this software for any
   * purpose with or without fee is hereby granted, provided that the above
   * copyright notice and this permission notice appear in all copies.
   *
   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   */
  
  #include "markdown.h"
  
  #include <stdio.h>
  #include <errno.h>
  #include <string.h>
  
  #define READ_UNIT 1024
  #define OUTPUT_UNIT 64
  
  
  /****************************
   * MARKDOWN TO MAN RENDERER *
   ****************************/
  
  void
  man_text_escape(struct buf *ob, char *src, size_t size) {
  	size_t  i = 0, org;
  	while (i < size) {
  		/* copying directly unescaped characters */
  		org = i;
  		while (i < size && src[i] != '-')
  			i += 1;
  		if (i > org) bufput(ob, src + org, i - org);
  
  		/* escaping */
  		if (i >= size) break;
  		else if (src[i] == '-') BUFPUTSL(ob, "\\-");
  		i += 1; } }
  
  static void
  man_blockcode(struct buf *ob, struct buf *text, void *opaque) {
  	if (ob->size) bufputc(ob, '\n');
  	BUFPUTSL(ob, ".nf\n");
  	if (text) man_text_escape(ob, text->data, text->size);
  	BUFPUTSL(ob, ".fi\n"); }
  
  static int
  man_codespan(struct buf *ob, struct buf *text, void *opaque) {
  	if (ob->size) bufputc(ob, '\n');
  	BUFPUTSL(ob, ".nf\n");
  	if (text) man_text_escape(ob, text->data, text->size);
  	BUFPUTSL(ob, ".fi\n"); 
  	return 1; }
  
  static void
  man_header(struct buf *ob, struct buf *text, int level, void *opaque) {
  	if (ob->size) bufputc(ob, '\n');
  	switch(level) {
  		case 1:
  			bufprintf(ob,".TH "); 
  			break;
  		case 2:
  			bufprintf(ob, ".SH ");
  			break;
  		case 3:
  			bufprintf(ob, ".SS ");
  			break;
  	}
  	if (text) bufput(ob, text->data, text->size);
  }
  
  static int
  man_double_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) {
  	if (!text || !text->size) return 0;
  	BUFPUTSL(ob, "\\fB");
  	bufput(ob, text->data, text->size);
  	BUFPUTSL(ob, "\\fP");
  	return 1; }
  
  static int
  man_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) {
  	if (!text || !text->size) return 0;
  	BUFPUTSL(ob, "\\fI");
  	if (text) bufput(ob, text->data, text->size);
  	BUFPUTSL(ob, "\\fP");
  	return 1; }
  
  static int
  man_linebreak(struct buf *ob, void *opaque) {
  	BUFPUTSL(ob, "\n.LP\n");
  	return 1; }
  
  static void
  man_paragraph(struct buf *ob, struct buf *text, void *opaque) {
  	if (ob->size) bufputc(ob, '\n');
  	BUFPUTSL(ob, ".TP\n");
  	if (text) bufput(ob, text->data, text->size);
  	BUFPUTSL(ob, "\n"); }
  
  static void
  man_list(struct buf *ob, struct buf *text, int flags, void *opaque) {
  	if (ob->size) bufputc(ob, '\n');
  	if (flags & MKD_LIST_ORDERED)
  		BUFPUTSL(ob,"\n.nr step 0 1\n");
  	else
  		BUFPUTSL(ob,".\n");
  	if (text) bufput(ob, text->data, text->size);
  	BUFPUTSL(ob, "\n"); }
  
  static void
  man_listitem(struct buf *ob, struct buf *text, int flags, void *opaque) {
  	if (flags & MKD_LIST_ORDERED)
  		BUFPUTSL(ob, ".IP \\n+[step]\n");
  	else
  		BUFPUTSL(ob, ".IP \\[bu] 2 \n");
  
  	if (text) {
  		while (text->size && text->data[text->size - 1] == '\n')
  			text->size -= 1;
  		bufput(ob, text->data, text->size); }
  	BUFPUTSL(ob, "\n"); }
  
  static void
  man_normal_text(struct buf *ob, struct buf *text, void *opaque) {
  	if (text) man_text_escape(ob, text->data, text->size); }
  
  
  /* renderer structure */
  struct mkd_renderer to_man = {
  	/* block-level callbacks */
  	man_blockcode,
  	NULL,
  	NULL,
  	man_header,
  	NULL,
  	man_list,
  	man_listitem,
  	man_paragraph,
  
  	/* span-level callbacks */
  	NULL,
  	man_codespan,
  	man_double_emphasis,
  	man_emphasis,
  	NULL,
  	man_linebreak,
  	NULL,
  	NULL,
  	NULL,
  
  	/* low-level callbacks */
  	NULL,
  	man_normal_text,
  
  	/* renderer data */
  	"*_",
  	NULL };
  
  
  
  /*****************
   * MAIN FUNCTION *
   *****************/
  
  /* main • main function, interfacing STDIO with the parser */
  int
  main(int argc, char **argv) {
  	struct buf *ib, *ob;
  	size_t ret;
  	FILE *in = stdin;
  
  	/* opening the file if given from the command line */
  	if (argc > 1) {
  		in = fopen(argv[1], "r");
  		if (!in) {
  			fprintf(stderr,"Unable to open input file \"%s\": %s\n",
  				argv[1], strerror(errno));
  			return 1; } }
  
  	/* reading everything */
  	ib = bufnew(READ_UNIT);
  	bufgrow(ib, READ_UNIT);
  	while ((ret = fread(ib->data + ib->size, 1,
  			ib->asize - ib->size, in)) > 0) {
  		ib->size += ret;
  		bufgrow(ib, ib->size + READ_UNIT); }
  	if (in != stdin) fclose(in);
  
  	/* performing markdown to man */
  	ob = bufnew(OUTPUT_UNIT);
  	markdown(ob, ib, &to_man);
  
  	/* writing the result to stdout */
  	fwrite(ob->data, 1, ob->size, stdout);
  
  	/* cleanup */
  	bufrelease(ib);
  	bufrelease(ob);
  	return 0; }
  
  /* vim: set filetype=c: */
  