*!	Version 2.5 21Jul2022 by Mead Over
*!	Based on -lineno.ado-, 0.0.1 posted on Statalist by NJC 18 Nov 2010 and accessed 23Nov2017
*!	from "https://www.stata.com/statalist/archive/2010-11/msg00735.html"
program lineno2
	version 11.0
	syntax using/ , saving(str) [STart(int 1) DOfile replace   ///
		 pdf landscape len(passthru) txt * log] // Other options passed to -makepdf- and -maketxt-

	if "`replace'" == "" {
		confirm new file `"`saving'"'
	}

	local file `using'

	local hoistemp 0
	tempname hi ho
	file open `hi' using `"`file'"', r
	capture noisily file open `ho' using `"`saving'"', w `replace'
	if _rc~=0 {
		di as err  ///
			_n "The -saving()- file cannot be opened for writing."  ///
			_n "Write the numbered file to a temporary file instead."
		tempfile tmpfl
		local ho "`tempfl'"
		local hoistemp 1
	}

	file read `hi' line
	local i = `start'

	if "`dofile'"=="dofile" {
		if lower(substr(`"`saving'"',-4,4))=="smcl" {
			di as err "The -dofile- option is incompatible with the file extension, -smcl-."
			exit 198
		}
	}

	local lasthad3dts
	local openp0etc
	while r(eof) == 0 {
		local noline
		if lower(substr(`"`saving'"',-5,5))==".smcl" {
			if (usubstr(ustrtrim(`"`line'"'),1,9)=="di as err") {
				// Trap lines that read -di as err "'' found where ':' expected"-
				if (usubstr(ustrtrim(`"`line'"'),10,.)=="") {
					local line `"di as err ***-lineno2- cannot read the rest of this line of the source file.***"'
				}
			}
			if substr(`"`line'"',1,8) == "{txt}{p}"  {
				// Delete the characters {p} which seem to disrupt the SMCL viewer.
				local line = subinstr(`"`line'"',"{txt}{p}","{txt}",1)
			}
			if substr(`"`line'"',1,4) == "{p 0" & strpos(`"`line'"',"{p_end}") > 0  {
				local p0end = strpos(`"`macval(line)'"',"}")
				local ptxt = substr(`"`macval(line)'"',1,`p0end')
				local restofline = substr(`"`macval(line)'"',`p0end' + 1,.)
				file write `ho' "`ptxt' {txt:{it}`i'{sf}} " `"`macval(restofline)'"' _n
			}
			else if strpos(`"`line'"',"{p2colset") >0  ///
				| strpos(`"`line'"',"{p2colreset}") > 0  ///
				| ("`lasthad3dts'"=="lasthad3dts" & substr(`"`line'"',1,3) == "{c ") ///
				| ("`lasthad3dts'"=="lasthad3dts" & substr(`"`line'"',1,5) == " {res" ) ///
				| ("`lasthad3dts'"=="lasthad3dts" & strpos(`"`line'"',"{") == 0 )   {
					// Omit the line number and don't increment the line count
					file write `ho' `"`macval(line)'"' _n
					local noline noline
			}
			else if substr(`"`line'"',1,6) == "{p2col"  {
				// Omit the line number, while incrementing the line count
				file write `ho' `"`macval(line)'"' _n
			}
			else if strpos(`"`line'"',"{txt}{p 0 8}Note:") > 0 {
				// Accommodate the long line length of -hausman-'s warning message
				file write `ho' "{txt:{it}`i'{sf}} " `"`macval(line)'"' _n(2)
			}

			//  Detect and accumulate "-Other smcl code- {p 0 ... syntax
			else if strpos(`"`line'"',"{p 0") > 0 & strpos(`"`line'"',"{p_end}") == 0  {
					// Add the line number, but don't increment the line count
					// Open the accumulation of code until the {p_end} or a blank line
					// Postpone the write to file
*					local line4p0etc = `"`macval(line)' {txt:{it}`i'{sf}} "'
					local line4p0etc = `"{txt:{it}`i'{sf}} `macval(line)' "'  //  <- ver. 2.5
					local noline noline
					local openp0etc openp0etc
			}
				else if ("`openp0etc'"=="openp0etc" & strpos(`"`line'"',"{p_end}") == 0 & strtrim(`"`line'"')~="" ) {  //  <- ver. 2.5
						// Accumulate code until the {p_end} or a blank line
						// Omit the line number and don't increment the line count
						local line4p0etc = `"`macval(line4p0etc)'"' + " " + `"`macval(line)'"'
						local noline noline
						local openp0etc openp0etc
				}
				else if ("`openp0etc'"=="openp0etc" & strpos(`"`line'"',"{p_end}") > 0  )  {
						// When the line includes the code -{p_end}-,
						// close the accumulation of {p 0 ... code and write to file
						// Increment the line count
						file write `ho' `"`macval(line4p0etc)' `macval(line)' "' _n
*						file write `ho' `"{smcl}"' _n
						local noline
						local openp0etc
						local line4p0etc
				}
				else if ("`openp0etc'"=="openp0etc" & strtrim( `"`line'"' ) ==""  )  {
						// When the line is empty,
						// Close the accumulation of {p 0 ... code and write to file
						// Increment the line count
						file write `ho' `"`macval(line4p0etc)' {p_end}"' _n(2) //  <- ver. 2.5
*						file write `ho' `"{smcl}"' _n
						local noline
						local openp0etc
						local line4p0etc
				}

			else {
				file write `ho' "{txt:{it}`i'{sf}} " `"`macval(line)'"' _n
			}
			if "`noline'"~="noline" {
				local ++i
			}
			if strpos(`"`line'"',"{...}") >0 {  //  Detect {...} syntax
					local lasthad3dts lasthad3dts
			}
			else {
					local lasthad3dts
			}
		}
		else if "`dofile'"=="dofile"  {
			file write `ho' "/*`i'*/ " `"`macval(line)'"' _n
			local ++i
		}
		else {
			file write `ho' "`i' " `"`macval(line)'"' _n
			local ++i
		}
		file read `hi' line
	}

	file close `ho'

	if `hoistemp' {
		copy `tempfl' `"`saving'"', `replace'
	}
	di `"{stata `"view `"`saving'"'"':view numbered file}"'

	//	Add a PDF option
	if "`pdf'" ~= "" {
		makepdf , saving(`saving') `replace' `landscape' `len' `options'
	}
	else {
		local pdfopts fontsize color tmargin bmargin lmargin rmargin
		foreach opt in `pdfopts'  {
			if "``opt''"~="" {
				di as err "Without the option -pdf-, the option `opt' is irrelevant"
			}
		}
	}

	//	Add a TXT option
	if "`txt'" ~= "" | "`log'" ~= "" {
		if "`logo'"~= "" local logo logo("`logo'")
		if "`headertext'"~=""  local headertext headertext("`headertext'")
		maketxt , saving(`saving') `replace' `landscape' `len' `log' `logo' `headertext'

	}

end  /* End of program -lineno2-  */

prog def makepdf, rclass

	syntax, saving(str) [ replace landscape len(real 79)  ///
		Color fontsize(real 7) lmargin(real 0.5) rmargin(real 0.5)  ///
		tmargin(real 0.5) bmargin(real 0.5)  ///
		logo(string) headertext(string asis) ]

		_getfilename `"`saving'"'  //  Gets filename without the path
		local filename `"`r(filename)'"'
		_shortenpath `"`saving'"', len(`len')    //  Gets filename with the path
		local pfilename `"`r(pfilename)'"'
		if strrpos("`filename'",".") >0 {
			local pdfname = substr(`"`pfilename'"',1,strrpos(`"`pfilename'"',".")-1)  // Includes path, excludes extension
			local ext = substr(`"`filename'"',strrpos(`"`filename'"',".")+1,.)
		}
		else {
			local pdfname = `"`r(pfilename)'"'   // Includes path, there is no extension
		}
		if ("`ext'"~="smcl" & "`ext'"~="Results" & "`ext'"~="Viewer" & "`ext'"~="txt" & "`ext'"~="log")  {
			local ext = "txt"
		}
		if "`color'"~= "" {
			if "`ext'"~="smcl" & "`ext'"~="Results" & "`ext'"~="Viewer" {
				di as err "The option -color- requires that the translated file have one of the following file extensions: smcl, Results or Viewer."
				exit 198
			}
			qui translator query `ext'2pdf
			local tscheme `r(scheme)'
			translator set `ext'2pdf scheme color
		}
		if "`landscape'"~= "" {
			qui translator query `ext'2pdf
			local tpagesize   `r(pagesize)'
			local tpagewidth  `r(pagewidth)'
			local tpageheight `r(pageheight)'
			translator set `ext'2pdf pagesize   custom
			translator set `ext'2pdf pagewidth  11.0
			translator set `ext'2pdf pageheight 8.5
		}
		foreach pdfopt in headertext logo {
			if `"``pdfopt''"'~=`""' {
				qui translator query `ext'2pdf
				local t`pdfopt'   `r(`pdfopt')'
				translator set `ext'2pdf `pdfopt' ``pdfopt''
			}
		}

		// Execute Stata's translate command to convert the newly created
		// file with line numbers to a PDF file.
		translate `"`saving'"' `"`pdfname'.pdf"', translator(`ext'2pdf) ///
			fontsize(`fontsize') lmargin(`lmargin') rmargin(`rmargin')  ///
			tmargin(`tmargin') bmargin(`bmargin') `replace'

		// Now return translator to user's previous settings
		if "`color'"~= "" {
			translator set `ext'2pdf scheme `tscheme'
		}
		if "`landscape'"~= "" {
			// Reset the width & height parameters before setting the pagesize parameter
			translator set `ext'2pdf pagewidth  `tpagewidth'
			translator set `ext'2pdf pageheight `tpageheight'
			translator set `ext'2pdf pagesize   `tpagesize'
		}
		if `"`headertext'"'~=`""' {
			translator set `ext'2pdf headertext "`theadertext'"
		}
		if `"`logo'"'~=`""' {
			translator set `ext'2pdf logo `tlogo'
		}

end  /* End of program -makepdf-  */

prog def maketxt, rclass

	syntax, saving(str) [ replace landscape len(real 79)  ///
		logo(string) headertext(string asis) log ]

		_getfilename `"`saving'"'  //  Gets filename without the path
		local filename `"`r(filename)'"'
		_shortenpath `"`saving'"', len(`len')    //  Gets filename with the path
		local pfilename `"`r(pfilename)'"'
		if strrpos("`filename'",".") >0 {
			local txtname = substr(`"`pfilename'"',1,strrpos(`"`pfilename'"',".")-1)  // Includes path, excludes extension
			local ext = substr(`"`filename'"',strrpos(`"`filename'"',".")+1,.)
		}
		else {
			local txtname = `"`r(pfilename)'"'   // Includes path, there is no extension
		}
		if ("`ext'"~="smcl" & "`ext'"~="Results" & "`ext'"~="Viewer" & "`ext'"~="txt" & "`ext'"~="log")  {
			local ext = "txt"
		}

		//  Only differences are: lmargin, logo and header
		local txtorlog = cond("`log'"~="","log","txt")

		if "`landscape'"~= "" {
			qui translator query `ext'2`txtorlog'
			local tlinesize  `r(linesize)'
			local tpagelines `r(pagelines)'
			translator set `ext'2`txtorlog' linesize  139
			translator set `ext'2`txtorlog' pagelines 40
		}
		foreach txtopt in headertext logo {
			if `"``txtopt''"'~=`""' {
				qui translator query `ext'2`txtorlog'
				local t`txtopt'   `r(`txtopt')'
				translator set `ext'2`txtorlog' `txtopt' ``txtopt''
			}
		}

		// Execute Stata's translate command to convert the newly created
		// file with line numbers to a txt file.
		di as inp `"translate `"`saving'"' `"`txtname'.txt"', translator(`ext'2`txtorlog') `replace'"'
		translate `"`saving'"' `"`txtname'.txt"', translator(`ext'2`txtorlog') `replace'

		// Now return translator to user's previous settings
		if "`color'"~= "" {
			translator set `ext'2`txtorlog' scheme `tscheme'
		}
		if "`landscape'"~= "" {
			translator set `ext'2`txtorlog' linesize  79
			translator set `ext'2`txtorlog' pagelines 60
		}
		if `"`headertext'"'~=`""' {
			translator set `ext'2`txtorlog' headertext "`theadertext'"
		}
		if `"`logo'"'~=`""' {
			translator set `ext'2`txtorlog' logo `tlogo'
		}

end  /* End of program -maketxt-  */

*	ver 1.0 25Nov2017 Mead Over added smcl formating
*	ver 1.1 14Dec2017 Trap failure of "file open, w"
*	ver 1.2 29Dec2017 Accomodate SMCL's -{p2col...- syntax & -hausman-'s long message
*				Change syntax to use the -using/- option.
*	ver 1.3 5Jan2017 Accomodate SMCL's -{...}- syntax
*   ver 1.4 30Jan2019 Accommodate "estimates table" output by detecting {txt}{p}
*		with which -est table- precedes the display of its -title()- option
*		and replacing it with {txt}.  Perhaps the {p} characters are a bug?
*   ver 1.5 6mar2019 Add a PDF option
*   ver 1.6 12nov2019 In PDF option, use _getfilename. Add option -color-
*   ver 1.7 15nov2019 In PDF option, add options -tmargin()-, -bmargin()-,
*		logo(), headertext() and landscape
*   ver 1.8 25Sep2020 Add option -len-
*   ver 1.9 30Sep2020 Move -pdf- production and options to a separate subroutine
*   ver 2.0 11Feb2021 Add TXT and LOG options
*   ver 2.1 5Jul2021  Display a clickable link to open the line numbered file in the viewer.
*   ver 2.2 27Jan2022 Accommodate {p 0 ... starting after column 1 without {p_end} on same line: lines 71-93
*   ver 2.3 5Jul2021  Capture the line -di as err "'' found where ':' expected"- appearing
*		in the output from -trace- and omit it from the numbered SMCL file.
*   ver 2.4 26Apr2022 Include edits from both 2.2 and 2.3
*   ver 2.5 21Jul2022

*	TODO:
*		Perhaps, if the option -pdf- is selected, the -view- option
*		would also launch Adobe Reader e.g.:  !start AcroRd32.exe test_diffplot_wlns.pdf
*		5Mar2021: Known issue: SMCL files containing `xrlow++' produce multiple error messages
*           but do not halt execution
*		16Aug2022: Known issue: There is no error trap for the user who misspecifies
*			the option -saving()- as -saving("path_and_filename", replace)
*			The saved file is named -"path_and_filename", replace)"-.
*			Should add a subroutine to parse the option -saving()-.