Browse files

initial checkin

  • Loading branch information...
0 parents commit 11008ab0c120d2f79a932f0e3187873ee0374215 @armandofox committed May 7, 2012
Showing with 1,814 additions and 0 deletions.
  1. +2 −0 .gitignore
  2. +164 −0 Makefile
  3. +104 −0 README.md
  4. +9 −0 book_mobi.tex
  5. +9 −0 book_pdf.tex
  6. +95 −0 common.tex
  7. +108 −0 html.4ht
  8. +5 −0 html.cfg
  9. +29 −0 listingdefaults.tex
  10. +548 −0 macros.tex
  11. +121 −0 mobi.4ht
  12. +6 −0 mobi.cfg
  13. 0 pasties.yml
  14. +1 −0 script/Gemfile
  15. +9 −0 script/Gemfile.lock
  16. +41 −0 script/changebars
  17. +148 −0 script/html_postprocess.rb
  18. +98 −0 script/mobi_postprocess.rb
  19. +78 −0 script/outl.pl
  20. +108 −0 script/pastebin.rb
  21. +123 −0 script/update_pastebin
  22. +8 −0 script/update_tex_with_pasties
2 .gitignore
@@ -0,0 +1,2 @@
+*~
+.#*
164 Makefile
@@ -0,0 +1,164 @@
+TEX = latex
+PDFLATEX = pdflatex
+TEX4HT = tex4ht
+T4HT = t4ht
+BIBTEX = bibtex
+PERL = perl
+RUBY = ruby
+ERB = erb
+KINDLEGEN = kindlegen # MacOS: /Applications/kindlegen
+CONVERT = convert
+AVCONVERT = avconvert
+
+OTHERFILES = # $(wildcard ch_*/figs/*) $(wildcard ch_*/tables/*) $(wildcard ch_*/code/*) $(wildcard ch_*/mov/*)
+TEXFILES = $(shell find . -name \*.tex)
+BIBFILES = book.bib
+HTML_CONFIGS = book_html.cfg book_html.4ht
+MOBI_CONFIGS = book_mobi.cfg book_mobi.4ht
+PDF_CONFIGS = macros.tex
+SRCS = $(TEXFILES) $(BIBFILES) $(OTHERFILES)
+CODEFILES = $(shell find ch_*/code/* -regex '.*/[^\#].*\..*[^~]$$')
+
+TAGSRCS = $(SRCS) $(HTML_CONFIGS) $(MOBI_CONFIGS) $(PDF_CONFIGS) $(CODEFILES)
+
+PDF_FIGURES = $(wildcard ch_*/figs/*.pdf)
+PDF_GIF_FIGURES = $(patsubst %.pdf,%.pdf.gif,$(PDF_FIGURES))
+PDF_ICONS = $(wildcard icons/*.pdf)
+JPG_FIGURES = $(shell find $(wildcard ch_*/figs) -name '*jpg' -not -name '*_SB*')
+SCALED_SIDEBAR_FIGURES = $(patsubst %.jpg,%_SB.jpg,$(JPG_FIGURES))
+
+all: help
+
+.PHONY: outline html mobi css pdf clean cleanup help newchapter only
+
+help:
+ @echo "'make html' creates www/ directory containing HTML5 version"
+ @echo "'make mobi' creates book.mobi (for Kindle)"
+ @echo "'make ibook' creates .rtfd files for importing into iWork Pages and then into iBooks Author"
+ @echo "'make pdf' creates book.pdf"
+ @echo "'make only CHAPTER=big_ideas' creates a draft PDF of just chapter 'big_ideas.tex'"
+ @echo " (NOTE: you must do 'make clean && make pdf' first)"
+ @echo "'make update_pastebin' uploads all new code/ examples to Pastebin and updates LaTeX files in place (SAVE ALL YOUR FILES AND RUN git commit BEFORE DOING THIS)"
+ @echo "'make outline' prints out TOC without chapter numbers"
+
+outline: $(SRCS)
+ @perl -ne 'print "$$1.tex " if /[^%][ *]\\include{(ch_.*\/.*)}/' common.tex | xargs $(PERL) script/outl.pl -n
+
+only: $(SRCS)
+ @echo '\\def\\onechap{TRUE} \\includeonly{ch_$(CHAPTER)/$(CHAPTER)}' > only.tex
+ make pdf
+ rm -f only.tex
+
+onlyquick: $(SRCS)
+ @echo '\\def\\onechap{TRUE} \\includeonly{ch_$(CHAPTER)/$(CHAPTER)}' > only.tex
+ make 'QUICK=#' pdf
+ rm -f only.tex
+
+mobi: book_mobi.html css/mobi.css book_mobi.ncx book_mobi.opf $(SCALED_SIDEBAR_FIGURES) $(PDF_GIF_FIGURES)
+ $(RUBY) script/mobi_postprocess.rb $<.bak > $<
+ cat css/mobi.css >> mobi.css
+ -$(KINDLEGEN) book_mobi.opf
+ mv book_mobi.mobi book.mobi
+
+%.ncx: %.ncx.erb
+ $(ERB) $< > $@
+
+book_mobi.html: book_mobi.tex book_mobi.dvi $(MOBI_CONFIGS)
+ $(TEX4HT) $<
+ $(T4HT) $<
+ cp $@ $@.bak
+
+book_html.html: book_html.tex book_html.dvi $(HTML_CONFIGS)
+ $(TEX4HT) $<
+ $(T4HT) $<
+ cp $@ $@.bak
+
+.PHONY: html
+html: book_html.html html_preamble.html footer.tex
+ rm -f book_html.css
+ $(RUBY) script/html_postprocess.rb book_html{ch,ap,li}*.html
+
+html_postprocess: book_html.html book_htmlli*.html book_htmlch*.html book_htmlap*.html
+ $(RUBY) script/html_postprocess.rb $^
+
+find_undefined: book.pdf
+ $(PDFLATEX) book_pdf | grep -i defined | grep -v 'Font shape' 2>&1
+
+pdf: book_pdf.tex $(SRCS) $(PDF_CONFIGS)
+ $(PDFLATEX) $(basename $<)
+ $(QUICK) -for i in bu?*.aux ; do bibtex `basename $$i .aux` ; done
+ $(QUICK) $(PDFLATEX) $(basename $<)
+ $(QUICK) $(PDFLATEX) $(basename $<)
+ mv $(basename $<).pdf book.pdf
+
+%.dvi: $(SRCS) $(HTML_CONFIGS) $(MOBI_CONFIGS)
+ $(TEX) $(basename $@)
+ -for i in bu?*.aux ; do bibtex `basename $$i .aux` ; done
+ $(TEX) $(basename $@)
+ $(TEX) $(basename $@)
+ rm -f only.tex
+
+# image conversion
+# convert fullsize PDF images to GIF (for Kindle)
+%.pdf.gif: %.pdf
+ $(CONVERT) -flatten -background '#ffffff' -transparent-color '#ffffff' -density 300x300 -resize 1024x $^ $@ 2>/dev/null
+
+# resize fullsize images to sidebar images (for Kindle)
+%_SB.jpg: %.jpg
+ $(CONVERT) -flatten -background '#ffffff' -transparent-color '#ffffff' -resize x150 $^ $@ 2>/dev/null
+
+.PHONY: update_pastebin clear_pastebin diff_pastebin
+clear_pastebin:
+ -$(RUBY) script/update_pastebin delete_all
+ -$(RUBY) script/update_pastebin truncate_pastie_file
+
+update_pastebin:
+ @echo Uploading files to pastebin, this may take a couple of minutes...
+ -$(RUBY) script/update_pastebin update $(CODEFILES)
+ @echo Updating LaTeX files with Pastebin URIs, be sure to reload them in your editor....
+ $(RUBY) -p -i.bak script/update_tex_with_pasties $(SRCS) && find . -name '*.tex.bak' -exec rm '{}' ';'
+
+diff_pastebin:
+ -$(RUBY) script/update_pastebin diff $(CODEFILES)
+
+update_vimeo:
+ @echo Updating LaTeX files with Vimeo URIs
+ $(RUBY) -p -i.bak script/update_tex_with_screencasts $(SRCS) && find . -name '*.tex.bak' -exec rm '{}' ';'
+
+TAGS: $(TAGSRCS)
+ etags $^
+
+.PHONY: check_blank_lines
+check_blank_lines:
+ @echo The following code files may have spurious blank lines at end:
+ -@pcregrep -l -M '(^\s*$$){2,}\Z' $(CODEFILES)
+
+# remove changebars
+.PHONY: remove_changebars
+remove_changebars:
+ $(PERL) -p -i.bak -e 's/\\\\cb(start|end)\{\}//g' $(TEXFILES)
+
+.PHONY: fulltags
+fulltags:
+ find . -name '.#*' -prune -o -name '*.tex' -o -name '*.bib' -o -name '*.rb' -type f | xargs etags
+
+veryclean: clean clean_figs
+
+clean_docs:
+ rm -f book_pdf.pdf $(ROOT).pdf
+ rm -f book_html.html $(ROOT).html
+ rm -f book_mobi.html $(ROOT).mobi
+
+clean_figs:
+ rm -f ch_*/figs/*_SB.* ch_*/figs/*.pdf.gif ch_*/figs/*.jpg.gif icons/*_SB.*
+
+clean: clean_docs
+ rm -f \
+only.tex \
+*.bbl *.dvi *.idx *.ilg *.ind *.out *.ist *.bak \
+*.cb *.cb2 *.glo *.glg *.gls *.mtc* bu*.blg \
+book_*.{ps,ent,blg,toc,4ct,4tc,xref,idv,lg,tmp,css,lof,lot,lg,xref,ncx,maf} \
+book_html*.gif zz$(ROOT)* book_mobi*x.png \
+book_html{ch,ap,li}*.html
+ find . -name '*.bak' -or -name '*~' -or -name '*.log' -or -name '*.aux' | xargs rm -f
+
104 README.md
@@ -0,0 +1,104 @@
+About
+=====
+
+A toolchain using free/open source tools to generate both PDF and ebooks
+(.mobi and soon .epub) from a single set of LaTeX sources.
+
+We used this toolchain to create the hardcopy and Kindle editions of our
+textbook [Engineering Long-Lasting Software](http://saasbook.info),
+along with additional design elements and macros to give our book its
+unique appearance.
+
+Warning
+-------
+
+This brief guide assumes you are *very* comfortable with LaTeX and the
+Unix development environment (using Makefiles to manage complex builds,
+etc.) and will teach you nothing about those things. If you haven't
+used LaTeX before, writing in LaTeX is more like programming than like
+authoring. If that scares you, this bundle's not for you. If you're
+not comfortable running Makefile-based builds, good luck.
+
+There is no support. Seriously. None. Pull requests are welcome since
+many improvements are needed, but sadly I just don't have the time to
+teach people to use this. It has lots of moving parts and things that
+can break. When better ebook authoring tools come around I'll be happy
+to switch!
+
+Basic Idea
+----------
+
+The two major ebook file formats today are Mobipocket (.prc) and ePub
+(.epub). Amazon bought Mobipocket, added their own DRM to Mobipocket
+format, and rebranded it as "Kindle format" (.azw files); ePub is an
+open standard that allows for but doesn't require DRM. Amazon has since
+extended its format to add more support for HTML positioning, wrapping,
+etc; this is the new KF8 (Kindle Format 8), which is essentially a
+proprietary extension to Mobipocket that takes advantage of the
+renderers on the Kindle Fire and Kindle reading apps. Currently, this
+toolchain doesn't have support for the new KF8 features.
+
+Both formats are based on HTML markup for text and embedded assets
+(images, etc). tex4ht was designed to output HTML from LaTeX documents.
+However, because of differences between "plain old" HTML 5 and the
+individual formats, limitations/quirks of tex4ht, and limitations/quirks
+of the rendering software on ebook readers, substantial surgery is
+needed on the output of tex4ht, and some care is needed in your LaTeX
+authoring. The 'mobi_postprocess.rb' and 'html_postprocess.rb' scripts
+perform this surgery using the powerful Nokogiri XML library as a base.
+
+The toolchain works exclusively with the LaTeX "book" document class.
+There are some extensions and some limitations on what you can do.
+In general, every logical type of document element---chapter header,
+figure, sidebar, code file listing, etc.---must be wrapped in its own
+LaTeX macro, because the output instructions for ebook generation may
+differ substantially from the instructions for PDF output.
+
+If you stick to the book elements described below, you should be able to
+use everything as-is. If you want to customize/add behaviors, see
+Customizing at the end of this README.
+
+Requirements
+============
+
+A Unix-like system (Mac OS X is fine, but you need to have Unix-fu) and
+full installs of the following:
+
+- Ruby 1.9.2 or later, including the Rubygems library manager
+- The Nokogiri gem (see setup instructions below)
+- A full install of LaTeX2e with lots o'packages. The MacTeX installer
+is a great choice for Mac OS X users.
+- pdftex - included with MacTeX
+- A full install of Ghostscript and ImageMagick, for converting images
+for ebook output
+- A full install of tex4ht (may be included with MacTeX, I forget)
+- The [kindlegen script](http://www.amazon.com/kindleformat/KindleGen)
+for building the .mobi file
+
+
+Setup
+=====
+
+1. With Ruby 1.9.2+ installed, cd to the script/ subdirectory and run
+'bundle install' to make sure the Nokogiri gem is available.
+2. In Makefile, change KINDLEGEN to the path to your kindlegen script.
+
+Adding LaTeX files and assets
+=============================
+
+RECOMMENDED: if you add your book content according to the following
+structure, you won't need any Makefile changes. If you follow your own
+structure, you'll need to make substantial changes to the Makefile and
+to common.tex.
+
+Edit the file common.tex, which will serve as your toplevel file, by
+searching for ::EDITME::
+
+Add your book chapters, each in its own subdirectory, organized as
+follows for a chapter called mychap:
+
+ch_mychap/
+ch_mychap/mychap.tex - toplevel file for that chapter
+ch_mychap/figs/ - figures (.pdf files ONLY--see below)
+ch_mychap/tables/ - tables (usually just .tex files)
+
9 book_mobi.tex
@@ -0,0 +1,9 @@
+\NeedsTeXFormat{LaTeX2e}[1996/06/01]
+\documentclass{book}
+\usepackage[saasbook_html,xhtml,3.2]{tex4ht}
+\newcommand{\fullwidth}{6in} % excluding whitespace margins
+\newif\ifhtmloutput\htmloutputtrue
+\newif\ifmobioutput\mobioutputtrue
+\input{common}
+
+
9 book_pdf.tex
@@ -0,0 +1,9 @@
+\NeedsTeXFormat{LaTeX2e}[1996/06/01]
+\documentclass[10pt,twoside,textbookpaper,titlepage]{textbook} % subclass of Book
+\newif\ifhtmloutput\htmloutputfalse
+\newif\ifmobioutput\mobioutputfalse
+% see figure C.3 in LaTeX manual for other dimensions that MUST be
+% adjusted if you change the page size
+\input{common}
+
+
95 common.tex
@@ -0,0 +1,95 @@
+% book version - referenced by some other macros, but optional for you
+\newcommand{\bookversion}{1.0}
+
+% load macros, some of which depend on the above switches
+\input{macros}
+
+% Enable compilation of just a single chapter at a time.
+% \onechap will be defined as TRUE in only.tex if it
+% exists, and used in an \includeonly below.
+\IfFileExists{only.tex}%
+ {\input{only}}%
+ {\def\onechap{FALSE}}
+
+% partial compilation?
+\ifthenelse{\equal{\onechap}{FALSE}}%
+ {\newif\ifwholebook\wholebooktrue}% full compilation
+ {\newif\ifwholebook\wholebookfalse}% partial compilation
+
+% ::EDITME::
+% comment out the following if you DON'T want ``Draft:'' watermark
+% when you generate individual chapters
+\ifwholebook
+\else
+ \usepackage{draftwatermark}
+ \SetWatermarkText{DRAFT: Do Not Distribute}
+ \SetWatermarkScale{0.4}
+\fi
+
+\begin{document}
+ \raggedbottom
+ % make true PDF page size match logical page size (for pdflatex)
+ % to change physical paper size, edit \paperwidth and \paperheight
+ % in book.cls or whatever class file you're using
+ \ifhtmloutput
+ \else
+ \special{papersize=\paperwidth,\paperheight}
+ \setlength{\pdfpagewidth}{\paperwidth}
+ \setlength{\pdfpageheight}{\paperheight}
+ \fi
+
+ % ::EDITME::
+ \title{Your Title Here}
+ \author{Your Name Here}
+ \pagestyle{headings}
+
+ \ifhtmloutput
+ % define XHTML outputs for different book elements
+ \ifmobioutput \input{mobi.4ht} \else \input{html.4ht} \fi %
+ \else
+ %
+ \fi
+ \ifwholebook%
+ \maketitle%
+ % title page with CIP data
+ \input{titlepage}% title page, copyright info
+ \ifhtmloutput
+ \ifmobioutput
+ \tableofcontents
+ \else
+ \CutAt{chapter,likesubsubsection}
+ \fi
+ \else
+ \dominitoc % remove to suppress per-chapter mini-TOCs
+ \thispagestyle{empty}\tableofcontents
+ \fi
+ \fi
+
+ % bibliography per-chapter (requires bibunits package)
+ \defaultbibliographystyle{abbrvnat}
+ \bibpunct{}{}{;}{a}{}{,}
+ \defaultbibliography{saasbook}
+ \bibliographyunit[\chapter]
+
+ % ::EDITME::
+ % Add your chapters here:
+ % \include{ch_foreword/foreword}
+ % \include{ch_intro/intro}
+ % etc.
+ % all chapters after the \appendix directive are treated as appendices:
+ \appendix
+ % \include{ch_app1/app1}
+
+ % back matter: bibliography, indices, etc.
+
+ % ::EDITME::
+ % uncomment only if you REMOVED the per-chapter bibliography stuff above:
+ %\bibliographystyle{authordate1}
+ %\bibliography{saasbook}
+ \ifhtmloutput
+ % suppress index for HTML, Kindle versions
+ \else
+ \ifwholebook \printindex \fi
+ \fi
+
+\end{document}
108 html.4ht
@@ -0,0 +1,108 @@
+% -*- mode: latex -*-
+\Configure{DOCTYPE}{\HCode{<!DOCTYPE html>}}
+
+\newcommand{\wrapwithdiv}[1]{
+ \ConfigureEnv{#1}
+ {\HCode{<div class="#1">}}
+ {\HCode{</div>}}
+ {}
+ {}
+}
+
+
+\wrapwithdiv{fallacy}
+\wrapwithdiv{pitfall}
+\wrapwithdiv{exercise}
+\wrapwithdiv{answer}
+\wrapwithdiv{sidebar}
+\wrapwithdiv{sidebargraphic}
+
+\ConfigureEnv{elaboration}
+ {\HCode{<div class="elaboration"><span class="elabname">Elaboration: </span>}}
+ {\HCode{</div>}}
+ {}
+ {}
+
+% Syntax for customizing sectional headings (LaTeX Web Companion, p.175):
+% \Configure{unit}{beforemarkup}{aftermarkup}{beforesectionname}{aftersectionname}
+% use \ConfigureToc instead of \Configure to control TOC entry generation
+%% \Configure{tableofcontents}
+%% {\HCode{<!-- begin TOC -->}}
+%% {\HCode{</div>} \input{footer} \HCode{</div><!-- end TOC -->}}
+%% {}
+%% {\HCode{<br/>}}
+%% {\HCode{<br/>}}
+
+%% \ConfigureToc{chapter}{\HCode{<br/>}}{~}{}{\HCode{<br/>}}
+%% \ConfigureToc{likechapter}{\HCode{<br/>}}{~}{}{\HCode{<br/>}}
+%% \ConfigureToc{section}{~~~}{~}{}{\HCode{<br/>}}
+%% \ConfigureToc{likesection}{~~~}{~}{}{\HCode{<br/>}}
+
+% \TocAt*{chapter}
+
+\ConfigureEnv{dedication}
+ {\HCode{<div data-role="page" id="dedication"><div data-role="content">}}
+ {\HCode{</div>} \input{footer} \HCode{</div><!-- end of dedication -->}}
+ {}
+ {}
+
+\Configure{maketitle}
+ {\HCode{<div data-role="page" id="titlepage"><div data-role="content">}}
+ {\HCode{</div>} \input{footer} \HCode{</div><!-- end of titlepage -->}}
+ {}
+ {}
+
+% same configuration for chapter and likechapter (chapter*, ie foreword and appendices)
+
+\Configure{chapter}
+ {\HCode{<div data-role="page" id="chapter_\thechapter">
+ <div data-role="content"> <h2 class="chapterhead">} \thechapter. }
+ {}
+ {}
+ {\HCode{</h2>}}
+
+\Configure{likechapter}
+ {\HCode{<!-- likechapter --><div data-role="page" id="chapter_\thechapter">
+ <div data-role="content"> <h2 class="chapterhead">} Foreword}
+ {}
+ {}
+ {\HCode{</h2>}}
+
+\Configure{appendix}
+ {\HCode{<!-- appendix --><div data-role="page" id="chapter_\thechapter">
+ <div data-role="content"> <h2 class="chapterhead">} \thechapter. }
+ {}
+ {}
+ {\HCode{</h2>}}
+
+\ConfigureEnv{chapterfrontmatter}
+ {}
+ {\HCode{</div>} \input{footer} \HCode{</div><!-- end chapter frontmatter -->}}
+ {}
+ {}
+
+\Configure{section}
+ {\HCode{<div data-role="page" id="section_\thesection">
+ <div data-role="content">
+ <h3 class="sectionhead">}\thesection }
+ {\HCode{ </div><!-- end of section content -->}
+ \input{footer}
+ \HCode{</div><!-- end of section -->}}
+ { }
+ {\HCode{</h3>}}
+
+\Configure{likesection}
+ {\HCode{<div data-role="page" id="section_\thesection">
+ <div data-role="content">
+ <h3 class="sectionhead">}}
+ {\HCode{</div><!-- end of section content -->}
+ \input{footer}
+ \HCode{</div><!-- end of section -->}}
+ { }
+ {\HCode{</h3>}}
+
+\Configure{caption}
+ {\HCode{<div class="caption">}}
+ {: }
+ {}
+ {\HCode{</div>}}
5 html.cfg
@@ -0,0 +1,5 @@
+\Preamble{}
+\Configure{DOCTYPE}{\HCode{<!DOCTYPE html>}}
+\begin{document}
+
+\EndPreamble{}
29 listingdefaults.tex
@@ -0,0 +1,29 @@
+\usepackage{color}
+\usepackage{textcomp} % needed for 'upquote' option below
+\definecolor{ListingColor}{RGB}{248,248,248}
+\lstset{ %
+ language=Ruby, % the language of the code
+ upquote=true, % use straight quotes not curly quotes
+ basicstyle=\scriptsize\ttfamily\upshape, % the size and style of the fonts that are used for the code
+ commentstyle=\slshape,
+ xrightmargin=0.1in,
+ xleftmargin=0.2in,
+ numbers=left, % where to put the line-numbers
+ numberstyle=\footnotesize, % the size of the fonts that are used for the line-numbers
+ stepnumber=1, % the step between two line-numbers. If it's 1, each line
+ % will be numbered
+ numbersep=8pt, % how far the line-numbers are from the code
+ backgroundcolor=\color{ListingColor}, % choose the background color. You must add \usepackage{color}
+ showspaces=false, % show spaces adding particular underscores
+ showstringspaces=false, % underline spaces within strings
+ showtabs=false, % show tabs within strings adding particular underscores
+ frame=trbL, % adds a frame around the code
+ tabsize=2, % sets default tabsize to 2 spaces
+ captionpos=b, % sets the caption-position to bottom
+ breaklines=true, % sets automatic line breaking
+ breakatwhitespace=false, % sets if automatic breaks should only happen at whitespace
+ %title=\lstname, % show the filename of files included with \lstinputlisting;
+ % also try caption instead of title
+ escapeinside={\%*}{*)}, % if you want to add a comment within your code
+ morekeywords={*,...} % if you want to add more keywords to the set
+}
548 macros.tex
@@ -0,0 +1,548 @@
+\setcounter{errorcontextlines}{400}
+
+% line breaking tolerances
+\tolerance=1000 % usually 200, infinite=10000
+% last ditch effort: how much space OK to insert
+% in a line to avoid overfull
+\setlength{\emergencystretch}{3em}
+
+% write metadata about screencasts to output file
+\newwrite\screencastdata
+\openout\screencastdata=\jobname.screencasts
+
+\ifhtmloutput
+% \newcommand{\mtctitle}{}
+\else
+ \usepackage{minitoc}
+ \renewcommand{\mtctitle}{}
+ \usepackage[dvips,color,innerbars]{changebar}
+\fi
+\usepackage{xstring} % provides StrSubstitute
+\usepackage{natbib}
+% natbib redefines the bibliography environment, so we have to override
+% its definition after loading it to make the chapter bibs section-like
+% rather than chapter-like
+\makeatletter
+ \renewenvironment{thebibliography}[1]
+ {%\addtocounter{section}{1}
+ %\section*{References In This Chapter}%
+ \@mkboth{\MakeUppercase\bibname}{\MakeUppercase\bibname}%
+ \list{\@biblabel{\@arabic\c@enumiv}}%
+ {\settowidth\labelwidth{\@biblabel{#1}}%
+ \leftmargin\labelwidth
+ \advance\leftmargin\labelsep
+ \@openbib@code
+ \usecounter{enumiv}%
+ \let\p@enumiv\@empty
+ \renewcommand\theenumiv{\@arabic\c@enumiv}}%
+ \sloppy
+ \clubpenalty4000
+ \@clubpenalty \clubpenalty
+ \widowpenalty4000%
+ \sfcode`\.\@m}
+ {\def\@noitemerr
+ {\@latex@warning{Empty `thebibliography' environment}}%
+ \endlist}
+\makeatother
+
+\usepackage{bibunits}
+\usepackage{ifthen}
+\usepackage{tabularx}
+\usepackage{array}
+\usepackage{graphicx}
+\usepackage{fancyvrb}
+\usepackage{verbatim}
+%\usepackage[font=small,labelfont=bf,margin=10pt]{caption}
+
+% Textual ``figures''
+\ifhtmloutput
+ \newenvironment{textfigure}{}{}
+\else
+ \newenvironment{textfigure}{\hrule\small}{\vspace{0.5ex}\hrule}
+\fi
+
+% highlighting deltas from a previous explanation
+
+\ifhtmloutput
+ \newenvironment{changebar}{\HCode{<span class="changebar">}}{\HCode{</span>}}
+\else
+ \setlength{\changebarwidth}{0.5em}
+ \definecolor{ChangebarColor}{rgb}{0.05,0,0.5}
+ \cbcolor{ChangebarColor}
+\fi
+\usepackage{endnotes}
+\usepackage{listings} \input{listingdefaults}
+\usepackage[figuresright]{rotating}
+\usepackage{floatpag}
+\rotfloatpagestyle{empty}
+\usepackage{makeidx}
+ \makeindex
+\usepackage{enumerate}
+\usepackage{fancybox}
+
+\usepackage{hyperref}
+ \ifhtmloutput
+ \hypersetup{hyperfootnotes=false}
+ \fi
+
+
+%\usepackage{glossaries}
+
+% shortcuts
+\newcommand{\fillinblank}{\_\_\_\_}
+\newcommand{\n}{\tabularnewline}
+\newcommand{\tg}{\textgreater}
+\newcommand{\tl}{\textless}
+\newcommand{\ttil}{\textasciitilde}
+\newcommand{\tcaret}{\textasciicircum}
+\ifhtmloutput
+ \ifmobioutput
+ \newcommand{\T}[1]{\HCode{<tt>}#1\HCode{</tt>}}
+ \newcommand{\B}[1]{\HCode{<strong>}#1\HCode{</strong>}}
+ \newcommand{\Sf}[1]{\HCode{<span class="sans-serif">}#1\HCode{</span>}}
+ \newcommand{\C}[1]{\HCode{<span style="color: \#800;"><tt>}#1\HCode{</tt></span>}}
+ \else
+ \newcommand{\T}[1]{\HCode{<span class="typewriter">}#1\HCode{</span>}}
+ \newcommand{\B}[1]{\HCode{<span class="strong">}#1\HCode{</span>}}
+ \newcommand{\Sf}[1]{\HCode{<span class="sans-serif">}#1\HCode{</span>}}
+ \newcommand{\C}[1]{\HCode{<span class="code-example">}#1\HCode{</span>}}
+ \fi
+\else
+ \newcommand{\T}{\texttt}
+ \newcommand{\B}{\textbf}
+ \newcommand{\Sf}[1]{\textsf{\emph{#1}}}
+ %\definecolor{TextHighlight}{rgb}{0.05,0,0.50}
+ \definecolor{TextHighlight}{rgb}{0.1,0.1,0.1}
+ \newcommand{\C}[1]{\textsf{\upshape{\textbf{#1}}}}
+\fi
+
+% make caption font smaller (tip from
+% http://dcwww.camd.dtu.dk/~schiotz/comp/LatexTips/LatexTips.html#captfont)
+\newcommand{\captionfonts}{\scriptsize\bf}
+\ifhtmloutput
+ %
+\else
+ \makeatletter % Allow the use of @ in command names
+ \long\def\@makecaption#1#2{%
+ \begin{centering}
+ \vskip\abovecaptionskip % space between figure & caption
+ \sbox\@tempboxa{{\captionfonts #1: #2}}%
+ \ifdim \wd\@tempboxa >\hsize
+ {\captionfonts #1: #2\par}
+ \else
+ \hbox to\hsize{\hfil\box\@tempboxa\hfil}%
+ \fi
+ \end{centering}
+ \vskip\belowcaptionskip% space between caption & text
+ }
+ \makeatother % Cancel the effect of \makeatletter
+\fi
+
+% hyperlinks
+
+\ifhtmloutput
+ \newcommand{\weblink}[2]{\href{#1}{#2}}
+\else
+ \newcommand{\weblink}[2]{#2\endnote{\expandafter \url{#1}}}
+ %\newcommand{\weblink}[2]{#2 ({\url{#1}})}
+\fi
+
+
+% Quotation in small-ish type with attribution
+% Example use:
+% \makequotation{There's a sucker born every minute.}{W.C. Fields}
+
+\ifhtmloutput
+ \ifmobioutput
+ \newcommand{\makequotation}[2]{\HCode{%
+ <div class="makequotation" align="center">
+ <table cellpadding="10" style="margin-left: 0;">
+ <tr><td align="center" border="1" style="background-color: \#c0c0c0">}%
+ #1\HCode{<i>} #2\HCode{</i>
+ </td></tr>
+ </table>
+ </div>}}
+ \else
+ \newcommand{\makequotation}[2]{\begin{quote}\small\emph{#1} ---#2\end{quote}}
+ \fi
+\else
+ \newcommand{\makequotation}[2]{\begin{quote}\small\emph{#1}
+
+ \hfill---#2\end{quote}}
+\fi
+
+% Chapter front matter (used only by html build)
+\newenvironment{chapterfrontmatter}{}{}
+
+% Chapter overview (key messages)
+% Example use:
+% \chapteroverview{User stories are important. Let's learn about them.}
+%
+% implemented as Subsection for TOC inclusion:
+\newcommand{\chapteroverview}[1]{
+ \addtocontents{toc}{#1}
+ \subsection*{#1}
+ \vspace{0.25in}
+ \ifhtmloutput\else\minitoc\fi
+ \vfill\clearpage}
+
+% Sidebar - appearance will vary depending on output target, and may not
+% actually be along the side.
+% Example use:
+% \begin{sidebar}{Historical note}
+% All this stuff will be in a sidebar.
+% You can include most elements that would go in normal text, like
+% figures, citations, etc.
+% \end{sidebar}
+
+\definecolor{SidebarColor}{rgb}{0.9,0.9,0.9}
+\ifhtmloutput
+ \newenvironment{sidebar}[2][]%
+ {\HCode{<span class="sidebar_name"><b><i>}#2{\HCode{</i></b></span>}}}
+ {}
+\else
+ \newcommand{\thesidebarraise}{}
+ \newenvironment{sidebar}[2][-2ex]%
+ {\renewcommand{\thesidebarraise}{#1}%
+ \begin{Sbox}\begin{minipage}{\marginparwidth}\sidebarfont\raggedright \textbf{#2}}%
+ {\end{minipage}\end{Sbox}\marginpar{\vspace{\thesidebarraise}\colorbox{SidebarColor}{\TheSbox}}}
+\fi
+
+% Sidebars with photos - need to be small in PDF Output
+% Small Kindle is 520x622 px; images 260x311 or smaller won't be resized on device
+\ifhtmloutput
+ \ifmobioutput
+ \newenvironment{sidebargraphic}[3][]%
+ {\HCode{<img width="150" src="#2_SB.jpg"/>}
+ \HCode{<span class="sidebar_name"><b><i>}#3{\HCode{</i></b></span>}}}
+ {}
+ \else
+ \newenvironment{sidebargraphic}[3][]%
+ {\HCode{<img class="sidebarimage" src="#2.jpg">}
+ \HCode{<span class="sidebar_name">}#3\HCode{</span>}}
+ {}
+ \fi
+\else
+ \newcommand{\thesidebarimg}{}% hack- numbered params can't appear in end content of \newenvironment
+ \newenvironment{sidebargraphic}[3][0in]%
+ {\renewcommand{\thesidebarraise}{#1}%
+ \renewcommand{\thesidebarimg}{#2}%
+ \begin{Sbox}\begin{minipage}{\marginparwidth}\sidebarfont\raggedright \textbf{#3}}%
+ {\hfill\\ \includegraphics[width=\marginparwidth]{\thesidebarimg}\end{minipage}\end{Sbox}\marginpar{\vspace{\thesidebarraise}\colorbox{SidebarColor}{\TheSbox}}}
+\fi
+
+
+
+% Endnotes - in print version, footnotes/endnotes come at end of
+% chapters
+
+\newenvironment{tolearnmore}{\section{To Learn More}}{%
+ % per-chapter bibliography
+ \putbib
+ % Endnotes
+ \ifhtmloutput\else%
+ \theendnotes
+ \setcounter{endnote}{0}
+ \fi
+}
+
+% Code - uses \verbatiminput to put in file 'undecorated' for HTML use,
+% or \lstlisting to decorate it with styling and line numbers for PDF
+\ifhtmloutput
+ \ifmobioutput
+ \newcommand{\codefile}[2][u/saasbook]{\HCode{<a class="pastebin" href="http://pastebin.com/#1">http://pastebin.com/#1</a><br/><pre class="code">}
+
+ \begin{minipage}{\textwidth}% prevent gratuitous blank lines
+ \verbatiminput{#2}%
+ \end{minipage}%
+ \HCode{</pre><hr>}}
+ \else
+ \newcommand{\codefile}[2][u/saasbook]{\HCode{<a class="pastebin" href="http://pastebin.com/#1">http://pastebin.com/#1</a><pre class="code">}
+ \verbatiminput{#2}
+
+ \HCode{</pre>}}
+ \fi
+\else
+ \newcommand{\codefile}[2][u/saasbook]{%
+ \noindent\begin{minipage}{\textwidth}%
+ \Sf{\scriptsize{}http://pastebin.com/#1}%
+ \vspace{-1ex}%
+ \lstinputlisting{#2}%
+ \end{minipage}}
+\fi
+
+% Fallacies and pitfalls
+% \begin{fallacy}{The moon is made of green cheese.}
+% It really isn't.
+% \end{fallacy}
+
+\ifhtmloutput
+ \newcommand{\fallaciesandpitfalls}{}
+\else
+ \newcommand{\fallaciesandpitfalls}{%
+ ~\vspace{-1.5ex}
+
+ }
+\fi
+
+\ifhtmloutput
+ \ifmobioutput
+ \newenvironment{fallacy}[1]%
+ {\HCode{<img src="icons/mobi/fallacy.png"><span style="font-weight: bold; color: red;">Fallacy: }#1\HCode{</span> }}
+ {}
+ \newenvironment{pitfall}[1]%
+ {\HCode{<img src="icons/mobi/pitfall.png"><span style="font-weight: bold; color: red;">Pitfall: }#1\HCode{</span> }}
+ {}
+ \else
+ \newenvironment{fallacy}[1]{\Picture{icons/fallacy.png class="marginicon"}\B{\emph{Fallacy:} #1}}{}
+ \newenvironment{pitfall}[1]{\Picture{icons/pitfall.png class="marginicon"}\B{\emph{Pitfall:} #1}}{}
+ \fi
+\else
+ \newcommand{\iconwithtext}[3]{%
+ \vspace{1ex}%
+ \hspace{-\parindent}%
+ \begin{tabular}{b{0.25in}b{4.5in}}%
+ \includegraphics[width=0.25in]{icons/#1} & \B{\emph{#2: }\/#3} \\ %
+ \end{tabular}%
+ \vspace{1ex}}
+ \newenvironment{fallacy}[1]%
+ {\iconwithtext{fallacy.pdf}{Fallacy}{#1}
+
+ }
+ {\vspace{0.5ex}}
+ \newenvironment{pitfall}[1]%
+ {\iconwithtext{pitfall.pdf}{Pitfall}{#1}
+
+ }
+ {\vspace{0.5ex}}
+\fi
+
+% Pull in Exercises
+\newcommand{\exercises}[1]{\input{ch_#1/exercises_#1}}
+
+% Screencast - in print version, just a URL link; in HTML5 version,
+% embedded quicktime player or youtube player??
+
+\newcounter{screencast}[section]
+\renewcommand*\thescreencast{\thesection.\arabic{screencast}}
+\newcommand{\screencastprefix}{http://vimeo.com/}
+\newcommand{\screencastsuffix}{}
+
+\newcommand{\screencast}[5][saasbook/videos]{%
+ \refstepcounter{screencast}
+ \label{#2}\relax%
+ % write metadata to output file
+ \write\screencastdata{\thescreencast: #2@@#3@@#4##@5}
+ %
+ \ifhtmloutput
+ \ifmobioutput
+ \HCode{<hr>
+ <div class="screencast"> Screencast \thescreencast: <a class="screencast-url" %
+ href="\screencastprefix#1\screencastsuffix">#3</a>}%
+ \HCode{<div class="screencastsummary">}#5\HCode{</div></div>
+ <hr>}
+ \else
+ \HCode{<div class="screencast"><h2>Screencast \thescreencast: <span class="screencast_title">}#3\HCode{</span></h2>}
+ \HCode{ <video src="#4" width="600" controls="controls">}
+ \HCode{ <!-- for browsers that don't support embedded video tag -->}
+ \HCode{ <a class="screencast-url" href="\screencastprefix#1\screencastsuffix">Watch on Vimeo</a>}
+ \HCode{ </video>}
+ \HCode{<div class="screencastsummary">}#5\HCode{</div></div>}
+ \fi
+ \else
+ \hfill
+ \begin{minipage}{0.9\textwidth}
+ \rule{\textwidth}{0.5pt}\hfill\\
+ \small\textbf{Screencast \thescreencast: #3.} \\
+ \url{\screencastprefix#1\screencastsuffix}\\
+ #5\relax \\
+ \rule{\textwidth}{0.5pt}\hfill\\
+ \end{minipage}
+ \hfill
+ \fi
+}
+
+% Elaboration - optional material that's longer than a sidebar
+
+%\definecolor{ElaborationColor}{rgb}{0.05,0.0,0.50}
+\definecolor{ElaborationColor}{rgb}{0.1,0.1,0.1}
+\ifhtmloutput
+ \ifmobioutput
+ \newenvironment{elaboration}[1]%
+ {\HCode{<hr><b>E</b><small><b>LABORATION:</b> #1<br/> }}
+ {\HCode{</small><hr>}}
+ \else
+ \newenvironment{elaboration}[1]%
+ {\HCode{<span class="elaboration_name">#1</span> }}
+ {}
+ \fi
+\else
+ \newcommand{\aboveelaborationskip}{1.5ex}
+ \newcommand{\belowelaborationskip}{1.5ex}
+ \newcommand{\elaborationpadding}{0.5ex}
+ \newenvironment{elaboration}[1]%
+ {\color{ElaborationColor} %
+ \begin{Sbox}\begin{minipage}[r]{0.9\textwidth}%
+ \vspace{\aboveelaborationskip}
+ \rule{\textwidth}{2pt}
+ \vspace{\elaborationpadding}
+ \rule{1ex}{1ex}\hspace{2pt}\small\emph{\textbf{Elaboration: #1\relax}}
+ \vspace{\elaborationpadding}
+ \hrule
+ \vspace{\elaborationpadding}
+ }%
+ {\vspace{\elaborationpadding}
+ \hrule
+ \end{minipage}\end{Sbox}
+ \hfill\mbox{\TheSbox}
+ \vspace{\belowelaborationskip}
+ }
+\fi
+
+% LaTeX macros that aren't automatically translated to HTML
+\ifhtmloutput
+ \renewcommand{\copyright}{\HCode{&copy;}}
+\fi
+
+% forced page breaks
+\ifhtmloutput
+ \newcommand{\flushpage}{\clearpage\HCode{<mbp:pagebreak/>}}
+\else
+ \newcommand{\flushpage}{\clearpage}
+\fi
+
+% graphics - for HTML/mobi, use the gif version
+\ifhtmloutput
+ \newcommand{\picfigure}[4][width=\textwidth]{%
+ \begin{figure}
+ \Picture{#2.gif id="#3"}
+ \ifthenelse{\equal{#4}{}}{}{\caption{\label{#3}#4}}
+ \end{figure}}
+\else
+ \newcommand{\picfigure}[4][width=\textwidth]{%
+ \begin{figure}
+ \includegraphics[#1]{#2}
+ \ifthenelse{\equal{#4}{}}{}{\caption{\label{#3}#4}}
+ \end{figure}}
+\fi
+
+% table figures - will eventually do something different for mobi
+\newcommand{\tablefigure}[3]{%
+ \begin{figure}
+ \input{#1}
+ \caption{\label{#2}%
+#3}
+ \end{figure}
+}
+
+
+% Summary - key points summarizing a chapter or section
+
+\ifhtmloutput
+ \ifmobioutput
+ \newenvironment{summary}{}{}
+ \else
+ \newenvironment{summary}%
+ {\HCode{<div class="summary">}}%
+ {\HCode{</div>}}
+ \fi
+\else
+ \renewcommand{\shadowsize}{2pt}
+ \newenvironment{summary}%
+ {\vspace{2ex}\begin{Sbox}\begin{minipage}{0.96\textwidth}\noindent}
+ {\vspace{0.25\baselineskip}\end{minipage}\end{Sbox}\noindent\hspace{0in}\shadowbox{\TheSbox}}
+\fi
+
+% Note to self
+\newcommand{\note}[1]{\fbox{#1}}
+
+% DRYing out codefile examples
+\newcommand{\codefilefigure}[4][u/saasbook]{% filename, reflabel, caption
+ \begin{figure}
+ \codefile[#1]{#2}
+ \caption{\label{#3}
+ #4
+ }
+ \end{figure}%
+}
+
+% Crosscutting themes - icon or note in margin
+\ifhtmloutput
+ \ifmobioutput
+ \newcommand{\crosscutting}[2]{%
+ \HCode{<img align="right" width="40" src="icons/mobi/#2.png"/>&nbsp;}}
+ \else
+ \newcommand{\crosscutting}[2]{%
+ \HCode{<img class="crosscutting" src="icons/#2.png"/>&nbsp;}}
+ \fi
+\else
+ \newcommand{\crosscutting}[2]{%
+ \marginpar{\vspace{#1}\includegraphics[width=0.5in]{icons/#2.png}}}
+\fi
+\newcommand{\dry}[1][0.5ex]{\crosscutting{#1}{dry}}
+\newcommand{\reuse}[1][0.5ex]{\crosscutting{#1}{reuse}}
+\newcommand{\codegen}[1][0.5ex]{\crosscutting{#1}{codegen}}
+\newcommand{\concise}[1][0.5ex]{\crosscutting{#1}{concise}}
+\newcommand{\coc}[1][0.5ex]{\crosscutting{#1}{coc}}
+\newcommand{\legacy}[1][0.5ex]{\crosscutting{#1}{legacy}}
+\newcommand{\beauty}[1][0.5ex]{\crosscutting{#1}{beauty}}
+\newcommand{\tool}[1][0.5ex]{\crosscutting{#1}{tool}}
+\newcommand{\learnbydoing}[1][0.5ex]{\crosscutting{#1}{learnbydoing}}
+\newcommand{\automation}[1][0.5ex]{\crosscutting{#1}{automation}}
+
+% ``Learn by doing''
+
+% Table of contents formatting
+\setcounter{tocdepth}{5}
+%\newcommand{\chapteroverview}[1]{\addcontentsline{toc}{section}{#1}}
+
+\newcommand{\gloss[1]}{\textbf{#1}}
+
+% Exercises
+\newtheorem{exercise}{Exercise}[chapter]
+% Check Yourself
+\newtheorem{checkyourself}{Self-Check}[section]
+% answers embedded in self-checks
+\ifhtmloutput
+ \ifmobioutput
+ \newenvironment{answer}{\HCode{<img height="15" width="15" src="icons/mobi/green_dot.gif"/>}}{}
+ \else
+ \newenvironment{answer}{}{}
+ \fi
+\else
+ \newenvironment{answer}%
+ {\hfill \\ \noindent\upshape$\diamond$ }%
+ {\rule{1ex}{1ex}~~}
+\fi
+
+% answers to exercises: omit from book, collect elsewhere
+%\newenvironment{exanswer}{}{}
+\let\exanswer=\comment
+\let\endexanswer=\endcomment
+
+
+% Wikipedia link
+
+\newcommand{\w}[2][]{%
+ \ifhtmloutput
+ \ifthenelse{\equal{#1}{}}{%
+ \StrSubstitute{#2}{ }{_}[\thewikilink]%
+ \weblink{http://en.wikipedia.org/wiki/\thewikilink}{\emph{\B{#2}}}%
+ }{%
+ \StrSubstitute{#1}{ }{_}[\thewikilink]%
+ \weblink{http://en.wikipedia.org/wiki/\thewikilink}{\emph{\B{#2}}}%
+ }%
+ \else%
+ \textsf{\slshape\B{#2}}%
+ \fi%
+ \ifhtmloutput%
+ \else%
+ \index{#2}%
+ \ifthenelse{\equal{#1}{}}{}{\index{#1}}%
+ \relax%
+ \fi%
+}
+
+
+% important term - not a link
+\newcommand{\x}[1]{\emph{\B{#1}}}
121 mobi.4ht
@@ -0,0 +1,121 @@
+% -*- mode: latex -*-
+% HTML information for Kindle/Mobipocket HTML conversion
+
+
+\newcommand{\wrapwithdiv}[1]{
+ \ConfigureEnv{#1}
+ {\HCode{<div class="}#1\HCode{">}}
+ {\HCode{</div>}}
+ {}
+ {}
+}
+\wrapwithdiv{selfcheck}
+\wrapwithdiv{exercise}
+\wrapwithdiv{answer}
+
+\ConfigureEnv{minipage}{}{}{}{}
+
+\Configure{tableofcontents}
+ {\HCode{<a name="TOC"/>}}% required for Kindle to find the TOC
+ {}
+ {}
+ {}
+
+\ConfigureToc{chapter}{\HCode{<br/>}}{~}{}{\HCode{<br/>}}
+\ConfigureToc{likechapter}{\HCode{<br/>}}{~}{}{\HCode{<br/>}}
+\ConfigureToc{appendix}{\HCode{<br/>Appendix }}{~}{}{\HCode{<br/>}}
+\ConfigureToc{section}{~~~}{~}{}{\HCode{<br/>}}
+\ConfigureToc{likesection}{~~~}{~}{}{\HCode{<br/>}}
+
+\ConfigureToc{likesubsection}%
+ {\HCode{<div class="toc_subsection" align="center">
+ <table cellpadding="10">
+ <tr><td width="2\%"></td>
+ <td align="left" style="background-color: \#f0f0f0;">}}
+ {~}
+ {}
+ {\HCode{</td></tr></table></div>}}
+\NoLink{likesubsection} % chapter summaries in TOC shouldn't be links
+
+% Put a mini-TOC of the sections of a chapter at the start of each chapter
+\TocAt*{chapter,section}
+
+\Configure{chapter}
+ {}
+ {}
+ {\HCode{<mbp:pagebreak/><br/><br/><br/><h2 class="chapter_name"><i>}\thechapter{}. }
+ {\HCode{</i></h2>}}
+
+\Configure{likechapter}
+ {}
+ {}
+ {\HCode{<mbp:pagebreak/><br/><br/><br/><h2 class="chapter_name"><i>}}
+ {\HCode{</i></h2>}}
+
+\Configure{appendix}
+ {}
+ {}
+ {\HCode{<mbp:pagebreak/><br/><br/><br/><h2 class="chapter_name"><i>}Appendix \thechapter. }
+ {\HCode{</i></h2>}}
+
+\ConfigureEnv{sidebar}
+ {\HCode{%
+ <div class="sidebar" align="center">%
+ <table cellpadding="10">%
+ <tr><td width="2\%" style="background-color: \#404040;"></td>%
+ <td align="left" style="background-color: \#f0f0f0;"><p width="0"><small>}}
+ {\HCode{%
+ </small></p></td></tr>%
+ </table></div>}}
+ {}
+ {}
+
+\ConfigureEnv{sidebargraphic}
+ {\HCode{%
+ <div class="sidebar" align="center">%
+ <table cellpadding="10">%
+ <tr><td width="2\%" style="background-color: \#404040;"></td>%
+ <td align="left" style="background-color: \#f0f0f0;">}}
+ {\HCode{%
+ </td></tr>%
+ </table></div>}}
+ {}
+ {}
+
+\ConfigureEnv{summary}
+ {\HCode{%
+ <hr><div class="summary" bgcolor="\#c0c0c0">}}%
+ {\HCode{</div><hr>}}
+ {}
+ {}
+
+\ConfigureEnv{tabular}{}{}
+\ConfigureEnv{array}{}{}
+\Configure{centerline}{\HCode{<center>}}{\HCode{</center}}
+\ConfigureEnv{figure}{}{}{}{}
+
+\ConfigureEnv{textfigure}
+ {\HCode{<div>}}
+ {\HCode{</div>}}
+ {}
+ {}
+
+\Configure{tabular}
+ {\HCode{<table class="texttable" border="0">}} % at start of table
+ {\HCode{</table>}} % at end of table
+ {\HCode{<tr>}} % at beginning of row
+ {\HCode{</tr>}} % at end of row
+ {\HCode{<td align="left" \ifnum\HMultispan>1 colspan="\HMultispan"\fi>}} % atbeginning of cell
+ {\HCode{</td>}} % at end of cell
+
+\Configure{caption}
+ {\HCode{%
+ <div class="caption" align="center">%
+ <table cellpadding="10">%
+ <tr><td align="left" style="background-color: \#c0c0c0;"><p width="0"><small><b>}}
+ {: \HCode{</b>} }
+ {}
+ {\HCode{%
+ </small></p>
+ </td></tr>%
+ </table></div>}}
6 mobi.cfg
@@ -0,0 +1,6 @@
+\Preamble{}
+\begin{document}
+
+\HCode{<link rel="stylesheet" type="text/css" href="css/mobi.css"/>}
+
+\EndPreamble{}
0 pasties.yml
No changes.
1 script/Gemfile
@@ -0,0 +1 @@
+gem 'nokogiri'
9 script/Gemfile.lock
@@ -0,0 +1,9 @@
+GEM
+ specs:
+ nokogiri (1.5.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ nokogiri
41 script/changebars
@@ -0,0 +1,41 @@
+#!/usr/bin/perl
+
+sub help {
+ printf STDERR <<Help;
+$0: add changebars to LaTeX files
+Usage: $0 add '<refspec>' filename.tex - add changebars to filename.tex
+ '<refspec>' is a Git commit ID or reference, such as 'master\@{2 days ago}'
+ $0 remove filename.tex - remove changebars from filename.tex
+Original file will be left in filename.tex.bak
+Help
+exit 0;
+}
+
+
+($what, $rev, $file) = @ARGV;
+$file = $rev if $what =~ /remove/;
+&help if $#ARGV==0 || grep(/--?h/, @ARGV) || !$file || !-r $file ;
+
+$has_changebars = (system("grep \\\\cbstart $file >/dev/null") == 0);
+
+if ($what =~ /^remove$/) {
+ die "$file has no changebars\n" unless $has_changebars;
+ system "sed -e s/\\\\\\\\cbstart{}//g -e s/\\\\\\\\cbend{}//g -i.bak $file";
+ printf STDERR "Change bars removed from $file\n";
+ exit 0;
+}
+
+# ensure file doesn't already have changebars!
+die "$file already has change bars! Remove them first\n" if $has_changebars;
+
+open(CMD, "GIT_DIFF_OPTS='-u0 --ignore-all-space' git diff '$rev' $file |") or die $!;
+$nchangebars = 0;
+while (<CMD>) {
+ next unless /^@@ -[0-9,]+ \+([0-9]+),?([0-9]+)?/;
+ $start = $1;
+ $end = $start + ($2 || 0);
+ $cmd = "sed -e ${start}s/^/\\\\\\\\cbstart{}/ -e ${end}s/\$/\\\\\\\\cbend{}/ -i.bak $file";
+ $nchangebars++ unless system($cmd)!=0;
+}
+printf STDERR "$nchangebars changebars added to $file\n";
+
148 script/html_postprocess.rb
@@ -0,0 +1,148 @@
+#!/usr/bin/env ruby
+
+require 'rubygems'
+require 'nokogiri'
+
+def warn(str)
+ $stderr.puts str
+end
+
+theuniquechapter = 0
+ARGV.each do |filename|
+ begin
+ doc = Nokogiri::HTML::Document.parse(IO.read(filename))
+ # preamble = Nokogiri::HTML::DocumentFragment.parse(IO.read('html_preamble.html'))
+
+ warn "#{filename}"
+
+ # get rid of all crosslinks
+ s = doc.xpath("//div[@class='crosslinks']").remove
+ warn " #{s.length} crosslink div's removed"
+
+ # get rid of tail links
+ s = doc.xpath("//p/a[starts-with(@id,'tail')]").remove
+ warn " #{s.length} tail links removed"
+
+ # get rid of tex4ht-generated TOCs
+ if (toc = doc.at_css('div#tableofcontents'))
+ toc.remove
+ warn " Table of contents removed"
+ end
+
+ case filename
+ when /htmlch/ # chapter
+ thechapternum = doc.at_css('h2.chapterhead').content.gsub(/[^0-9]/,'')
+ thechapter = "Chapter #{thechapternum}"
+ when /htmlli/ # unnumbered chapter (foreword, etc)
+ thechapternum = theuniquechapter
+ thechapter = doc.at_css('h2.chapterhead').content
+ # tex4ht puts id="section_0.0" for all unnumbered secs and
+ # "chapter_0" for all unnumbered chapters, so fix that
+ doc.at_css('div#chapter_0').set_attribute 'id', "chapter_#{theuniquechapter}"
+ theuniquechapter += 1
+ thesec = 1
+ doc.xpath("//div[@data-role='page']").each do |sectiondiv|
+ if sectiondiv.get_attribute('id') =~ /0\.0/
+ sectiondiv.set_attribute 'id', "section_#{theuniquechapter}.#{thesec}"
+ thesec += 1
+ end
+ end
+ when /htmlap/ # appendix
+ thechapter = doc.at_css('h2.chapterhead').content
+ thechapter =~ /^\s*(\w)/
+ thechapternum = $1
+ end
+
+ warn " chapter number: #{thechapternum} name: #{thechapter}"
+
+ # insert Preamble inside <head>
+ # doc.at_css('head').add_child(preamble)
+ # warn " HTML preamble inserted"
+
+
+ # delete extraneous css link
+ s = doc.xpath("//link[@href='saasbook_html.css']").remove
+ warn " #{s.length} extraneous CSS link removed"
+
+ # change TOC links to point to #toc
+ doc.xpath("//a[@href='#x1-1000']").each { |e| e['href'] = '#toc' }
+
+ # get rid of any <p>'s that are direct child of <body>
+ s = doc.xpath("//body/p").remove
+ warn " #{s.length} extraneous <p>'s inside <body> removed"
+
+ # last row of every TABLE seems to contain empty whitespace <td>, so just delete entire row
+ s = doc.xpath("//table[count(tr)>1]/tr[count(td)=1]/td[.='']").remove
+ warn " #{s.length} weird blank table rows removed"
+
+ # removing horizontal rules in tables since they're unsupported in iBooks
+ s = doc.xpath("//tr[@class='hline']").remove
+ warn " #{s.length} table rows with horizontal rules removed"
+
+ # strip in-book links, since they're unsupported in iBooks
+ # s = doc.xpath("//a[starts-with(@href,'#')]")
+ s = doc.xpath("//a[not(starts-with(@href,'http')) and contains(@href,'#')]")
+ warn " stripping #{s.length} in-book links"
+ s.each { |a| a.replace(a.inner_html) }
+
+ # change all the video tags to reference the m4v files because iBooks only supports m4v videos
+ doc.xpath("//video[@src]").each { |video| video['src'] = video['src'].sub(".mp4", ".m4v") }
+
+ # collect all section links, and put them in every nav footer
+ # each should turn into:
+ # <option value="#section_3.1">3.1 Section Name</option>
+ # secnumcounter = 1
+ # section_navs = Nokogiri::XML::NodeSet.new(doc,
+ # doc.xpath("//h3[@class='sectionhead']").map do |sec|
+ # if sec.content.match( /^\s*(\w+\.\d+)\s+(.*)$/ )
+ # secnum, sectitle = $1,$2
+ # else
+ # secnum = "#{thechapternum}.#{secnumcounter}"
+ # sectitle = sec.content
+ # end
+ # secnumcounter += 1
+ # option = Nokogiri::XML::Node.new 'option', doc
+ # option.set_attribute 'value', "#section_#{secnum}"
+ # option.content = sec.content
+ # option
+ # end
+ # )
+ # warn " Generated #{section_navs.length} subsection nav elements"
+ # optgroups = doc.xpath("//optgroup")
+ # warn " Adding them to #{optgroups.length} nav optgroups"
+ # optgroups.each do |optiongroup|
+ # # there must be a nicer way to do this
+ # optiongroup.set_attribute 'label', thechapter
+ # optiongroup.add_child(section_navs.to_html)
+ # end
+
+ # generate html
+ html = doc.to_html
+
+ # some TeX markup that mistakenly gets included in t4ht output:
+ # BEFORE: \protect \relax \special {t4ht=<tt>}self.title\relax \special {t4ht=</tt>}
+ # AFTER: <tt>self.title</tt>
+ html.gsub!(/(?:\\protect)?\s*\\relax\s*\\special\s*\{t4ht=([^}]+)\}/, ' \1 ')
+
+ # tex4ht outputs an apparently-random-length string of underscores to
+ # render \hrule in LaTeX, so if we see >8 of them, replace with <hr> tag
+ html.gsub!(/_______+/, '<hr>')
+
+ # get rid of malformed answer and sidebar code
+ html.gsub!(/<div class="&lt;span\s+class="\s+cmti-10>answer"&gt;/, '<div>')
+ html.gsub!(/<div class="&lt;span\s+class="\s+cmti-10>sidebar"&gt;/, '<div>')
+
+ # get rid of stupid and often incorrect codepoints
+ bad_codepoints = {
+ # '8212' => '|',
+ }
+ bad_codepoints.each_pair do |k,v|
+ html.gsub!(/&\##{k};/, v)
+ end
+ File.open(filename, 'w') do |file|
+ file.write html
+ end
+ rescue => e
+ warn "EXCEPTION processing #{filename} - preserving original input:\n======\n#{e.message}"
+ end
+end
98 script/mobi_postprocess.rb
@@ -0,0 +1,98 @@
+#!/usr/bin/ruby
+
+require 'rubygems'
+require 'nokogiri'
+
+doc = Nokogiri::HTML::Document.parse(IO.read(ARGV[0]), nil, 'ISO-8859-1')
+
+
+# last row of actual text TABLES seems to contain a single TD with empty whitespace
+doc.xpath("//table[count(tr)>1]/tr[count(td)=1]/td[.='']").remove
+
+# empty table rows cause some tables to not render at all
+doc.xpath("//tr[count(td)=0]").remove
+
+# for some reason figures are wrapped in <div align="center"><table><tr><td>...</td></tr></table></div>
+# and this prevents figures that are tables from rendering properly.
+doc.xpath("//div[not(@class)]/table[count(tr)=1]/tr[count(td)=1]/td").each do |node|
+ topdiv = node.parent.parent.parent
+ node.children.each { |child| topdiv.add_previous_sibling(child) }
+ topdiv.remove
+end
+
+# turn chapter anchors from this: <h2 class='chapter_name|appendixHead'><i>1. <a name='xx'></a> Chapter Name</i></h2>
+# into this: <a name='xx'><h2><i>1. Chapter Name</i></h2></a>
+
+# first wrap each <h2> in the <a>
+doc.xpath("//h2/i/a").each do |node|
+ anchor_name = node.get_attribute 'name'
+ # reparent the <h2> by putting <a> around it
+ h2 = node.parent.parent
+ h2.swap("<a name=\"#{anchor_name}\">#{h2}</a>")
+end
+# then remove the spurious <a></a> inside the h2's
+doc.xpath("//h2/i/a").each { |node| node.remove }
+
+# same thing for <h3> (section heads)
+doc.xpath("//h3/a").each do |node|
+ anchor_name = node.get_attribute 'name'
+ # reparent the <h3>
+ h3 = node.parent
+ h3.swap("<a name=\"#{anchor_name}\">#{h3}</a>")
+end
+# then remove spurious <a></a> inside <h3>
+doc.xpath("//h3/a").each { |node| node.remove }
+
+# add line numbers to all inline code examples
+doc.xpath("//pre[@class='code']").each do |node|
+ # ugh - tex4ht puts stray </p> INSIDE <pre>, so nokogiri turns that
+ # into <pre> <p> content </p> </pre> - we need to get rid of <p>
+ if (inner_par = node.xpath('p'))
+ node.inner_html = inner_par.inner_html
+ inner_par.remove
+ end
+ lines = node.inner_text.gsub(/\A\s*\n/m, '').split("\n")
+ node.content = "\n" + (1..lines.length).zip(lines).map { |n| sprintf("%2d%s", *n) }.join("\n") + "\n"
+end
+
+# generate html
+html = doc.to_xhtml(:encoding => 'ISO-8859-1')
+
+# tex4ht outputs an apparently-random-length string of underscores to
+# render \hrule in LaTeX, so if we see >8 of them, replace with <hr> tag
+html.gsub!(/_______+/, '<hr>')
+
+# anywhere that we have two <hr> in a row with only an anchor in between, delete one <hr>
+# BEFORE: <hr><a name="x1-70022"></a><hr>
+# AFTER: <hr><a name="x1-70022"></a>
+html.gsub!(/<hr(?: \/)?>\s*(<a[^<]+<\/a>)?\s*(<!--l.\s+\d+--><p>\s*<\/p>)?\s*<hr(?: \/)?>/, '<hr>\1')
+
+# some TeX markup that mistakenly gets included in t4ht output:
+# BEFORE: \protect \relax \special {t4ht=<tt>}self.title\relax \special {t4ht=</tt>}
+# AFTER: <tt>self.title</tt>
+
+html.gsub!(/(?:\\protect)?\s*\\relax\s*\\special\s*\{t4ht=([^}]+)\}/, ' \1 ')
+
+# god only knows where the hell this comes from:
+# BEFORE: \protect \unhbox \voidb@x {\unhbox \voidb@x \special {t4ht@95}x}
+# AFTER: _
+html.gsub!('\protect \unhbox \voidb@x {\unhbox \voidb@x \special {t4ht@95}x}', '_')
+
+# 'nowrap' tag inserted by tex4ht makes tables unrenderable on kindle
+html.gsub!(/\s+nowrap>/, '>')
+
+# xml-to-html somehow mangles <mbp:pagebreak> to just <pagebreak>
+html.gsub!('<pagebreak></pagebreak>', '<mbp:pagebreak/>')
+
+
+# get rid of stupid and often incorrect codepoints
+bad_codepoints = {
+# '8212' => '|',
+}
+bad_codepoints.each_pair do |k,v|
+ html.gsub!(/&\##{k};/, v)
+end
+
+# insert <a name="start"> anchor at top of body
+html.gsub!(/<body>/, '<body><a name="start">')
+puts html
78 script/outl.pl
@@ -0,0 +1,78 @@
+#!/opt/local/bin/perl -n
+
+BEGIN {
+ sub usage {
+ return <<EndOfUsage;
+$0: generate outline from TeX/LaTeX files
+Usage: $0 [-n] a.tex b.tex... - generates outline from filenames
+ $0 [-n] -f masterfile.tex - searches masterfile.tex for \\input{}'s
+ -n means don't include any body text in outline
+EndOfUsage
+}
+ $find_inputs = '';
+ while ($ARGV[0] =~ /^-/) {
+ $_ = shift @ARGV;
+ $suppress_body = 1, next if /^-n/;
+ $find_inputs = shift @ARGV, next if /^-f/;
+ die &usage if /^-h/;
+ }
+ if ($find_inputs) {
+ # scan a single file for \input lines, then add those filenames to
+ # @ARGV.
+ open(FILE, $find_inputs) or die $!;
+ @lines = grep( /^\s*[^%]/, (<FILE>));
+ grep(chomp, @lines);
+ push(@ARGV, grep( s/\\{include|input}{([^\}]+(.tex)?)}/\1.tex/g, @lines));
+ warn "Scanning @ARGV";
+ close FILE;
+ }
+ sub emit_bodytext {
+ if ($bodytext) {
+ # heading line: spit out body in a multiline paragraph
+ select(STDOUT); $save = $~; $~ = "BODYTEXT";
+ #print $bodytext;
+ write;
+ $~ = $save;
+ }
+ }
+}
+format BODYTEXT =
+ | ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~
+ $bodytext
+ | ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~
+ $bodytext
+ | ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~
+ $bodytext
+ | ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~
+ $bodytext
+ | ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~
+ $bodytext
+ | ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~
+ $bodytext
+ | ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<...~
+ $bodytext
+.
+
+ ;
+%headings = ('part' => '',
+ 'chapter' => '',
+ 'section' => ' ',
+ 'subsection' => ' ',
+ 'subsubsection' => ' ',
+ 'paragraph' => ' .',
+ );
+$headings = join('|', (keys(%headings)));
+if ( /^\s*\\($headings)\*?{([^\}]*)}/ ) {
+ &emit_bodytext;
+ print ($headings{$1},$2,"\n");
+ $bodytext = '';
+} elsif (! $suppress_body) {
+ # it's a body line
+ chomp;
+ s/\s+/ /; s/$/ /;
+ $bodytext .= $_ if /\S/;
+}
+
+END {
+ &emit_bodytext unless $suppress_body;
+}
108 script/pastebin.rb
@@ -0,0 +1,108 @@
+#!/usr/bin/ruby
+
+require 'rubygems'
+require 'nokogiri'
+require 'net/https'
+require 'net/http'
+
+class Pastebin
+ @@post_url = URI.parse 'http://pastebin.com/api/api_post.php'
+ @@login_url = URI.parse 'http://pastebin.com/api/api_login.php'
+
+ def secure_http(url)
+ secure_url = URI.parse(url.gsub(/http:/, 'https'))
+ conn = Net::HTTP.new(secure_url.host, secure_url.port)
+ conn.use_ssl = true
+ # don't require CA verification on our side.
+ conn.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ yield conn
+ end
+
+ def initialize(args)
+ #secure_http @@login_url do |http|
+ resp, data = Net::HTTP.post_form(@@login_url, args)
+ if resp.code_type == Net::HTTPOK
+ @userkey = data
+ @api_key = args[:api_dev_key]
+ else
+ raise RuntimeError, data
+ end
+ end
+
+ def paste(name, code)
+ format = case name
+ # pastebin knows how to do hiliting for these:
+ when /\.rb$/ then 'ruby'
+ when /\.css$/ then 'css'
+ when /\.txt$/ then 'text'
+ when /\.xml$/ then 'xml'
+ when /\.html$/ then 'html4strict'
+ when /\.sh$/ then 'bash'
+ when /\.sql$/ then 'sql'
+ # hacks: pastebin lacks built-in hiliting for these:
+ when /\.html\.haml$/ then 'ruby'
+ when /\.html\.erb$/ then 'html4strict'
+ when /\.feature$/ then 'text'
+ # everything else
+ else 'text'
+ end
+ args = {
+ :api_option => 'paste',
+ :api_dev_key => @api_key,
+ :api_user_key => @userkey,
+ :api_paste_code => code,
+ :api_paste_name => name,
+ :api_paste_expire_date => 'N', # never expire
+ :api_paste_private => '0', # public
+ :api_paste_format => format,
+ }
+ return do_cmd(args, /^https?:\/\/pastebin.com/, name )
+ end
+
+ def delete(id)
+ args = {
+ :api_dev_key => @api_key,
+ :api_user_key => @userkey,
+ #:api_paste_key => "http://pastebin.com/#{id}",
+ :api_paste_key => id,
+ :api_option => 'delete',
+ }
+ return do_cmd(args, /removed/i, id)
+ end
+
+ def list
+ args = {
+ :api_dev_key => @api_key,
+ :api_user_key => @userkey,
+ :api_results_limit => 1000, # max allowed
+ :api_option => 'list'
+ }
+ return nil unless xml = do_cmd(args, /^<paste>/, 'list')
+ Nokogiri::XML::Document.parse('<pastes>' << xml << '</pastes>').
+ xpath('//paste/paste_key').
+ map(&:content)
+ end
+
+ def paste_file(filename)
+ begin
+ postname = File.basename(filename)
+ content = IO.read(filename)
+ self.paste(postname, content)
+ rescue Exception => e
+ puts "Warning: posting file '#{filename}': #{e.message}"
+ nil
+ end
+ end
+
+ private
+
+ def do_cmd(args, success_regex, name='')
+ resp = Net::HTTP.post_form(@@post_url, args)
+ if resp.code_type == Net::HTTPOK && resp.body =~ success_regex
+ resp.body
+ else
+ puts "Warning: posting '#{name}': response #{resp.code} : #{resp.body}"
+ nil
+ end
+ end
+end
123 script/update_pastebin
@@ -0,0 +1,123 @@
+#!/usr/bin/env ruby
+API_DEV_KEY = 'replace with your Pastebin API developer key'
+API_USER_NAME = 'replace with your Pastebin username'
+API_USER_PASSWORD = 'replace with your Pastebin password'
+
+load 'script/pastebin.rb'
+require 'yaml'
+require 'time'
+
+def help
+ puts <<EOS
+#{$0} create <files> - sends each of <files> to Pastebin and writes index to pasties.yml
+#{$0} update <files> - like create, but ignores files already in pasties.yml if file hasn't changed since last time this script was run
+#{$0} diff <files> - like update, but shows which of <files> would be updated without actually doing anything
+#{$0} delete - deletes all pasties listed in pasties.yml
+#{$0} delete_all - deletes all our pasties, period - the nuclear option - don't use!
+EOS
+ exit 1
+end
+
+private
+
+def init
+ $PASTIEFILE = ENV['PASTIE_FILE_NAME'] || 'pasties.yml'
+ raise "Can't stat #{$PASTIEFILE}" unless File.writable?($PASTIEFILE)
+ $PASTIES = YAML.load_file $PASTIEFILE
+ $PB = Pastebin.new(
+ :api_dev_key => API_DEV_KEY,
+ :api_user_name => API_USER_NAME,
+ :api_user_password => API_USER_PASSWORD
+ )
+end
+
+def write_pastie_file(pasties)
+ File.open($PASTIEFILE, 'w') do |f|
+ f.write <<EOS
+# This file is automatically generated by #{$0}. Don't modify it!
+# If you've changed or added a codefile, run '#{$0} update' instead.
+EOS
+ f.write $PASTIES.to_yaml
+ end
+end
+
+def truncate_pastie_file
+ write_pastie_file(Hash.new)
+end
+
+def get_files_to_update
+ allfiles = ARGV.length
+ # a file can be skipped if its filename already appears in paste list
+ # AND its modtime is no older than the last time this script ran.
+ files = ARGV.reject do |filename|
+ $PASTIES.has_key?(filename) &&
+ File.stat(filename).mtime <= $PASTIES[filename]['updated_at']
+ end
+ return [files, allfiles]
+end
+
+public
+
+def create
+ $PASTIES = {}
+ nfiles = ARGV.length
+ ok = 0
+ ARGV.each do |filename|
+ if (resp = $PB.paste_file(filename))
+ $PASTIES[filename] = {
+ 'url' => resp.gsub(%r{^.*/}, ''),
+ 'updated_at' => Time.now
+ }
+ ok += 1
+ end
+ end
+ write_pastie_file($PASTIES)
+ puts "#{ok} out of #{nfiles} pasted, index is #{$PASTIEFILE}"
+end
+
+def update
+ files, numfiles = get_files_to_update
+ puts "Updating #{files.length} out of #{numfiles} files..."
+ ok = 0
+ files.each do |filename|
+ if (resp = $PB.paste_file(filename))
+ $PASTIES[filename] = {
+ 'url' => resp.gsub(%r{^.*/}, ''),
+ 'updated_at' => Time.now
+ }
+ ok += 1
+ end
+ end
+ write_pastie_file($PASTIES)
+ puts "#{ok} out of #{files.length} pasted (#{numfiles-files.length} already in index)"
+end
+
+def delete
+ ok = 0
+ orig_length = $PASTIES.keys.length
+ $PASTIES.each_pair do |k,v|
+ if $PB.delete(v['url'])
+ ok += 1
+ $PASTIES.delete(k)
+ end
+ end
+ write_pastie_file($PASTIES)
+ puts "#{ok} out of #{orig_length} deleted"
+end
+
+def diff
+ files,numfiles = get_files_to_update
+ puts files.join("\n")
+ puts "\n#{files.length} out of #{numfiles} files would be updated"
+end
+
+help unless ARGV.length >= 1
+init()
+cmd = ARGV.shift
+begin
+ self.send cmd
+rescue Exception => e
+ puts "Failed: #{e.message} #{e.backtrace}"
+ exit false
+end
+
8 script/update_tex_with_pasties
@@ -0,0 +1,8 @@
+# call with ruby -p -i.bak
+BEGIN {
+ require 'yaml'
+ $p = YAML.load_file(ENV['PASTIE_FILE_NAME'] || 'pasties.yml')
+ PAT = /\\codefile(figure)?\s*(?:\[\w*\])?\{([^}]+)\}/
+}
+$_.gsub!(PAT, "\\codefile#{$1}[#{$p[$2]['url']}]{#{$2}}") if $_ =~ PAT && $p.has_key?($2)
+

0 comments on commit 11008ab

Please sign in to comment.