*! version 2.1 20240516 - DIME Analytics & LSMS Team, The World Bank - dimeanalytics@worldbank.org, lsms@worldbank.org cap program drop repadolog program define repadolog qui { version 14.1 * Update the syntax. This is only a placeholder to make the command run syntax [using/], [Detail Save SAVEPath(string) QUIetly] *Get the full user input local full_user_input = "repadolog " + trim(itrim(`"`0'"')) local full_user_input : subinstr local full_user_input "\" "/" , all if (!strpos(`"`full_user_input'"',",")) { local full_user_input `"`full_user_input' ,"' } **************************************************************************** **************************************************************************** * Handle input **************************************************************************** ************************ * Find trk file to use * If no file path was used, find the trk file along the adopaths if missing("`using'") { findfile stata.trk if missing("`r(fn)'") { noi di as error "No stata.trk file was found across the adopaths" error 99 exit } else { local trkfile "`r(fn)'" local trkfolder = subinstr("`trkfile'","/stata.trk","",.) } } else { * If user included "stata.trk" in filepath, then remove to standardize if (substr("`using'",-9,9) == "stata.trk") { local using = substr("`using'",1,strlen("`using'")-10) } * Test if a file exists in that location cap confirm file "`using'/stata.trk" if _rc { noi di as error `"{pstd}No stata.trk file was found at location provided in using: {inp:`using'}{p_end}"' error 99 exit } local trkfolder "`using'" local trkfile "`trkfolder'/stata.trk" } ************************ * Handle csv output options * Test if either option for outputting csv is used if !missing("`save'`savepath'") { local csvused 1 * Test if savepath() option is used if !missing("`savepath'") { * Test if file is a csv file if (substr("`savepath'",-4,4) != ".csv") { noi di as error `"{pstd}The file in {opt savepath(`savepath') is not a CSV-file.}{p_end}"' error 99 exit } } * savepath is not used, save the report in the same location as the trk else { local savepath "`trkfolder'/repadolog.csv" } } * Niether save or savepath is used else { local csvused 0 } **************************************************************************** **************************************************************************** * Execute the command **************************************************************************** ************************ * Set up the frame * Create a frame to store all pkg info and command info tempname pkg_frame frame create `pkg_frame' /// str500(package_name distribution_date download_date command_name checksum) strL(notes commands source) byte(is_cmd) * Read the trk file tempname trk_read file open `trk_read' using "`trkfile'", read * Initiate commands local local commands "" local cmd_count = 0 ************************ * Parse the trk file * Read first line file read `trk_read' line * Write lines as-is until section while (r(eof)==0) { * Escape tricky characters local line : subinstr local line "'" `"" _char(39) ""', all local line : subinstr local line "`" `"" _char(96) ""', all * Get the source the command is installed from if (substr(`"`macval(line)'"',1,2) == "S ") { local source `=trim(substr(`"`line'"',3,.))' } * Get package name if (substr(`"`macval(line)'"',1,2) == "N ") { local pkgname `=trim(substr(`"`line'"',3,.))' } * Get download date if (substr(`"`macval(line)'"',1,2) == "D ") { local downdate `=trim(substr(`"`line'"',3,.))' } * Get commands if (lower(substr(`"`macval(line)'"',1,2)) == "f ") { noi parse_command `=trim(substr(`"`line'"',3,.))', trkfolder("`trkfolder'") if ("`r(is_ado)'" == "1") { local cmd_count = `cmd_count' + 1 local commands "`commands', `r(command_name)'" local cmd_name_`cmd_count' = "`r(command_name)'" local cmd_chck_`cmd_count' = "`r(checksum)'" local cmd_note_`cmd_count' = `"`r(notes)'"' } } * Get distribution date if (substr(`"`macval(line)'"',1,21) == "d Distribution-Date: ") { local distdate `=trim(substr(`"`line'"',22,.))' } if (substr(`"`macval(line)'"',1,2) == "e") { * Distribution date is options, explicitly set N/A when missing if missing("`distdate'") local distdate "N/A" local commands = trim(subinstr("`commands'",",","",1)) * Write package line to the data frame frame post `pkg_frame' ("`pkgname'") ("`distdate'") ("`downdate'") ("") ("") ("") ("`commands'") ("`source'") (0) * Write command line to the data frame forvalues i = 1/`cmd_count' { frame post `pkg_frame' ("`pkgname'") ("`distdate'") ("`downdate'") ("`cmd_name_`i''") ("`cmd_chck_`i''") (`"`cmd_note_`i''"') ("") ("`source'") (1) } * Resetting all locals local pkgname "" local distdate "" local downdate "" local commands "" local cmd_count = 0 local source "" } * Read next line file read `trk_read' line } **************************************************************************** **************************************************************************** * Perform output **************************************************************************** * A list of package info vars always included local pkg_vars "package_name distribution_date download_date" * Handle what to include in output based on the option detail used or not if missing("`detail'") { local roworder package_name command_name source //package name first - source break ties local colorder "`pkg_vars' commands source" local keepcondition "is_cmd == 0" } else { local roworder command_name package_name source //command name first - source break ties local colorder "command_name `pkg_vars' checksum notes source" local keepcondition "is_cmd == 1" } * Display and save output frame `pkg_frame' { * Keep only relevant rows, and sort as needed keep if `keepcondition' sort `roworder' order `colorder' * Output in result window if applicable if missing("`quietly'") noi list `colorder', abbreviate(32) * Save if applicable if `csvused' == 1 { qui export delimited `colorder' using `"`savepath'"', replace quote } } if `csvused' == 1 { noi di as text _n `"{pstd}{inp:repadolog} report written to {browse "`savepath'":`savepath'}.{p_end}"' } else { noi di as text _n `"{pstd}No report saved to disk. To save a {inp:repadolog} report in the same location as your {it:stata.trk} file, {stata `"`full_user_input' save"' :click here}. To save the report in a custom location run this command again using the {opt savepath()} option.{p_end}"' } } end cap program drop parse_command program define parse_command, rclass qui { syntax anything, trkfolder(string) local cpath "`anything'" * Test that file is ado-file if (substr("`cpath'",-4,4) == ".ado") { * Extract command name local cname = substr("`cpath'",3,strlen("`cpath'")-6) * See if the file is found or if the file path in the trk file is broken cap confirm file "`trkfolder'/`cpath'" if _rc { local cksum "N/A" local notes "N/A" } * File path works, generate results based on the file content else { * Get size and checksum of the file content checksum "`trkfolder'/`cpath'" local cksum "`r(filelen)':`r(checksum)'" ************************** * Read the content of the file and extract all notes, * i.e. lines starting with !* * Read the ado-file tempname ado_read file open `ado_read' using "`trkfolder'/`cpath'", read * Read first line of the ado-file file read `ado_read' line * Loop over all lines local notes "" while (r(eof)==0) { * Escape tricky characters local line : subinstr local line "'" `"" _char(39) ""', all local line : subinstr local line "`" `"" _char(96) ""', all * Test if line is a notes line if (substr(trim(`"`macval(line)'"'),1,2) == "*!") { local notes `"`macval(notes)', `macval(line)'"' } * Read next line of the ado-file file read `ado_read' line } * Clean up notes local notes =trim(itrim(subinstr(`"`macval(notes)'"',",","",1))) } * Return dofile locals return local is_ado 1 return local command_name "`cname'" return local checksum "`cksum'" return local notes `"`macval(notes)'"' } * Not an ado file else { return local is_ado 0 } } end