diff --git a/all.sas b/all.sas
index a77f33a..7489f52 100644
--- a/all.sas
+++ b/all.sas
@@ -1844,6 +1844,441 @@ Usage:
%mend mf_loc;
/**
+ @file
+ @brief Returns a mime type from a file extension
+ @details Provide a file extension and get a mime type.
+ The mappings were derived from this source:
+ https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
+
+
+
+ @param [in] ext The file extension (unquoted)
+ @return output The mime type
+
+ @version 9.2
+ @author Allan Bowe
+**/
+
+%macro mf_mimetype(
+ ext
+)/*/STORE SOURCE*/ /minoperator mindelimiter=' ';
+
+%let ext=%lowcase(&ext);
+
+%if &ext in (sas txt text conf def list log)
+%then %do;%str(text/plain)%end;
+%else %if &ext=xlsx %then %do;
+%str(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet)%end;
+%else %if &ext in (xls xlm xla xlc xlt xlw)
+%then %do;%str(application/vnd.ms-excel)%end;
+%else %if &ext=xlsm
+%then %do;%str(application/vnd.ms-excel.sheet.macroenabled.12)%end;
+%else %if &ext=xlsb
+%then %do;%str(application/vnd.ms-excel.sheet.binary.macroenabled.12)%end;
+%else %if &ext in (css csv html n3 sgml vcard)
+%then %do;%str(text/&ext)%end;
+%else %if &ext in (avif bmp cgm gif ief jxl ktx png sgi tiff webp)
+%then %do;%str(image/&ext)%end;
+%else %if &ext in (exi gxf ipfix json mbox mp21 mxf oda oxps pdf rtf sdp wasm
+ xml yang zip)
+%then %do;%str(application/&ext)%end;
+%else %if &ext in (jpeg jpg jpe) %then %do;%str(image/jpeg)%end;
+%else %if &ext in (mp4 mp4v mpg4) %then %do;%str(video/mp4)%end;
+%else %if &ext in (otf ttf woff woff2) %then %do;%str(font/&ext)%end;
+%else %if &ext in (mpeg mpg mpe m1v m2v)
+%then %do;%str(video/mpeg)%end;
+%else %if &ext in (h261 h263 h264 jpm mj2 webm)
+%then %do;%str(video/&ext)%end;
+%else %if &ext in (f4v fli flv m4v mng smv)
+%then %do;%str(video/x-&ext)%end;
+%else %if &ext in (3ds cmx pcx rgb tga)
+%then %do;%str(image/x-&ext)%end;
+%else %if &ext in (asm nfo opml sfv)
+%then %do;%str(text/x-&ext)%end;
+%else %if &ext in (aac caf flac wav)
+%then %do;%str(audio/x-&ext)%end;
+%else %if &ext in (ts m2t m2ts mts)
+%then %do;%str(video/mp2t)%end;
+%else %if &ext in (pfa pfb pfm afm)
+%then %do;%str(application/x-font-type1)%end;
+%else %if &ext in (oga ogg spx opus)
+%then %do;%str(audio/ogg)%end;
+%else %if &ext in (mid midi kar rmi)
+%then %do;%str(audio/midi)%end;
+%else %if &ext in (onetoc onetoc2 onetmp onepkg)
+%then %do;%str(application/onenote)%end;
+%else %if &ext in (mxml xhvml xvml xvm)
+%then %do;%str(application/xv+xml)%end;
+%else %if &ext in (f for f77 f90)
+%then %do;%str(text/x-fortran)%end;
+%else %if &ext in (wmf wmz emf emz)
+%then %do;%str(application/x-msmetafile)%end;
+%else %if &ext in (exe dll com bat msi)
+%then %do;%str(application/x-msdownload)%end;
+%else %if &ext in (bin dms lrf mar so dist distz pkg bpk dump elc deploy)
+%then %do;%str(application/octet-stream)%end;
+%else %if &ext in (atom atomcat atomsvc ccxml davmount emma gml gpx inkml mads
+ mathml metalink mets mods omdoc pls rdf rsd rss sbml shf smil sru ssdl ssml
+ tei wsdl wspolicy xaml xenc xhtml xop xslt xspf yin)
+%then %do;%str(application/&ext+xml)%end;
+%else %if &ext in (dir dcr dxr cst cct cxt w3d fgd swa)
+%then %do;%str(application/x-director)%end;
+%else %if &ext in (z1 z2 z3 z4 z5 z6 z7 z8)
+%then %do;%str(application/x-zmachine)%end;
+%else %if &ext in (c cc cxx cpp h hh dic)
+%then %do;%str(text/x-c)%end;
+%else %if &ext in (mpga mp2 mp2a mp3 m2a m3a)
+%then %do;%str(audio/mpeg)%end;
+%else %if &ext in (t tr roff man me ms)
+%then %do;%str(text/troff)%end;
+%else %if &ext in (cbr cba cbt cbz cb7)
+%then %do;%str(application/x-cbr)%end;
+%else %if &ext in (fh fhc fh4 fh5 fh7)
+%then %do;%str(image/x-freehand)%end;
+%else %if &ext in (aab x32 u32 vox)
+%then %do;%str(application/x-authorware-bin)%end;
+%else %if &ext in (uvi uvvi uvg uvvg)
+%then %do;%str(image/vnd.dece.graphic)%end;
+%else %if &ext in (cdx cif cmdf cml csml xyz)
+%then %do;%str(chemical/x-&ext)%end;
+%else %if &ext in (aif aiff aifc) %then %do;%str(audio/x-aiff)%end;
+%else %if &ext in (ma nb mb) %then %do;%str(application/mathematica)%end;
+%else %if &ext in (mvb m13 m14) %then %do;%str(application/x-msmediaview)%end;
+%else %if &ext in (msh mesh silo) %then %do;%str(model/mesh)%end;
+%else %if &ext in (uri uris urls) %then %do;%str(text/uri-list)%end;
+%else %if &ext in (mkv mk3d mks) %then %do;%str(video/x-matroska)%end;
+%else %if &ext=ez %then %do;%str(application/andrew-inset)%end;
+%else %if &ext=aw %then %do;%str(application/applixware)%end;
+%else %if &ext=cdmia %then %do;%str(application/cdmi-capability)%end;
+%else %if &ext=cdmic %then %do;%str(application/cdmi-container)%end;
+%else %if &ext=cdmid %then %do;%str(application/cdmi-domain)%end;
+%else %if &ext=cdmio %then %do;%str(application/cdmi-object)%end;
+%else %if &ext=cdmiq %then %do;%str(application/cdmi-queue)%end;
+%else %if &ext=cu %then %do;%str(application/cu-seeme)%end;
+%else %if &ext=dssc %then %do;%str(application/dssc+der)%end;
+%else %if &ext=xdssc %then %do;%str(application/dssc+xml)%end;
+%else %if &ext=ecma %then %do;%str(application/ecmascript)%end;
+%else %if &ext=epub %then %do;%str(application/epub+zip)%end;
+%else %if &ext=pfr %then %do;%str(application/font-tdpfr)%end;
+%else %if &ext=stk %then %do;%str(application/hyperstudio)%end;
+%else %if &ext=ink %then %do;%str(application/inkml+xml)%end;
+%else %if &ext=jar %then %do;%str(application/java-archive)%end;
+%else %if &ext=ser %then %do;%str(application/java-serialized-object)%end;
+%else %if &ext=class %then %do;%str(application/java-vm)%end;
+%else %if &ext=jsonml %then %do;%str(application/jsonml+json)%end;
+%else %if &ext=lostxml %then %do;%str(application/lost+xml)%end;
+%else %if &ext=hqx %then %do;%str(application/mac-binhex40)%end;
+%else %if &ext=cpt %then %do;%str(application/mac-compactpro)%end;
+%else %if &ext=mrc %then %do;%str(application/marc)%end;
+%else %if &ext=mrcx %then %do;%str(application/marcxml+xml)%end;
+%else %if &ext=mscml %then %do;%str(application/mediaservercontrol+xml)%end;
+%else %if &ext=meta4 %then %do;%str(application/metalink4+xml)%end;
+%else %if &ext=m21 %then %do;%str(application/mp21)%end;
+%else %if &ext=mp4s %then %do;%str(application/mp4)%end;
+%else %if &ext=doc %then %do;%str(application/msword)%end;
+%else %if &ext=dot %then %do;%str(application/msword)%end;
+%else %if &ext=opf %then %do;%str(application/oebps-package+xml)%end;
+%else %if &ext=ogx %then %do;%str(application/ogg)%end;
+%else %if &ext=xer %then %do;%str(application/patch-ops-error+xml)%end;
+%else %if &ext=pgp %then %do;%str(application/pgp-encrypted)%end;
+%else %if &ext=asc %then %do;%str(application/pgp-signature)%end;
+%else %if &ext=sig %then %do;%str(application/pgp-signature)%end;
+%else %if &ext=prf %then %do;%str(application/pics-rules)%end;
+%else %if &ext=p10 %then %do;%str(application/pkcs10)%end;
+%else %if &ext=p7m %then %do;%str(application/pkcs7-mime)%end;
+%else %if &ext=p7c %then %do;%str(application/pkcs7-mime)%end;
+%else %if &ext=p7s %then %do;%str(application/pkcs7-signature)%end;
+%else %if &ext=p8 %then %do;%str(application/pkcs8)%end;
+%else %if &ext=ac %then %do;%str(application/pkix-attr-cert)%end;
+%else %if &ext=cer %then %do;%str(application/pkix-cert)%end;
+%else %if &ext=crl %then %do;%str(application/pkix-crl)%end;
+%else %if &ext=pkipath %then %do;%str(application/pkix-pkipath)%end;
+%else %if &ext=pki %then %do;%str(application/pkixcmp)%end;
+%else %if &ext=cww %then %do;%str(application/prs.cww)%end;
+%else %if &ext=pskcxml %then %do;%str(application/pskc+xml)%end;
+%else %if &ext=rif %then %do;%str(application/reginfo+xml)%end;
+%else %if &ext=rnc %then %do;%str(application/relax-ng-compact-syntax)%end;
+%else %if &ext=rld %then %do;%str(application/resource-lists-diff+xml)%end;
+%else %if &ext=rl %then %do;%str(application/resource-lists+xml)%end;
+%else %if &ext=gbr %then %do;%str(application/rpki-ghostbusters)%end;
+%else %if &ext=mft %then %do;%str(application/rpki-manifest)%end;
+%else %if &ext=roa %then %do;%str(application/rpki-roa)%end;
+%else %if &ext=scq %then %do;%str(application/scvp-cv-request)%end;
+%else %if &ext=scs %then %do;%str(application/scvp-cv-response)%end;
+%else %if &ext=spq %then %do;%str(application/scvp-vp-request)%end;
+%else %if &ext=spp %then %do;%str(application/scvp-vp-response)%end;
+%else %if &ext=setpay %then %do;%str(application/set-payment-initiation)%end;
+%else %if &ext=setreg %then %do;%str(application/set-registration-initiation)%end;
+%else %if &ext=smi %then %do;%str(application/smil+xml)%end;
+%else %if &ext=rq %then %do;%str(application/sparql-query)%end;
+%else %if &ext=srx %then %do;%str(application/sparql-results+xml)%end;
+%else %if &ext=gram %then %do;%str(application/srgs)%end;
+%else %if &ext=grxml %then %do;%str(application/srgs+xml)%end;
+%else %if &ext=teicorpus %then %do;%str(application/tei+xml)%end;
+%else %if &ext=tfi %then %do;%str(application/thraud+xml)%end;
+%else %if &ext=tsd %then %do;%str(application/timestamped-data)%end;
+%else %if &ext=vxml %then %do;%str(application/voicexml+xml)%end;
+%else %if &ext=wgt %then %do;%str(application/widget)%end;
+%else %if &ext=hlp %then %do;%str(application/winhlp)%end;
+%else %if &ext=7z %then %do;%str(application/x-7z-compressed)%end;
+%else %if &ext=abw %then %do;%str(application/x-abiword)%end;
+%else %if &ext=ace %then %do;%str(application/x-ace-compressed)%end;
+%else %if &ext=dmg %then %do;%str(application/x-apple-diskimage)%end;
+%else %if &ext=aam %then %do;%str(application/x-authorware-map)%end;
+%else %if &ext=aas %then %do;%str(application/x-authorware-seg)%end;
+%else %if &ext=bcpio %then %do;%str(application/x-bcpio)%end;
+%else %if &ext=torrent %then %do;%str(application/x-bittorrent)%end;
+%else %if &ext=blb %then %do;%str(application/x-blorb)%end;
+%else %if &ext=blorb %then %do;%str(application/x-blorb)%end;
+%else %if &ext=bz %then %do;%str(application/x-bzip)%end;
+%else %if &ext=bz2 %then %do;%str(application/x-bzip2)%end;
+%else %if &ext=boz %then %do;%str(application/x-bzip2)%end;
+%else %if &ext=vcd %then %do;%str(application/x-cdlink)%end;
+%else %if &ext=cfs %then %do;%str(application/x-cfs-compressed)%end;
+%else %if &ext=chat %then %do;%str(application/x-chat)%end;
+%else %if &ext=pgn %then %do;%str(application/x-chess-pgn)%end;
+%else %if &ext=nsc %then %do;%str(application/x-conference)%end;
+%else %if &ext=cpio %then %do;%str(application/x-cpio)%end;
+%else %if &ext=csh %then %do;%str(application/x-csh)%end;
+%else %if &ext=deb %then %do;%str(application/x-debian-package)%end;
+%else %if &ext=udeb %then %do;%str(application/x-debian-package)%end;
+%else %if &ext=dgc %then %do;%str(application/x-dgc-compressed)%end;
+%else %if &ext=wad %then %do;%str(application/x-doom)%end;
+%else %if &ext=ncx %then %do;%str(application/x-dtbncx+xml)%end;
+%else %if &ext=dtb %then %do;%str(application/x-dtbook+xml)%end;
+%else %if &ext=res %then %do;%str(application/x-dtbresource+xml)%end;
+%else %if &ext=dvi %then %do;%str(application/x-dvi)%end;
+%else %if &ext=evy %then %do;%str(application/x-envoy)%end;
+%else %if &ext=eva %then %do;%str(application/x-eva)%end;
+%else %if &ext=bdf %then %do;%str(application/x-font-bdf)%end;
+%else %if &ext=gsf %then %do;%str(application/x-font-ghostscript)%end;
+%else %if &ext=psf %then %do;%str(application/x-font-linux-psf)%end;
+%else %if &ext=pcf %then %do;%str(application/x-font-pcf)%end;
+%else %if &ext=snf %then %do;%str(application/x-font-snf)%end;
+%else %if &ext=arc %then %do;%str(application/x-freearc)%end;
+%else %if &ext=spl %then %do;%str(application/x-futuresplash)%end;
+%else %if &ext=gca %then %do;%str(application/x-gca-compressed)%end;
+%else %if &ext=ulx %then %do;%str(application/x-glulx)%end;
+%else %if &ext=gnumeric %then %do;%str(application/x-gnumeric)%end;
+%else %if &ext=gramps %then %do;%str(application/x-gramps-xml)%end;
+%else %if &ext=gtar %then %do;%str(application/x-gtar)%end;
+%else %if &ext=hdf %then %do;%str(application/x-hdf)%end;
+%else %if &ext=install %then %do;%str(application/x-install-instructions)%end;
+%else %if &ext=iso %then %do;%str(application/x-iso9660-image)%end;
+%else %if &ext=jnlp %then %do;%str(application/x-java-jnlp-file)%end;
+%else %if &ext=latex %then %do;%str(application/x-latex)%end;
+%else %if &ext=lzh %then %do;%str(application/x-lzh-compressed)%end;
+%else %if &ext=lha %then %do;%str(application/x-lzh-compressed)%end;
+%else %if &ext=mie %then %do;%str(application/x-mie)%end;
+%else %if &ext=prc %then %do;%str(application/x-mobipocket-ebook)%end;
+%else %if &ext=mobi %then %do;%str(application/x-mobipocket-ebook)%end;
+%else %if &ext=application %then %do;%str(application/x-ms-application)%end;
+%else %if &ext=lnk %then %do;%str(application/x-ms-shortcut)%end;
+%else %if &ext=wmd %then %do;%str(application/x-ms-wmd)%end;
+%else %if &ext=wmz %then %do;%str(application/x-ms-wmz)%end;
+%else %if &ext=xbap %then %do;%str(application/x-ms-xbap)%end;
+%else %if &ext=mdb %then %do;%str(application/x-msaccess)%end;
+%else %if &ext=obd %then %do;%str(application/x-msbinder)%end;
+%else %if &ext=crd %then %do;%str(application/x-mscardfile)%end;
+%else %if &ext=clp %then %do;%str(application/x-msclip)%end;
+%else %if &ext=mny %then %do;%str(application/x-msmoney)%end;
+%else %if &ext=pub %then %do;%str(application/x-mspublisher)%end;
+%else %if &ext=scd %then %do;%str(application/x-msschedule)%end;
+%else %if &ext=trm %then %do;%str(application/x-msterminal)%end;
+%else %if &ext=wri %then %do;%str(application/x-mswrite)%end;
+%else %if &ext=nc %then %do;%str(application/x-netcdf)%end;
+%else %if &ext=cdf %then %do;%str(application/x-netcdf)%end;
+%else %if &ext=nzb %then %do;%str(application/x-nzb)%end;
+%else %if &ext=p12 %then %do;%str(application/x-pkcs12)%end;
+%else %if &ext=pfx %then %do;%str(application/x-pkcs12)%end;
+%else %if &ext=p7b %then %do;%str(application/x-pkcs7-certificates)%end;
+%else %if &ext=spc %then %do;%str(application/x-pkcs7-certificates)%end;
+%else %if &ext=p7r %then %do;%str(application/x-pkcs7-certreqresp)%end;
+%else %if &ext=rar %then %do;%str(application/x-rar-compressed)%end;
+%else %if &ext=ris %then %do;%str(application/x-research-info-systems)%end;
+%else %if &ext=sh %then %do;%str(application/x-sh)%end;
+%else %if &ext=shar %then %do;%str(application/x-shar)%end;
+%else %if &ext=swf %then %do;%str(application/x-shockwave-flash)%end;
+%else %if &ext=xap %then %do;%str(application/x-silverlight-app)%end;
+%else %if &ext=sql %then %do;%str(application/x-sql)%end;
+%else %if &ext=sit %then %do;%str(application/x-stuffit)%end;
+%else %if &ext=sitx %then %do;%str(application/x-stuffitx)%end;
+%else %if &ext=srt %then %do;%str(application/x-subrip)%end;
+%else %if &ext=sv4cpio %then %do;%str(application/x-sv4cpio)%end;
+%else %if &ext=sv4crc %then %do;%str(application/x-sv4crc)%end;
+%else %if &ext=t3 %then %do;%str(application/x-t3vm-image)%end;
+%else %if &ext=gam %then %do;%str(application/x-tads)%end;
+%else %if &ext=tar %then %do;%str(application/x-tar)%end;
+%else %if &ext=tcl %then %do;%str(application/x-tcl)%end;
+%else %if &ext=tex %then %do;%str(application/x-tex)%end;
+%else %if &ext=tfm %then %do;%str(application/x-tex-tfm)%end;
+%else %if &ext=texinfo %then %do;%str(application/x-texinfo)%end;
+%else %if &ext=texi %then %do;%str(application/x-texinfo)%end;
+%else %if &ext=obj %then %do;%str(application/x-tgif)%end;
+%else %if &ext=ustar %then %do;%str(application/x-ustar)%end;
+%else %if &ext=src %then %do;%str(application/x-wais-source)%end;
+%else %if &ext=der %then %do;%str(application/x-x509-ca-cert)%end;
+%else %if &ext=crt %then %do;%str(application/x-x509-ca-cert)%end;
+%else %if &ext=fig %then %do;%str(application/x-xfig)%end;
+%else %if &ext=xlf %then %do;%str(application/x-xliff+xml)%end;
+%else %if &ext=xpi %then %do;%str(application/x-xpinstall)%end;
+%else %if &ext=xz %then %do;%str(application/x-xz)%end;
+%else %if &ext=xdf %then %do;%str(application/xcap-diff+xml)%end;
+%else %if &ext=xht %then %do;%str(application/xhtml+xml)%end;
+%else %if &ext=xsl %then %do;%str(application/xml)%end;
+%else %if &ext=dtd %then %do;%str(application/xml-dtd)%end;
+%else %if &ext=xpl %then %do;%str(application/xproc+xml)%end;
+%else %if &ext=adp %then %do;%str(audio/adpcm)%end;
+%else %if &ext=au %then %do;%str(audio/basic)%end;
+%else %if &ext=snd %then %do;%str(audio/basic)%end;
+%else %if &ext=m4a %then %do;%str(audio/mp4)%end;
+%else %if &ext=mp4a %then %do;%str(audio/mp4)%end;
+%else %if &ext=s3m %then %do;%str(audio/s3m)%end;
+%else %if &ext=sil %then %do;%str(audio/silk)%end;
+%else %if &ext=uva %then %do;%str(audio/vnd.dece.audio)%end;
+%else %if &ext=uvva %then %do;%str(audio/vnd.dece.audio)%end;
+%else %if &ext=eol %then %do;%str(audio/vnd.digital-winds)%end;
+%else %if &ext=dra %then %do;%str(audio/vnd.dra)%end;
+%else %if &ext=dts %then %do;%str(audio/vnd.dts)%end;
+%else %if &ext=dtshd %then %do;%str(audio/vnd.dts.hd)%end;
+%else %if &ext=lvp %then %do;%str(audio/vnd.lucent.voice)%end;
+%else %if &ext=pya %then %do;%str(audio/vnd.ms-playready.media.pya)%end;
+%else %if &ext=ecelp4800 %then %do;%str(audio/vnd.nuera.ecelp4800)%end;
+%else %if &ext=ecelp7470 %then %do;%str(audio/vnd.nuera.ecelp7470)%end;
+%else %if &ext=ecelp9600 %then %do;%str(audio/vnd.nuera.ecelp9600)%end;
+%else %if &ext=rip %then %do;%str(audio/vnd.rip)%end;
+%else %if &ext=weba %then %do;%str(audio/webm)%end;
+%else %if &ext=mka %then %do;%str(audio/x-matroska)%end;
+%else %if &ext=m3u %then %do;%str(audio/x-mpegurl)%end;
+%else %if &ext=wax %then %do;%str(audio/x-ms-wax)%end;
+%else %if &ext=wma %then %do;%str(audio/x-ms-wma)%end;
+%else %if &ext=ra %then %do;%str(audio/x-pn-realaudio)%end;
+%else %if &ext=ram %then %do;%str(audio/x-pn-realaudio)%end;
+%else %if &ext=rmp %then %do;%str(audio/x-pn-realaudio-plugin)%end;
+%else %if &ext=xm %then %do;%str(audio/xm)%end;
+%else %if &ext=ttc %then %do;%str(font/collection)%end;
+%else %if &ext=g3 %then %do;%str(image/g3fax)%end;
+%else %if &ext=btif %then %do;%str(image/prs.btif)%end;
+%else %if &ext=svg %then %do;%str(image/svg+xml)%end;
+%else %if &ext=svgz %then %do;%str(image/svg+xml)%end;
+%else %if &ext=tif %then %do;%str(image/tiff)%end;
+%else %if &ext=psd %then %do;%str(image/vnd.adobe.photoshop)%end;
+%else %if &ext=djv %then %do;%str(image/vnd.djvu)%end;
+%else %if &ext=djvu %then %do;%str(image/vnd.djvu)%end;
+%else %if &ext=sub %then %do;%str(image/vnd.dvb.subtitle)%end;
+%else %if &ext=dwg %then %do;%str(image/vnd.dwg)%end;
+%else %if &ext=dxf %then %do;%str(image/vnd.dxf)%end;
+%else %if &ext=fbs %then %do;%str(image/vnd.fastbidsheet)%end;
+%else %if &ext=fpx %then %do;%str(image/vnd.fpx)%end;
+%else %if &ext=fst %then %do;%str(image/vnd.fst)%end;
+%else %if &ext=mmr %then %do;%str(image/vnd.fujixerox.edmics-mmr)%end;
+%else %if &ext=rlc %then %do;%str(image/vnd.fujixerox.edmics-rlc)%end;
+%else %if &ext=mdi %then %do;%str(image/vnd.ms-modi)%end;
+%else %if &ext=wdp %then %do;%str(image/vnd.ms-photo)%end;
+%else %if &ext=npx %then %do;%str(image/vnd.net-fpx)%end;
+%else %if &ext=wbmp %then %do;%str(image/vnd.wap.wbmp)%end;
+%else %if &ext=xif %then %do;%str(image/vnd.xiff)%end;
+%else %if &ext=ras %then %do;%str(image/x-cmu-raster)%end;
+%else %if &ext=ico %then %do;%str(image/x-icon)%end;
+%else %if &ext=sid %then %do;%str(image/x-mrsid-image)%end;
+%else %if &ext=pct %then %do;%str(image/x-pict)%end;
+%else %if &ext=pic %then %do;%str(image/x-pict)%end;
+%else %if &ext=pnm %then %do;%str(image/x-portable-anymap)%end;
+%else %if &ext=pbm %then %do;%str(image/x-portable-bitmap)%end;
+%else %if &ext=pgm %then %do;%str(image/x-portable-graymap)%end;
+%else %if &ext=ppm %then %do;%str(image/x-portable-pixmap)%end;
+%else %if &ext=xbm %then %do;%str(image/x-xbitmap)%end;
+%else %if &ext=xpm %then %do;%str(image/x-xpixmap)%end;
+%else %if &ext=xwd %then %do;%str(image/x-xwindowdump)%end;
+%else %if &ext=eml %then %do;%str(message/rfc822)%end;
+%else %if &ext=mime %then %do;%str(message/rfc822)%end;
+%else %if &ext=iges %then %do;%str(model/iges)%end;
+%else %if &ext=igs %then %do;%str(model/iges)%end;
+%else %if &ext=dae %then %do;%str(model/vnd.collada+xml)%end;
+%else %if &ext=dwf %then %do;%str(model/vnd.dwf)%end;
+%else %if &ext=gdl %then %do;%str(model/vnd.gdl)%end;
+%else %if &ext=gtw %then %do;%str(model/vnd.gtw)%end;
+%else %if &ext=vtu %then %do;%str(model/vnd.vtu)%end;
+%else %if &ext=vrml %then %do;%str(model/vrml)%end;
+%else %if &ext=wrl %then %do;%str(model/vrml)%end;
+%else %if &ext=x3db %then %do;%str(model/x3d+binary)%end;
+%else %if &ext=x3dbz %then %do;%str(model/x3d+binary)%end;
+%else %if &ext=x3dv %then %do;%str(model/x3d+vrml)%end;
+%else %if &ext=x3dvz %then %do;%str(model/x3d+vrml)%end;
+%else %if &ext=x3d %then %do;%str(model/x3d+xml)%end;
+%else %if &ext=x3dz %then %do;%str(model/x3d+xml)%end;
+%else %if &ext=appcache %then %do;%str(text/cache-manifest)%end;
+%else %if &ext=ics %then %do;%str(text/calendar)%end;
+%else %if &ext=ifb %then %do;%str(text/calendar)%end;
+%else %if &ext=htm %then %do;%str(text/html)%end;
+%else %if &ext=js %then %do;%str(text/javascript)%end;
+%else %if &ext=mjs %then %do;%str(text/javascript)%end;
+%else %if &ext=dsc %then %do;%str(text/prs.lines.tag)%end;
+%else %if &ext=rtx %then %do;%str(text/richtext)%end;
+%else %if &ext=sgm %then %do;%str(text/sgml)%end;
+%else %if &ext=tsv %then %do;%str(text/tab-separated-values)%end;
+%else %if &ext=ttl %then %do;%str(text/turtle)%end;
+%else %if &ext=curl %then %do;%str(text/vnd.curl)%end;
+%else %if &ext=dcurl %then %do;%str(text/vnd.curl.dcurl)%end;
+%else %if &ext=mcurl %then %do;%str(text/vnd.curl.mcurl)%end;
+%else %if &ext=scurl %then %do;%str(text/vnd.curl.scurl)%end;
+%else %if &ext=sub %then %do;%str(text/vnd.dvb.subtitle)%end;
+%else %if &ext=fly %then %do;%str(text/vnd.fly)%end;
+%else %if &ext=flx %then %do;%str(text/vnd.fmi.flexstor)%end;
+%else %if &ext=gv %then %do;%str(text/vnd.graphviz)%end;
+%else %if &ext=3dml %then %do;%str(text/vnd.in3d.3dml)%end;
+%else %if &ext=spot %then %do;%str(text/vnd.in3d.spot)%end;
+%else %if &ext=jad %then %do;%str(text/vnd.sun.j2me.app-descriptor)%end;
+%else %if &ext=wml %then %do;%str(text/vnd.wap.wml)%end;
+%else %if &ext=wmls %then %do;%str(text/vnd.wap.wmlscript)%end;
+%else %if &ext=s %then %do;%str(text/x-asm)%end;
+%else %if &ext=java %then %do;%str(text/x-java-source)%end;
+%else %if &ext=p %then %do;%str(text/x-pascal)%end;
+%else %if &ext=pas %then %do;%str(text/x-pascal)%end;
+%else %if &ext=etx %then %do;%str(text/x-setext)%end;
+%else %if &ext=uu %then %do;%str(text/x-uuencode)%end;
+%else %if &ext=vcs %then %do;%str(text/x-vcalendar)%end;
+%else %if &ext=vcf %then %do;%str(text/x-vcard)%end;
+%else %if &ext=3gp %then %do;%str(video/3gpp)%end;
+%else %if &ext=3g2 %then %do;%str(video/3gpp2)%end;
+%else %if &ext=jpgv %then %do;%str(video/jpeg)%end;
+%else %if &ext=jpgm %then %do;%str(video/jpm)%end;
+%else %if &ext=mjp2 %then %do;%str(video/mj2)%end;
+%else %if &ext=ogv %then %do;%str(video/ogg)%end;
+%else %if &ext=mov %then %do;%str(video/quicktime)%end;
+%else %if &ext=qt %then %do;%str(video/quicktime)%end;
+%else %if &ext=uvh %then %do;%str(video/vnd.dece.hd)%end;
+%else %if &ext=uvvh %then %do;%str(video/vnd.dece.hd)%end;
+%else %if &ext=uvm %then %do;%str(video/vnd.dece.mobile)%end;
+%else %if &ext=uvvm %then %do;%str(video/vnd.dece.mobile)%end;
+%else %if &ext=uvp %then %do;%str(video/vnd.dece.pd)%end;
+%else %if &ext=uvvp %then %do;%str(video/vnd.dece.pd)%end;
+%else %if &ext=uvs %then %do;%str(video/vnd.dece.sd)%end;
+%else %if &ext=uvvs %then %do;%str(video/vnd.dece.sd)%end;
+%else %if &ext=uvv %then %do;%str(video/vnd.dece.video)%end;
+%else %if &ext=uvvv %then %do;%str(video/vnd.dece.video)%end;
+%else %if &ext=dvb %then %do;%str(video/vnd.dvb.file)%end;
+%else %if &ext=fvt %then %do;%str(video/vnd.fvt)%end;
+%else %if &ext=m4u %then %do;%str(video/vnd.mpegurl)%end;
+%else %if &ext=mxu %then %do;%str(video/vnd.mpegurl)%end;
+%else %if &ext=pyv %then %do;%str(video/vnd.ms-playready.media.pyv)%end;
+%else %if &ext=uvu %then %do;%str(video/vnd.uvvu.mp4)%end;
+%else %if &ext=uvvu %then %do;%str(video/vnd.uvvu.mp4)%end;
+%else %if &ext=viv %then %do;%str(video/vnd.vivo)%end;
+%else %if &ext=asf %then %do;%str(video/x-ms-asf)%end;
+%else %if &ext=asx %then %do;%str(video/x-ms-asf)%end;
+%else %if &ext=vob %then %do;%str(video/x-ms-vob)%end;
+%else %if &ext=wm %then %do;%str(video/x-ms-wm)%end;
+%else %if &ext=wmv %then %do;%str(video/x-ms-wmv)%end;
+%else %if &ext=wmx %then %do;%str(video/x-ms-wmx)%end;
+%else %if &ext=wvx %then %do;%str(video/x-ms-wvx)%end;
+%else %if &ext=avi %then %do;%str(video/x-msvideo)%end;
+%else %if &ext=movie %then %do;%str(video/x-sgi-movie)%end;
+%else %if &ext=ice %then %do;%str(x-conference/x-cooltalk)%end;
+%else %if "&ext"="in" %then %do;%str(text/plain)%end;
+%else %do;%str(application/octet-stream)%end;
+
+%mend mf_mimetype;/**
@file
@brief Creates a directory, including any intermediate directories
@details Works on windows and unix environments via dcreate function.
@@ -23736,9 +24171,64 @@ run;
%mend mfv_existsashdat;
/**
@file
- @brief Creates a file in SAS Drive
- @details Creates a file in SAS Drive and adds the appropriate content type.
+ @brief Returns the uri of a file or folder
+ @details The automatic variable _FILESRVC_[fref]_URI is used after assigning
+ a fileref using the filesrvc engine.
+
+ Usage:
+
+ %put %mfv_existfile(/Public/folder/file.txt);
+ %put %mfv_existfile(/Public/folder);
+
+ @param [in] filepath The full path to the file on SAS drive
+ (eg /Public/myfile.txt)
+
+
SAS Macros
+ @li mf_abort.sas
+ @li mf_getuniquefileref.sas
+
+ Related Macros
+ @li mfv_existfile.sas
+ @li mfv_existfolder.sas
+
+ @version 3.5
+ @author [Allan Bowe](https://www.linkedin.com/in/allanbowe/)
+**/
+
+%macro mfv_getpathuri(filepath
+)/*/STORE SOURCE*/;
+
+ %mf_abort(
+ iftrue=(&syscc ne 0),
+ msg=Cannot enter &sysmacroname with syscc=&syscc
+ )
+
+ %local fref rc path name var /* var is used to avoid delete timing issue */;
+ %let fref=%mf_getuniquefileref();
+ %let name=%scan(&filepath,-1,/);
+ %let path=%substr(&filepath,1,%length(&filepath)-%length(&name)-1);
+
+ %if %sysfunc(filename(fref,,filesrvc,folderPath="&path" filename="&name"))=0
+ %then %do;
+ %let var=_FILESRVC_&fref._URI;
+ %str(&&&var)
+ %let rc=%sysfunc(filename(fref));
+ %symdel &var;
+ %end;
+ %else %do;
+ %put &sysmacroname: did not find &filepath;
+ %let syscc=0;
+ %end;
+
+%mend mfv_getpathuri;/**
+ @file
+ @brief Creates a file in SAS Drive using the API method
+ @details Creates a file in SAS Drive using the API interface.
If the parent folder does not exist, it is created.
+ The API approach is more flexible than using the filesrvc engine of the
+ filename statement, as it provides more options.
+
+ SAS docs: https://developer.sas.com/rest-apis/files/createNewFile
Usage:
@@ -23750,49 +24240,53 @@ run;
%mv_createfile(path=/Public/temp,name=newfile.txt,inref=myfile)
- @param [in] path= The parent folder in which to create the file
+ @param [in] path= The parent (SAS Drive) folder in which to create the file
@param [in] name= The name of the file to be created
@param [in] inref= The fileref pointing to the file to be uploaded
@param [in] intype= (BINARY) The type of the input data. Valid values:
@li BINARY File is copied byte for byte using the mp_binarycopy.sas macro.
@li BASE64 File will be first decoded using the mp_base64.sas macro, then
loaded byte by byte to SAS Drive.
- @param [in] contentdisp= (inline) Content Disposition. Example values:
+ @param [in] contentdisp= (attchment) Content Disposition. Example values:
@li inline
@li attachment
- @param [in] ctype= (0) Set a default HTTP Content-Type header to be returned
- with the file when the content is retrieved from the Files service.
+ @param [in] ctype= (0) The actual MIME type of the file (if blank will be
+ determined based on file extension))
@param [in] access_token_var= The global macro variable to contain the access
token, if using authorization_code grant type.
@param [in] grant_type= (sas_services) Valid values are:
@li password
@li authorization_code
@li sas_services
+ @param [out] outds= (_null_) Output dataset with the uri of the new file
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
- @version VIYA V.03.05
- @author Allan Bowe, source: https://github.com/sasjs/core
-
SAS Macros
+ @li mf_getplatform.sas
@li mf_getuniquefileref.sas
+ @li mf_getuniquename.sas
@li mf_isblank.sas
+ @li mf_mimetype.sas
@li mp_abort.sas
@li mp_base64copy.sas
- @li mp_binarycopy.sas
@li mv_createfolder.sas
+ Related Macros
+ @li mv_createfile.sas
+
**/
%macro mv_createfile(path=
,name=
,inref=
,intype=BINARY
- ,contentdisp=inline
+ ,contentdisp=attachment
,ctype=0
,access_token_var=ACCESS_TOKEN
,grant_type=sas_services
,mdebug=0
+ ,outds=_null_
);
%local dbg;
%if &mdebug=1 %then %do;
@@ -23827,38 +24321,92 @@ run;
,msg=%str(name value with length >1 must be provided)
)
+/* prep the source file */
+%local fref;
+%let fref=%mf_getuniquefileref();
+
+%if %upcase(&intype)=BINARY %then %let fref=&inref;
+%else %if %upcase(&intype)=BASE64 %then %do;
+ %mp_base64copy(inref=&inref, outref=&fref, action=DECODE)
+%end;
+%else %put %str(ERR)OR: invalid value for intype: &intype;
+
+
+%if &mdebug=1 %then %do;
+ data _null_;
+ infile &fref lrecl=32767;
+ input;
+ put _infile_;
+ run;
+%end;
+
+
/* create folder if it does not already exist */
+%local folderds parenturi;
+%let folderds=%mf_getuniquename(prefix=folderds);
%mv_createfolder(path=&path
,access_token_var=&access_token_var
,grant_type=&grant_type
,mdebug=&mdebug
+ ,outds=&folderds
)
+data _null_;
+ set &folderds;
+ call symputx('self_uri',self_uri,'l');
+run;
-/* create file with relevant options */
-%local fref;
-%let fref=%mf_getuniquefileref();
-filename &fref filesrvc
- folderPath="&path"
- filename="&name"
- cdisp="&contentdisp"
-%if "&ctype" ne "0" %then %do;
- ctype="&ctype"
-%end;
- lrecl=1048544;
-%if &intype=BINARY %then %do;
- %mp_binarycopy(inref=&inref, outref=&fref)
-%end;
-%else %if &intype=BASE64 %then %do;
- %mp_base64copy(inref=&inref, outref=&fref, action=DECODE)
-%end;
-
-filename &fref clear;
+options noquotelenmax;
%local base_uri; /* location of rest apis */
%let base_uri=%mf_getplatform(VIYARESTAPI);
-%put &sysmacroname: File &name successfully created in &path;
-%put &sysmacroname:;%put;
+%local url mimetype;
+%let url=&base_uri/files/files?parentFolderUri=&self_uri;
+
+/* fetch job info */
+%local fname1;
+%let fname1=%mf_getuniquefileref();
+proc http method='POST' out=&fname1 &oauth_bearer in=&fref
+ %if "&ctype" = "0" %then %do;
+ %let mimetype=%mf_mimetype(%scan(&name,-1,.));
+ ct="&mimetype"
+ %end;
+ %else %do;
+ ct="&ctype"
+ %end;
+ %if "&mimetype"="text/html" %then %do;
+ url="&url%str(&)typeDefName=file";
+ %end;
+ %else %do;
+ url="&url";
+ %end;
+
+ headers "Accept"="application/json"
+ %if &grant_type=authorization_code %then %do;
+ "Authorization"="Bearer &&&access_token_var"
+ %end;
+ "Content-Disposition"= "&contentdisp filename=""&name""; name=""&name"";";
+run;
+%put &=SYS_PROCHTTP_STATUS_CODE;
+%put &=SYS_PROCHTTP_STATUS_PHRASE;
+%mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 201)
+ ,mac=&sysmacroname
+ ,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
+)
+%local libref2;
+%let libref2=%mf_getuniquelibref();
+libname &libref2 JSON fileref=&fname1;
+%put Grabbing the follow on link ;
+data &outds;
+ set &libref2..links end=last;
+ if rel='createChild' then do;
+ call symputx('href',quote(cats("&base_uri",href)),'l');
+ &dbg put (_all_)(=);
+ end;
+run;
+
+%put &sysmacroname: File &name successfully created:;%put;
+%put &base_uri%mfv_getpathuri(&path/&name);%put;
%put &base_uri/SASJobExecution?_file=&path/&name;%put;
%put &sysmacroname:;
@@ -23881,15 +24429,16 @@ filename &fref clear;
@li sas_services
@param [in] mdebug=(0) set to 1 to enable DEBUG messages
+ @param [out] outds=(_null_) Optionally create an output dataset which will
+ contain the uri (self_uri) of the created (and parent) folder.
- @version VIYA V.03.04
- @author Allan Bowe, source: https://github.com/sasjs/core
SAS Macros
@li mp_abort.sas
@li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas
@li mf_isblank.sas
+ @li mfv_getpathuri.sas
@li mf_getplatform.sas
@li mfv_existfolder.sas
@@ -23900,6 +24449,7 @@ filename &fref clear;
,access_token_var=ACCESS_TOKEN
,grant_type=sas_services
,mdebug=0
+ ,outds=_null_
);
%local dbg;
%if &mdebug=1 %then %do;
@@ -23910,6 +24460,11 @@ filename &fref clear;
%if %mfv_existfolder(&path)=1 %then %do;
%put &sysmacroname: &path already exists;
+ data &outds;
+ self_uri="%mfv_getpathuri(&path)";
+ output;
+ stop;
+ run;
%return;
%end;
@@ -23944,12 +24499,12 @@ options noquotelenmax;
%local subfolder_cnt; /* determine the number of subfolders */
%let subfolder_cnt=%sysfunc(countw(&path,/));
-%local href; /* resource address (none for root) */
-%let href="/folders/folders?parentFolderUri=/folders/folders/none";
-
%local base_uri; /* location of rest apis */
%let base_uri=%mf_getplatform(VIYARESTAPI);
+%local href; /* resource address (none for root) */
+%let href="&base_uri/folders/folders?parentFolderUri=/folders/folders/none";
+
%local x newpath subfolder;
%do x=1 %to &subfolder_cnt;
%let subfolder=%scan(&path,&x,%str(/));
@@ -23979,7 +24534,7 @@ options noquotelenmax;
%put &sysmacroname following check to see if &newpath exists:;
%put _local_;
data _null_;
- set &fname1;
+ infile &fname1;
input;
putlog _infile_;
run;
@@ -24031,12 +24586,17 @@ options noquotelenmax;
%let libref2=%mf_getuniquelibref();
libname &libref2 JSON fileref=&fname2;
%put &sysmacroname &newpath now created. Grabbing the follow on link ;
- data _null_;
+ data &outds;
set &libref2..links;
if rel='createChild' then do;
call symputx('href',quote(cats("&base_uri",href)),'l');
&dbg put (_all_)(=);
end;
+ if method='GET' and rel='self' then do;
+ self_uri=uri;
+ output;
+ end;
+ keep self_uri ;
run;
libname &libref2 clear;
diff --git a/base/mf_mimetype.sas b/base/mf_mimetype.sas
new file mode 100644
index 0000000..c134428
--- /dev/null
+++ b/base/mf_mimetype.sas
@@ -0,0 +1,436 @@
+/**
+ @file
+ @brief Returns a mime type from a file extension
+ @details Provide a file extension and get a mime type.
+ The mappings were derived from this source:
+ https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
+
+
+
+ @param [in] ext The file extension (unquoted)
+ @return output The mime type
+
+ @version 9.2
+ @author Allan Bowe
+**/
+
+%macro mf_mimetype(
+ ext
+)/*/STORE SOURCE*/ /minoperator mindelimiter=' ';
+
+%let ext=%lowcase(&ext);
+
+%if &ext in (sas txt text conf def list log)
+%then %do;%str(text/plain)%end;
+%else %if &ext=xlsx %then %do;
+%str(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet)%end;
+%else %if &ext in (xls xlm xla xlc xlt xlw)
+%then %do;%str(application/vnd.ms-excel)%end;
+%else %if &ext=xlsm
+%then %do;%str(application/vnd.ms-excel.sheet.macroenabled.12)%end;
+%else %if &ext=xlsb
+%then %do;%str(application/vnd.ms-excel.sheet.binary.macroenabled.12)%end;
+%else %if &ext in (css csv html n3 sgml vcard)
+%then %do;%str(text/&ext)%end;
+%else %if &ext in (avif bmp cgm gif ief jxl ktx png sgi tiff webp)
+%then %do;%str(image/&ext)%end;
+%else %if &ext in (exi gxf ipfix json mbox mp21 mxf oda oxps pdf rtf sdp wasm
+ xml yang zip)
+%then %do;%str(application/&ext)%end;
+%else %if &ext in (jpeg jpg jpe) %then %do;%str(image/jpeg)%end;
+%else %if &ext in (mp4 mp4v mpg4) %then %do;%str(video/mp4)%end;
+%else %if &ext in (otf ttf woff woff2) %then %do;%str(font/&ext)%end;
+%else %if &ext in (mpeg mpg mpe m1v m2v)
+%then %do;%str(video/mpeg)%end;
+%else %if &ext in (h261 h263 h264 jpm mj2 webm)
+%then %do;%str(video/&ext)%end;
+%else %if &ext in (f4v fli flv m4v mng smv)
+%then %do;%str(video/x-&ext)%end;
+%else %if &ext in (3ds cmx pcx rgb tga)
+%then %do;%str(image/x-&ext)%end;
+%else %if &ext in (asm nfo opml sfv)
+%then %do;%str(text/x-&ext)%end;
+%else %if &ext in (aac caf flac wav)
+%then %do;%str(audio/x-&ext)%end;
+%else %if &ext in (ts m2t m2ts mts)
+%then %do;%str(video/mp2t)%end;
+%else %if &ext in (pfa pfb pfm afm)
+%then %do;%str(application/x-font-type1)%end;
+%else %if &ext in (oga ogg spx opus)
+%then %do;%str(audio/ogg)%end;
+%else %if &ext in (mid midi kar rmi)
+%then %do;%str(audio/midi)%end;
+%else %if &ext in (onetoc onetoc2 onetmp onepkg)
+%then %do;%str(application/onenote)%end;
+%else %if &ext in (mxml xhvml xvml xvm)
+%then %do;%str(application/xv+xml)%end;
+%else %if &ext in (f for f77 f90)
+%then %do;%str(text/x-fortran)%end;
+%else %if &ext in (wmf wmz emf emz)
+%then %do;%str(application/x-msmetafile)%end;
+%else %if &ext in (exe dll com bat msi)
+%then %do;%str(application/x-msdownload)%end;
+%else %if &ext in (bin dms lrf mar so dist distz pkg bpk dump elc deploy)
+%then %do;%str(application/octet-stream)%end;
+%else %if &ext in (atom atomcat atomsvc ccxml davmount emma gml gpx inkml mads
+ mathml metalink mets mods omdoc pls rdf rsd rss sbml shf smil sru ssdl ssml
+ tei wsdl wspolicy xaml xenc xhtml xop xslt xspf yin)
+%then %do;%str(application/&ext+xml)%end;
+%else %if &ext in (dir dcr dxr cst cct cxt w3d fgd swa)
+%then %do;%str(application/x-director)%end;
+%else %if &ext in (z1 z2 z3 z4 z5 z6 z7 z8)
+%then %do;%str(application/x-zmachine)%end;
+%else %if &ext in (c cc cxx cpp h hh dic)
+%then %do;%str(text/x-c)%end;
+%else %if &ext in (mpga mp2 mp2a mp3 m2a m3a)
+%then %do;%str(audio/mpeg)%end;
+%else %if &ext in (t tr roff man me ms)
+%then %do;%str(text/troff)%end;
+%else %if &ext in (cbr cba cbt cbz cb7)
+%then %do;%str(application/x-cbr)%end;
+%else %if &ext in (fh fhc fh4 fh5 fh7)
+%then %do;%str(image/x-freehand)%end;
+%else %if &ext in (aab x32 u32 vox)
+%then %do;%str(application/x-authorware-bin)%end;
+%else %if &ext in (uvi uvvi uvg uvvg)
+%then %do;%str(image/vnd.dece.graphic)%end;
+%else %if &ext in (cdx cif cmdf cml csml xyz)
+%then %do;%str(chemical/x-&ext)%end;
+%else %if &ext in (aif aiff aifc) %then %do;%str(audio/x-aiff)%end;
+%else %if &ext in (ma nb mb) %then %do;%str(application/mathematica)%end;
+%else %if &ext in (mvb m13 m14) %then %do;%str(application/x-msmediaview)%end;
+%else %if &ext in (msh mesh silo) %then %do;%str(model/mesh)%end;
+%else %if &ext in (uri uris urls) %then %do;%str(text/uri-list)%end;
+%else %if &ext in (mkv mk3d mks) %then %do;%str(video/x-matroska)%end;
+%else %if &ext=ez %then %do;%str(application/andrew-inset)%end;
+%else %if &ext=aw %then %do;%str(application/applixware)%end;
+%else %if &ext=cdmia %then %do;%str(application/cdmi-capability)%end;
+%else %if &ext=cdmic %then %do;%str(application/cdmi-container)%end;
+%else %if &ext=cdmid %then %do;%str(application/cdmi-domain)%end;
+%else %if &ext=cdmio %then %do;%str(application/cdmi-object)%end;
+%else %if &ext=cdmiq %then %do;%str(application/cdmi-queue)%end;
+%else %if &ext=cu %then %do;%str(application/cu-seeme)%end;
+%else %if &ext=dssc %then %do;%str(application/dssc+der)%end;
+%else %if &ext=xdssc %then %do;%str(application/dssc+xml)%end;
+%else %if &ext=ecma %then %do;%str(application/ecmascript)%end;
+%else %if &ext=epub %then %do;%str(application/epub+zip)%end;
+%else %if &ext=pfr %then %do;%str(application/font-tdpfr)%end;
+%else %if &ext=stk %then %do;%str(application/hyperstudio)%end;
+%else %if &ext=ink %then %do;%str(application/inkml+xml)%end;
+%else %if &ext=jar %then %do;%str(application/java-archive)%end;
+%else %if &ext=ser %then %do;%str(application/java-serialized-object)%end;
+%else %if &ext=class %then %do;%str(application/java-vm)%end;
+%else %if &ext=jsonml %then %do;%str(application/jsonml+json)%end;
+%else %if &ext=lostxml %then %do;%str(application/lost+xml)%end;
+%else %if &ext=hqx %then %do;%str(application/mac-binhex40)%end;
+%else %if &ext=cpt %then %do;%str(application/mac-compactpro)%end;
+%else %if &ext=mrc %then %do;%str(application/marc)%end;
+%else %if &ext=mrcx %then %do;%str(application/marcxml+xml)%end;
+%else %if &ext=mscml %then %do;%str(application/mediaservercontrol+xml)%end;
+%else %if &ext=meta4 %then %do;%str(application/metalink4+xml)%end;
+%else %if &ext=m21 %then %do;%str(application/mp21)%end;
+%else %if &ext=mp4s %then %do;%str(application/mp4)%end;
+%else %if &ext=doc %then %do;%str(application/msword)%end;
+%else %if &ext=dot %then %do;%str(application/msword)%end;
+%else %if &ext=opf %then %do;%str(application/oebps-package+xml)%end;
+%else %if &ext=ogx %then %do;%str(application/ogg)%end;
+%else %if &ext=xer %then %do;%str(application/patch-ops-error+xml)%end;
+%else %if &ext=pgp %then %do;%str(application/pgp-encrypted)%end;
+%else %if &ext=asc %then %do;%str(application/pgp-signature)%end;
+%else %if &ext=sig %then %do;%str(application/pgp-signature)%end;
+%else %if &ext=prf %then %do;%str(application/pics-rules)%end;
+%else %if &ext=p10 %then %do;%str(application/pkcs10)%end;
+%else %if &ext=p7m %then %do;%str(application/pkcs7-mime)%end;
+%else %if &ext=p7c %then %do;%str(application/pkcs7-mime)%end;
+%else %if &ext=p7s %then %do;%str(application/pkcs7-signature)%end;
+%else %if &ext=p8 %then %do;%str(application/pkcs8)%end;
+%else %if &ext=ac %then %do;%str(application/pkix-attr-cert)%end;
+%else %if &ext=cer %then %do;%str(application/pkix-cert)%end;
+%else %if &ext=crl %then %do;%str(application/pkix-crl)%end;
+%else %if &ext=pkipath %then %do;%str(application/pkix-pkipath)%end;
+%else %if &ext=pki %then %do;%str(application/pkixcmp)%end;
+%else %if &ext=cww %then %do;%str(application/prs.cww)%end;
+%else %if &ext=pskcxml %then %do;%str(application/pskc+xml)%end;
+%else %if &ext=rif %then %do;%str(application/reginfo+xml)%end;
+%else %if &ext=rnc %then %do;%str(application/relax-ng-compact-syntax)%end;
+%else %if &ext=rld %then %do;%str(application/resource-lists-diff+xml)%end;
+%else %if &ext=rl %then %do;%str(application/resource-lists+xml)%end;
+%else %if &ext=gbr %then %do;%str(application/rpki-ghostbusters)%end;
+%else %if &ext=mft %then %do;%str(application/rpki-manifest)%end;
+%else %if &ext=roa %then %do;%str(application/rpki-roa)%end;
+%else %if &ext=scq %then %do;%str(application/scvp-cv-request)%end;
+%else %if &ext=scs %then %do;%str(application/scvp-cv-response)%end;
+%else %if &ext=spq %then %do;%str(application/scvp-vp-request)%end;
+%else %if &ext=spp %then %do;%str(application/scvp-vp-response)%end;
+%else %if &ext=setpay %then %do;%str(application/set-payment-initiation)%end;
+%else %if &ext=setreg %then %do;%str(application/set-registration-initiation)%end;
+%else %if &ext=smi %then %do;%str(application/smil+xml)%end;
+%else %if &ext=rq %then %do;%str(application/sparql-query)%end;
+%else %if &ext=srx %then %do;%str(application/sparql-results+xml)%end;
+%else %if &ext=gram %then %do;%str(application/srgs)%end;
+%else %if &ext=grxml %then %do;%str(application/srgs+xml)%end;
+%else %if &ext=teicorpus %then %do;%str(application/tei+xml)%end;
+%else %if &ext=tfi %then %do;%str(application/thraud+xml)%end;
+%else %if &ext=tsd %then %do;%str(application/timestamped-data)%end;
+%else %if &ext=vxml %then %do;%str(application/voicexml+xml)%end;
+%else %if &ext=wgt %then %do;%str(application/widget)%end;
+%else %if &ext=hlp %then %do;%str(application/winhlp)%end;
+%else %if &ext=7z %then %do;%str(application/x-7z-compressed)%end;
+%else %if &ext=abw %then %do;%str(application/x-abiword)%end;
+%else %if &ext=ace %then %do;%str(application/x-ace-compressed)%end;
+%else %if &ext=dmg %then %do;%str(application/x-apple-diskimage)%end;
+%else %if &ext=aam %then %do;%str(application/x-authorware-map)%end;
+%else %if &ext=aas %then %do;%str(application/x-authorware-seg)%end;
+%else %if &ext=bcpio %then %do;%str(application/x-bcpio)%end;
+%else %if &ext=torrent %then %do;%str(application/x-bittorrent)%end;
+%else %if &ext=blb %then %do;%str(application/x-blorb)%end;
+%else %if &ext=blorb %then %do;%str(application/x-blorb)%end;
+%else %if &ext=bz %then %do;%str(application/x-bzip)%end;
+%else %if &ext=bz2 %then %do;%str(application/x-bzip2)%end;
+%else %if &ext=boz %then %do;%str(application/x-bzip2)%end;
+%else %if &ext=vcd %then %do;%str(application/x-cdlink)%end;
+%else %if &ext=cfs %then %do;%str(application/x-cfs-compressed)%end;
+%else %if &ext=chat %then %do;%str(application/x-chat)%end;
+%else %if &ext=pgn %then %do;%str(application/x-chess-pgn)%end;
+%else %if &ext=nsc %then %do;%str(application/x-conference)%end;
+%else %if &ext=cpio %then %do;%str(application/x-cpio)%end;
+%else %if &ext=csh %then %do;%str(application/x-csh)%end;
+%else %if &ext=deb %then %do;%str(application/x-debian-package)%end;
+%else %if &ext=udeb %then %do;%str(application/x-debian-package)%end;
+%else %if &ext=dgc %then %do;%str(application/x-dgc-compressed)%end;
+%else %if &ext=wad %then %do;%str(application/x-doom)%end;
+%else %if &ext=ncx %then %do;%str(application/x-dtbncx+xml)%end;
+%else %if &ext=dtb %then %do;%str(application/x-dtbook+xml)%end;
+%else %if &ext=res %then %do;%str(application/x-dtbresource+xml)%end;
+%else %if &ext=dvi %then %do;%str(application/x-dvi)%end;
+%else %if &ext=evy %then %do;%str(application/x-envoy)%end;
+%else %if &ext=eva %then %do;%str(application/x-eva)%end;
+%else %if &ext=bdf %then %do;%str(application/x-font-bdf)%end;
+%else %if &ext=gsf %then %do;%str(application/x-font-ghostscript)%end;
+%else %if &ext=psf %then %do;%str(application/x-font-linux-psf)%end;
+%else %if &ext=pcf %then %do;%str(application/x-font-pcf)%end;
+%else %if &ext=snf %then %do;%str(application/x-font-snf)%end;
+%else %if &ext=arc %then %do;%str(application/x-freearc)%end;
+%else %if &ext=spl %then %do;%str(application/x-futuresplash)%end;
+%else %if &ext=gca %then %do;%str(application/x-gca-compressed)%end;
+%else %if &ext=ulx %then %do;%str(application/x-glulx)%end;
+%else %if &ext=gnumeric %then %do;%str(application/x-gnumeric)%end;
+%else %if &ext=gramps %then %do;%str(application/x-gramps-xml)%end;
+%else %if &ext=gtar %then %do;%str(application/x-gtar)%end;
+%else %if &ext=hdf %then %do;%str(application/x-hdf)%end;
+%else %if &ext=install %then %do;%str(application/x-install-instructions)%end;
+%else %if &ext=iso %then %do;%str(application/x-iso9660-image)%end;
+%else %if &ext=jnlp %then %do;%str(application/x-java-jnlp-file)%end;
+%else %if &ext=latex %then %do;%str(application/x-latex)%end;
+%else %if &ext=lzh %then %do;%str(application/x-lzh-compressed)%end;
+%else %if &ext=lha %then %do;%str(application/x-lzh-compressed)%end;
+%else %if &ext=mie %then %do;%str(application/x-mie)%end;
+%else %if &ext=prc %then %do;%str(application/x-mobipocket-ebook)%end;
+%else %if &ext=mobi %then %do;%str(application/x-mobipocket-ebook)%end;
+%else %if &ext=application %then %do;%str(application/x-ms-application)%end;
+%else %if &ext=lnk %then %do;%str(application/x-ms-shortcut)%end;
+%else %if &ext=wmd %then %do;%str(application/x-ms-wmd)%end;
+%else %if &ext=wmz %then %do;%str(application/x-ms-wmz)%end;
+%else %if &ext=xbap %then %do;%str(application/x-ms-xbap)%end;
+%else %if &ext=mdb %then %do;%str(application/x-msaccess)%end;
+%else %if &ext=obd %then %do;%str(application/x-msbinder)%end;
+%else %if &ext=crd %then %do;%str(application/x-mscardfile)%end;
+%else %if &ext=clp %then %do;%str(application/x-msclip)%end;
+%else %if &ext=mny %then %do;%str(application/x-msmoney)%end;
+%else %if &ext=pub %then %do;%str(application/x-mspublisher)%end;
+%else %if &ext=scd %then %do;%str(application/x-msschedule)%end;
+%else %if &ext=trm %then %do;%str(application/x-msterminal)%end;
+%else %if &ext=wri %then %do;%str(application/x-mswrite)%end;
+%else %if &ext=nc %then %do;%str(application/x-netcdf)%end;
+%else %if &ext=cdf %then %do;%str(application/x-netcdf)%end;
+%else %if &ext=nzb %then %do;%str(application/x-nzb)%end;
+%else %if &ext=p12 %then %do;%str(application/x-pkcs12)%end;
+%else %if &ext=pfx %then %do;%str(application/x-pkcs12)%end;
+%else %if &ext=p7b %then %do;%str(application/x-pkcs7-certificates)%end;
+%else %if &ext=spc %then %do;%str(application/x-pkcs7-certificates)%end;
+%else %if &ext=p7r %then %do;%str(application/x-pkcs7-certreqresp)%end;
+%else %if &ext=rar %then %do;%str(application/x-rar-compressed)%end;
+%else %if &ext=ris %then %do;%str(application/x-research-info-systems)%end;
+%else %if &ext=sh %then %do;%str(application/x-sh)%end;
+%else %if &ext=shar %then %do;%str(application/x-shar)%end;
+%else %if &ext=swf %then %do;%str(application/x-shockwave-flash)%end;
+%else %if &ext=xap %then %do;%str(application/x-silverlight-app)%end;
+%else %if &ext=sql %then %do;%str(application/x-sql)%end;
+%else %if &ext=sit %then %do;%str(application/x-stuffit)%end;
+%else %if &ext=sitx %then %do;%str(application/x-stuffitx)%end;
+%else %if &ext=srt %then %do;%str(application/x-subrip)%end;
+%else %if &ext=sv4cpio %then %do;%str(application/x-sv4cpio)%end;
+%else %if &ext=sv4crc %then %do;%str(application/x-sv4crc)%end;
+%else %if &ext=t3 %then %do;%str(application/x-t3vm-image)%end;
+%else %if &ext=gam %then %do;%str(application/x-tads)%end;
+%else %if &ext=tar %then %do;%str(application/x-tar)%end;
+%else %if &ext=tcl %then %do;%str(application/x-tcl)%end;
+%else %if &ext=tex %then %do;%str(application/x-tex)%end;
+%else %if &ext=tfm %then %do;%str(application/x-tex-tfm)%end;
+%else %if &ext=texinfo %then %do;%str(application/x-texinfo)%end;
+%else %if &ext=texi %then %do;%str(application/x-texinfo)%end;
+%else %if &ext=obj %then %do;%str(application/x-tgif)%end;
+%else %if &ext=ustar %then %do;%str(application/x-ustar)%end;
+%else %if &ext=src %then %do;%str(application/x-wais-source)%end;
+%else %if &ext=der %then %do;%str(application/x-x509-ca-cert)%end;
+%else %if &ext=crt %then %do;%str(application/x-x509-ca-cert)%end;
+%else %if &ext=fig %then %do;%str(application/x-xfig)%end;
+%else %if &ext=xlf %then %do;%str(application/x-xliff+xml)%end;
+%else %if &ext=xpi %then %do;%str(application/x-xpinstall)%end;
+%else %if &ext=xz %then %do;%str(application/x-xz)%end;
+%else %if &ext=xdf %then %do;%str(application/xcap-diff+xml)%end;
+%else %if &ext=xht %then %do;%str(application/xhtml+xml)%end;
+%else %if &ext=xsl %then %do;%str(application/xml)%end;
+%else %if &ext=dtd %then %do;%str(application/xml-dtd)%end;
+%else %if &ext=xpl %then %do;%str(application/xproc+xml)%end;
+%else %if &ext=adp %then %do;%str(audio/adpcm)%end;
+%else %if &ext=au %then %do;%str(audio/basic)%end;
+%else %if &ext=snd %then %do;%str(audio/basic)%end;
+%else %if &ext=m4a %then %do;%str(audio/mp4)%end;
+%else %if &ext=mp4a %then %do;%str(audio/mp4)%end;
+%else %if &ext=s3m %then %do;%str(audio/s3m)%end;
+%else %if &ext=sil %then %do;%str(audio/silk)%end;
+%else %if &ext=uva %then %do;%str(audio/vnd.dece.audio)%end;
+%else %if &ext=uvva %then %do;%str(audio/vnd.dece.audio)%end;
+%else %if &ext=eol %then %do;%str(audio/vnd.digital-winds)%end;
+%else %if &ext=dra %then %do;%str(audio/vnd.dra)%end;
+%else %if &ext=dts %then %do;%str(audio/vnd.dts)%end;
+%else %if &ext=dtshd %then %do;%str(audio/vnd.dts.hd)%end;
+%else %if &ext=lvp %then %do;%str(audio/vnd.lucent.voice)%end;
+%else %if &ext=pya %then %do;%str(audio/vnd.ms-playready.media.pya)%end;
+%else %if &ext=ecelp4800 %then %do;%str(audio/vnd.nuera.ecelp4800)%end;
+%else %if &ext=ecelp7470 %then %do;%str(audio/vnd.nuera.ecelp7470)%end;
+%else %if &ext=ecelp9600 %then %do;%str(audio/vnd.nuera.ecelp9600)%end;
+%else %if &ext=rip %then %do;%str(audio/vnd.rip)%end;
+%else %if &ext=weba %then %do;%str(audio/webm)%end;
+%else %if &ext=mka %then %do;%str(audio/x-matroska)%end;
+%else %if &ext=m3u %then %do;%str(audio/x-mpegurl)%end;
+%else %if &ext=wax %then %do;%str(audio/x-ms-wax)%end;
+%else %if &ext=wma %then %do;%str(audio/x-ms-wma)%end;
+%else %if &ext=ra %then %do;%str(audio/x-pn-realaudio)%end;
+%else %if &ext=ram %then %do;%str(audio/x-pn-realaudio)%end;
+%else %if &ext=rmp %then %do;%str(audio/x-pn-realaudio-plugin)%end;
+%else %if &ext=xm %then %do;%str(audio/xm)%end;
+%else %if &ext=ttc %then %do;%str(font/collection)%end;
+%else %if &ext=g3 %then %do;%str(image/g3fax)%end;
+%else %if &ext=btif %then %do;%str(image/prs.btif)%end;
+%else %if &ext=svg %then %do;%str(image/svg+xml)%end;
+%else %if &ext=svgz %then %do;%str(image/svg+xml)%end;
+%else %if &ext=tif %then %do;%str(image/tiff)%end;
+%else %if &ext=psd %then %do;%str(image/vnd.adobe.photoshop)%end;
+%else %if &ext=djv %then %do;%str(image/vnd.djvu)%end;
+%else %if &ext=djvu %then %do;%str(image/vnd.djvu)%end;
+%else %if &ext=sub %then %do;%str(image/vnd.dvb.subtitle)%end;
+%else %if &ext=dwg %then %do;%str(image/vnd.dwg)%end;
+%else %if &ext=dxf %then %do;%str(image/vnd.dxf)%end;
+%else %if &ext=fbs %then %do;%str(image/vnd.fastbidsheet)%end;
+%else %if &ext=fpx %then %do;%str(image/vnd.fpx)%end;
+%else %if &ext=fst %then %do;%str(image/vnd.fst)%end;
+%else %if &ext=mmr %then %do;%str(image/vnd.fujixerox.edmics-mmr)%end;
+%else %if &ext=rlc %then %do;%str(image/vnd.fujixerox.edmics-rlc)%end;
+%else %if &ext=mdi %then %do;%str(image/vnd.ms-modi)%end;
+%else %if &ext=wdp %then %do;%str(image/vnd.ms-photo)%end;
+%else %if &ext=npx %then %do;%str(image/vnd.net-fpx)%end;
+%else %if &ext=wbmp %then %do;%str(image/vnd.wap.wbmp)%end;
+%else %if &ext=xif %then %do;%str(image/vnd.xiff)%end;
+%else %if &ext=ras %then %do;%str(image/x-cmu-raster)%end;
+%else %if &ext=ico %then %do;%str(image/x-icon)%end;
+%else %if &ext=sid %then %do;%str(image/x-mrsid-image)%end;
+%else %if &ext=pct %then %do;%str(image/x-pict)%end;
+%else %if &ext=pic %then %do;%str(image/x-pict)%end;
+%else %if &ext=pnm %then %do;%str(image/x-portable-anymap)%end;
+%else %if &ext=pbm %then %do;%str(image/x-portable-bitmap)%end;
+%else %if &ext=pgm %then %do;%str(image/x-portable-graymap)%end;
+%else %if &ext=ppm %then %do;%str(image/x-portable-pixmap)%end;
+%else %if &ext=xbm %then %do;%str(image/x-xbitmap)%end;
+%else %if &ext=xpm %then %do;%str(image/x-xpixmap)%end;
+%else %if &ext=xwd %then %do;%str(image/x-xwindowdump)%end;
+%else %if &ext=eml %then %do;%str(message/rfc822)%end;
+%else %if &ext=mime %then %do;%str(message/rfc822)%end;
+%else %if &ext=iges %then %do;%str(model/iges)%end;
+%else %if &ext=igs %then %do;%str(model/iges)%end;
+%else %if &ext=dae %then %do;%str(model/vnd.collada+xml)%end;
+%else %if &ext=dwf %then %do;%str(model/vnd.dwf)%end;
+%else %if &ext=gdl %then %do;%str(model/vnd.gdl)%end;
+%else %if &ext=gtw %then %do;%str(model/vnd.gtw)%end;
+%else %if &ext=vtu %then %do;%str(model/vnd.vtu)%end;
+%else %if &ext=vrml %then %do;%str(model/vrml)%end;
+%else %if &ext=wrl %then %do;%str(model/vrml)%end;
+%else %if &ext=x3db %then %do;%str(model/x3d+binary)%end;
+%else %if &ext=x3dbz %then %do;%str(model/x3d+binary)%end;
+%else %if &ext=x3dv %then %do;%str(model/x3d+vrml)%end;
+%else %if &ext=x3dvz %then %do;%str(model/x3d+vrml)%end;
+%else %if &ext=x3d %then %do;%str(model/x3d+xml)%end;
+%else %if &ext=x3dz %then %do;%str(model/x3d+xml)%end;
+%else %if &ext=appcache %then %do;%str(text/cache-manifest)%end;
+%else %if &ext=ics %then %do;%str(text/calendar)%end;
+%else %if &ext=ifb %then %do;%str(text/calendar)%end;
+%else %if &ext=htm %then %do;%str(text/html)%end;
+%else %if &ext=js %then %do;%str(text/javascript)%end;
+%else %if &ext=mjs %then %do;%str(text/javascript)%end;
+%else %if &ext=dsc %then %do;%str(text/prs.lines.tag)%end;
+%else %if &ext=rtx %then %do;%str(text/richtext)%end;
+%else %if &ext=sgm %then %do;%str(text/sgml)%end;
+%else %if &ext=tsv %then %do;%str(text/tab-separated-values)%end;
+%else %if &ext=ttl %then %do;%str(text/turtle)%end;
+%else %if &ext=curl %then %do;%str(text/vnd.curl)%end;
+%else %if &ext=dcurl %then %do;%str(text/vnd.curl.dcurl)%end;
+%else %if &ext=mcurl %then %do;%str(text/vnd.curl.mcurl)%end;
+%else %if &ext=scurl %then %do;%str(text/vnd.curl.scurl)%end;
+%else %if &ext=sub %then %do;%str(text/vnd.dvb.subtitle)%end;
+%else %if &ext=fly %then %do;%str(text/vnd.fly)%end;
+%else %if &ext=flx %then %do;%str(text/vnd.fmi.flexstor)%end;
+%else %if &ext=gv %then %do;%str(text/vnd.graphviz)%end;
+%else %if &ext=3dml %then %do;%str(text/vnd.in3d.3dml)%end;
+%else %if &ext=spot %then %do;%str(text/vnd.in3d.spot)%end;
+%else %if &ext=jad %then %do;%str(text/vnd.sun.j2me.app-descriptor)%end;
+%else %if &ext=wml %then %do;%str(text/vnd.wap.wml)%end;
+%else %if &ext=wmls %then %do;%str(text/vnd.wap.wmlscript)%end;
+%else %if &ext=s %then %do;%str(text/x-asm)%end;
+%else %if &ext=java %then %do;%str(text/x-java-source)%end;
+%else %if &ext=p %then %do;%str(text/x-pascal)%end;
+%else %if &ext=pas %then %do;%str(text/x-pascal)%end;
+%else %if &ext=etx %then %do;%str(text/x-setext)%end;
+%else %if &ext=uu %then %do;%str(text/x-uuencode)%end;
+%else %if &ext=vcs %then %do;%str(text/x-vcalendar)%end;
+%else %if &ext=vcf %then %do;%str(text/x-vcard)%end;
+%else %if &ext=3gp %then %do;%str(video/3gpp)%end;
+%else %if &ext=3g2 %then %do;%str(video/3gpp2)%end;
+%else %if &ext=jpgv %then %do;%str(video/jpeg)%end;
+%else %if &ext=jpgm %then %do;%str(video/jpm)%end;
+%else %if &ext=mjp2 %then %do;%str(video/mj2)%end;
+%else %if &ext=ogv %then %do;%str(video/ogg)%end;
+%else %if &ext=mov %then %do;%str(video/quicktime)%end;
+%else %if &ext=qt %then %do;%str(video/quicktime)%end;
+%else %if &ext=uvh %then %do;%str(video/vnd.dece.hd)%end;
+%else %if &ext=uvvh %then %do;%str(video/vnd.dece.hd)%end;
+%else %if &ext=uvm %then %do;%str(video/vnd.dece.mobile)%end;
+%else %if &ext=uvvm %then %do;%str(video/vnd.dece.mobile)%end;
+%else %if &ext=uvp %then %do;%str(video/vnd.dece.pd)%end;
+%else %if &ext=uvvp %then %do;%str(video/vnd.dece.pd)%end;
+%else %if &ext=uvs %then %do;%str(video/vnd.dece.sd)%end;
+%else %if &ext=uvvs %then %do;%str(video/vnd.dece.sd)%end;
+%else %if &ext=uvv %then %do;%str(video/vnd.dece.video)%end;
+%else %if &ext=uvvv %then %do;%str(video/vnd.dece.video)%end;
+%else %if &ext=dvb %then %do;%str(video/vnd.dvb.file)%end;
+%else %if &ext=fvt %then %do;%str(video/vnd.fvt)%end;
+%else %if &ext=m4u %then %do;%str(video/vnd.mpegurl)%end;
+%else %if &ext=mxu %then %do;%str(video/vnd.mpegurl)%end;
+%else %if &ext=pyv %then %do;%str(video/vnd.ms-playready.media.pyv)%end;
+%else %if &ext=uvu %then %do;%str(video/vnd.uvvu.mp4)%end;
+%else %if &ext=uvvu %then %do;%str(video/vnd.uvvu.mp4)%end;
+%else %if &ext=viv %then %do;%str(video/vnd.vivo)%end;
+%else %if &ext=asf %then %do;%str(video/x-ms-asf)%end;
+%else %if &ext=asx %then %do;%str(video/x-ms-asf)%end;
+%else %if &ext=vob %then %do;%str(video/x-ms-vob)%end;
+%else %if &ext=wm %then %do;%str(video/x-ms-wm)%end;
+%else %if &ext=wmv %then %do;%str(video/x-ms-wmv)%end;
+%else %if &ext=wmx %then %do;%str(video/x-ms-wmx)%end;
+%else %if &ext=wvx %then %do;%str(video/x-ms-wvx)%end;
+%else %if &ext=avi %then %do;%str(video/x-msvideo)%end;
+%else %if &ext=movie %then %do;%str(video/x-sgi-movie)%end;
+%else %if &ext=ice %then %do;%str(x-conference/x-cooltalk)%end;
+%else %if "&ext"="in" %then %do;%str(text/plain)%end;
+%else %do;%str(application/octet-stream)%end;
+
+%mend mf_mimetype;
\ No newline at end of file
diff --git a/sasjs/sasjsconfig.json b/sasjs/sasjsconfig.json
index ab77752..bbdb6e7 100644
--- a/sasjs/sasjsconfig.json
+++ b/sasjs/sasjsconfig.json
@@ -37,6 +37,10 @@
"allowInsecureRequests": false
},
"appLoc": "/Public/app/macrocore",
+ "deployConfig": {
+ "deployServicePack": true,
+ "deployScripts": []
+ },
"macroFolders": [
"viya",
"tests/viyaonly"
diff --git a/tests/base/mf_mimetype.test.sas b/tests/base/mf_mimetype.test.sas
new file mode 100644
index 0000000..fc5ded3
--- /dev/null
+++ b/tests/base/mf_mimetype.test.sas
@@ -0,0 +1,16 @@
+/**
+ @file
+ @brief Testing mf_mimetype macro
+
+ SAS Macros
+ @li mf_mimetype.sas
+ @li mp_assert.sas
+
+**/
+
+
+%mp_assert(
+ iftrue=("%mf_mimetype(XLS)"="application/vnd.ms-excel",
+ desc=Checking correct value
+)
+
diff --git a/tests/testinit.sas b/tests/testinit.sas
index d9344ee..854b048 100644
--- a/tests/testinit.sas
+++ b/tests/testinit.sas
@@ -10,7 +10,7 @@
**/
/* location in metadata or SAS Drive for temporary files */
-%let mcTestAppLoc=/tmp/tests/sasjs/core/%mf_uid();
+%let mcTestAppLoc=/Public/testresults/sasjs_core/%mf_uid();
/* set defaults */
%mp_init()
diff --git a/tests/viyaonly/mfv_getpathuri.test.sas b/tests/viyaonly/mfv_getpathuri.test.sas
new file mode 100644
index 0000000..ef3a866
--- /dev/null
+++ b/tests/viyaonly/mfv_getpathuri.test.sas
@@ -0,0 +1,35 @@
+/**
+ @file
+ @brief Testing mfv_getpathuri macro function
+
+ SAS Macros
+ @li mf_uid.sas
+ @li mfv_getpathuri.sas
+ @li mp_assert.sas
+ @li mv_createfile.sas
+
+**/
+
+options mprint sgen;
+
+%let file=%mf_uid();
+
+/* create a folder */
+filename somefile temp;
+data _null_;
+ file somefile;
+ put 'hello testings';
+run;
+%let path=&mcTestAppLoc/temp;
+%mv_createfile(path=&path, name=&file..txt,inref=somefile)
+
+
+%mp_assert(
+ iftrue=(%mfv_existfile(&path/&file..txt)=1),
+ desc=Check if created file exists
+)
+
+%mp_assert(
+ iftrue=(%length(%mfv_getpathuri(&path/&file..txt))>0),
+ desc=Check that a URI was returned
+)
\ No newline at end of file
diff --git a/tests/viyaonly/mv_createfile.test.sas b/tests/viyaonly/mv_createfile.test.sas
index 52ea2fc..00bd499 100644
--- a/tests/viyaonly/mv_createfile.test.sas
+++ b/tests/viyaonly/mv_createfile.test.sas
@@ -6,6 +6,7 @@
@li mf_uid.sas
@li mfv_existfile.sas
@li mp_assert.sas
+ @li mp_assertscope.sas
@li mv_createfile.sas
@@ -21,22 +22,65 @@ data _null_;
file somefile;
put 'hello testings';
run;
-%mv_createfile(path=&mcTestAppLoc/temp, name=&file..txt,inref=somefile)
+%mp_assertscope(SNAPSHOT)
+%mv_createfile(path=&mcTestAppLoc/temp, name=&file..txt,inref=somefile,mdebug=1)
+%mp_assertscope(COMPARE
+ ,ignorelist=MCLIB0_JADP1LEN MCLIB0_JADP2LEN MCLIB0_JADPNUM
+ MCLIB0_JADVLEN MCLIB2_JADP1LEN
+ SASJSPROCESSMODE SASJS_STPSRV_HEADER_LOC
+ MCLIB2_JADP2LEN MCLIB2_JADPNUM MCLIB2_JADVLEN
+)
%mp_assert(
iftrue=(%mfv_existfile(&mcTestAppLoc/temp/&file..txt)=1),
desc=Check if created file exists
)
-%put TEST 2 - dataset upload ;
+%put TEST 2 - html file;
+filename f2 temp;
+data _null_;
+ file f2;
+ put 'Hello world
';
+run;
+%mv_createfile(path=&mcTestAppLoc/temp, name=test.html,inref=f2,mdebug=1)
+
+%mp_assert(
+ iftrue=(%mfv_existfile(&mcTestAppLoc/temp/test.html)=1),
+ desc=Check if created file exists
+)
+
+%put TEST 3 - dataset upload ;
data temp;
x=1;
run;
filename ds "%sysfunc(pathname(work))/temp.sas7bdat";
-%mv_createfile(path=&mcTestAppLoc/temp, name=&file..sas7bdat,inref=ds)
+%mv_createfile(path=&mcTestAppLoc/temp, name=&file..sas7bdat,inref=ds,mdebug=1)
%mp_assert(
iftrue=(%mfv_existfile(&mcTestAppLoc/temp/&file..sas7bdat)=1),
desc=Check if created dataset exists
-)
\ No newline at end of file
+)
+
+%put TEST 4 - create a .sas file;
+filename f4 temp;
+data _null_;
+ file f4;
+ put '%put hello FromSASStudioBailey; ';
+run;
+%mv_createfile(path=&mcTestAppLoc/temp, name=test4.sas,inref=f4,mdebug=1)
+
+%mp_assert(
+ iftrue=(%mfv_existfile(&mcTestAppLoc/temp/test4.sas)=1),
+ desc=Check if created sas program exists
+)
+
+
+
+%put TEST 5 - reading from files service and writing back;
+filename sendfrom filesrvc folderpath="&mcTestAppLoc/temp" filename='test4.sas';
+
+OPTIONS MERROR SYMBOLGEN MLOGIC MPRINT;
+
+%mv_createfile(path=&mcTestAppLoc/temp,name=test5.sas,inref=sendfrom,mdebug=1) ;
+
diff --git a/tests/viyaonly/mv_createfolder.test.sas b/tests/viyaonly/mv_createfolder.test.sas
index ce10507..605d818 100644
--- a/tests/viyaonly/mv_createfolder.test.sas
+++ b/tests/viyaonly/mv_createfolder.test.sas
@@ -29,4 +29,20 @@ run;
%mp_assert(
iftrue=(&test=1),
desc=Check if temp folder can be successfully created
+)
+
+/* create a folder without output dataset as part of the original macro */
+%mv_createfolder(path=&mcTestAppLoc/temp/&folder/folder2,outds=folders2)
+
+%let test=0;
+data _null_;
+ set work.folders2;
+ putlog (_all_)(=);
+ if not missing(self_uri) and not missing(parent_uri)
+ then call symputx('test2',1);
+run;
+
+%mp_assert(
+ iftrue=(&test2=1),
+ desc=Check if outds param works
)
\ No newline at end of file
diff --git a/viya/mfv_getpathuri.sas b/viya/mfv_getpathuri.sas
new file mode 100644
index 0000000..a6309f6
--- /dev/null
+++ b/viya/mfv_getpathuri.sas
@@ -0,0 +1,52 @@
+/**
+ @file
+ @brief Returns the uri of a file or folder
+ @details The automatic variable _FILESRVC_[fref]_URI is used after assigning
+ a fileref using the filesrvc engine.
+
+ Usage:
+
+ %put %mfv_existfile(/Public/folder/file.txt);
+ %put %mfv_existfile(/Public/folder);
+
+ @param [in] filepath The full path to the file on SAS drive
+ (eg /Public/myfile.txt)
+
+ SAS Macros
+ @li mf_abort.sas
+ @li mf_getuniquefileref.sas
+
+ Related Macros
+ @li mfv_existfile.sas
+ @li mfv_existfolder.sas
+
+ @version 3.5
+ @author [Allan Bowe](https://www.linkedin.com/in/allanbowe/)
+**/
+
+%macro mfv_getpathuri(filepath
+)/*/STORE SOURCE*/;
+
+ %mf_abort(
+ iftrue=(&syscc ne 0),
+ msg=Cannot enter &sysmacroname with syscc=&syscc
+ )
+
+ %local fref rc path name var /* var is used to avoid delete timing issue */;
+ %let fref=%mf_getuniquefileref();
+ %let name=%scan(&filepath,-1,/);
+ %let path=%substr(&filepath,1,%length(&filepath)-%length(&name)-1);
+
+ %if %sysfunc(filename(fref,,filesrvc,folderPath="&path" filename="&name"))=0
+ %then %do;
+ %let var=_FILESRVC_&fref._URI;
+ %str(&&&var)
+ %let rc=%sysfunc(filename(fref));
+ %symdel &var;
+ %end;
+ %else %do;
+ %put &sysmacroname: did not find &filepath;
+ %let syscc=0;
+ %end;
+
+%mend mfv_getpathuri;
\ No newline at end of file
diff --git a/viya/mv_createfile.sas b/viya/mv_createfile.sas
index 20196c8..c109b39 100644
--- a/viya/mv_createfile.sas
+++ b/viya/mv_createfile.sas
@@ -1,8 +1,12 @@
/**
@file
- @brief Creates a file in SAS Drive
- @details Creates a file in SAS Drive and adds the appropriate content type.
+ @brief Creates a file in SAS Drive using the API method
+ @details Creates a file in SAS Drive using the API interface.
If the parent folder does not exist, it is created.
+ The API approach is more flexible than using the filesrvc engine of the
+ filename statement, as it provides more options.
+
+ SAS docs: https://developer.sas.com/rest-apis/files/createNewFile
Usage:
@@ -14,49 +18,53 @@
%mv_createfile(path=/Public/temp,name=newfile.txt,inref=myfile)
- @param [in] path= The parent folder in which to create the file
+ @param [in] path= The parent (SAS Drive) folder in which to create the file
@param [in] name= The name of the file to be created
@param [in] inref= The fileref pointing to the file to be uploaded
@param [in] intype= (BINARY) The type of the input data. Valid values:
@li BINARY File is copied byte for byte using the mp_binarycopy.sas macro.
@li BASE64 File will be first decoded using the mp_base64.sas macro, then
loaded byte by byte to SAS Drive.
- @param [in] contentdisp= (inline) Content Disposition. Example values:
+ @param [in] contentdisp= (attchment) Content Disposition. Example values:
@li inline
@li attachment
- @param [in] ctype= (0) Set a default HTTP Content-Type header to be returned
- with the file when the content is retrieved from the Files service.
+ @param [in] ctype= (0) The actual MIME type of the file (if blank will be
+ determined based on file extension))
@param [in] access_token_var= The global macro variable to contain the access
token, if using authorization_code grant type.
@param [in] grant_type= (sas_services) Valid values are:
@li password
@li authorization_code
@li sas_services
+ @param [out] outds= (_null_) Output dataset with the uri of the new file
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
- @version VIYA V.03.05
- @author Allan Bowe, source: https://github.com/sasjs/core
-
SAS Macros
+ @li mf_getplatform.sas
@li mf_getuniquefileref.sas
+ @li mf_getuniquename.sas
@li mf_isblank.sas
+ @li mf_mimetype.sas
@li mp_abort.sas
@li mp_base64copy.sas
- @li mp_binarycopy.sas
@li mv_createfolder.sas
+ Related Macros
+ @li mv_createfile.sas
+
**/
%macro mv_createfile(path=
,name=
,inref=
,intype=BINARY
- ,contentdisp=inline
+ ,contentdisp=attachment
,ctype=0
,access_token_var=ACCESS_TOKEN
,grant_type=sas_services
,mdebug=0
+ ,outds=_null_
);
%local dbg;
%if &mdebug=1 %then %do;
@@ -91,38 +99,92 @@
,msg=%str(name value with length >1 must be provided)
)
+/* prep the source file */
+%local fref;
+%let fref=%mf_getuniquefileref();
+
+%if %upcase(&intype)=BINARY %then %let fref=&inref;
+%else %if %upcase(&intype)=BASE64 %then %do;
+ %mp_base64copy(inref=&inref, outref=&fref, action=DECODE)
+%end;
+%else %put %str(ERR)OR: invalid value for intype: &intype;
+
+
+%if &mdebug=1 %then %do;
+ data _null_;
+ infile &fref lrecl=32767;
+ input;
+ put _infile_;
+ run;
+%end;
+
+
/* create folder if it does not already exist */
+%local folderds parenturi;
+%let folderds=%mf_getuniquename(prefix=folderds);
%mv_createfolder(path=&path
,access_token_var=&access_token_var
,grant_type=&grant_type
,mdebug=&mdebug
+ ,outds=&folderds
)
+data _null_;
+ set &folderds;
+ call symputx('self_uri',self_uri,'l');
+run;
-/* create file with relevant options */
-%local fref;
-%let fref=%mf_getuniquefileref();
-filename &fref filesrvc
- folderPath="&path"
- filename="&name"
- cdisp="&contentdisp"
-%if "&ctype" ne "0" %then %do;
- ctype="&ctype"
-%end;
- lrecl=1048544;
-%if &intype=BINARY %then %do;
- %mp_binarycopy(inref=&inref, outref=&fref)
-%end;
-%else %if &intype=BASE64 %then %do;
- %mp_base64copy(inref=&inref, outref=&fref, action=DECODE)
-%end;
-
-filename &fref clear;
+options noquotelenmax;
%local base_uri; /* location of rest apis */
%let base_uri=%mf_getplatform(VIYARESTAPI);
-%put &sysmacroname: File &name successfully created in &path;
-%put &sysmacroname:;%put;
+%local url mimetype;
+%let url=&base_uri/files/files?parentFolderUri=&self_uri;
+
+/* fetch job info */
+%local fname1;
+%let fname1=%mf_getuniquefileref();
+proc http method='POST' out=&fname1 &oauth_bearer in=&fref
+ %if "&ctype" = "0" %then %do;
+ %let mimetype=%mf_mimetype(%scan(&name,-1,.));
+ ct="&mimetype"
+ %end;
+ %else %do;
+ ct="&ctype"
+ %end;
+ %if "&mimetype"="text/html" %then %do;
+ url="&url%str(&)typeDefName=file";
+ %end;
+ %else %do;
+ url="&url";
+ %end;
+
+ headers "Accept"="application/json"
+ %if &grant_type=authorization_code %then %do;
+ "Authorization"="Bearer &&&access_token_var"
+ %end;
+ "Content-Disposition"= "&contentdisp filename=""&name""; name=""&name"";";
+run;
+%put &=SYS_PROCHTTP_STATUS_CODE;
+%put &=SYS_PROCHTTP_STATUS_PHRASE;
+%mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 201)
+ ,mac=&sysmacroname
+ ,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
+)
+%local libref2;
+%let libref2=%mf_getuniquelibref();
+libname &libref2 JSON fileref=&fname1;
+%put Grabbing the follow on link ;
+data &outds;
+ set &libref2..links end=last;
+ if rel='createChild' then do;
+ call symputx('href',quote(cats("&base_uri",href)),'l');
+ &dbg put (_all_)(=);
+ end;
+run;
+
+%put &sysmacroname: File &name successfully created:;%put;
+%put &base_uri%mfv_getpathuri(&path/&name);%put;
%put &base_uri/SASJobExecution?_file=&path/&name;%put;
%put &sysmacroname:;
diff --git a/viya/mv_createfolder.sas b/viya/mv_createfolder.sas
index 3ff7279..1fca4d3 100644
--- a/viya/mv_createfolder.sas
+++ b/viya/mv_createfolder.sas
@@ -17,15 +17,16 @@
@li sas_services
@param [in] mdebug=(0) set to 1 to enable DEBUG messages
+ @param [out] outds=(_null_) Optionally create an output dataset which will
+ contain the uri (self_uri) of the created (and parent) folder.
- @version VIYA V.03.04
- @author Allan Bowe, source: https://github.com/sasjs/core
SAS Macros
@li mp_abort.sas
@li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas
@li mf_isblank.sas
+ @li mfv_getpathuri.sas
@li mf_getplatform.sas
@li mfv_existfolder.sas
@@ -36,6 +37,7 @@
,access_token_var=ACCESS_TOKEN
,grant_type=sas_services
,mdebug=0
+ ,outds=_null_
);
%local dbg;
%if &mdebug=1 %then %do;
@@ -46,6 +48,11 @@
%if %mfv_existfolder(&path)=1 %then %do;
%put &sysmacroname: &path already exists;
+ data &outds;
+ self_uri="%mfv_getpathuri(&path)";
+ output;
+ stop;
+ run;
%return;
%end;
@@ -80,12 +87,12 @@ options noquotelenmax;
%local subfolder_cnt; /* determine the number of subfolders */
%let subfolder_cnt=%sysfunc(countw(&path,/));
-%local href; /* resource address (none for root) */
-%let href="/folders/folders?parentFolderUri=/folders/folders/none";
-
%local base_uri; /* location of rest apis */
%let base_uri=%mf_getplatform(VIYARESTAPI);
+%local href; /* resource address (none for root) */
+%let href="&base_uri/folders/folders?parentFolderUri=/folders/folders/none";
+
%local x newpath subfolder;
%do x=1 %to &subfolder_cnt;
%let subfolder=%scan(&path,&x,%str(/));
@@ -115,7 +122,7 @@ options noquotelenmax;
%put &sysmacroname following check to see if &newpath exists:;
%put _local_;
data _null_;
- set &fname1;
+ infile &fname1;
input;
putlog _infile_;
run;
@@ -167,12 +174,17 @@ options noquotelenmax;
%let libref2=%mf_getuniquelibref();
libname &libref2 JSON fileref=&fname2;
%put &sysmacroname &newpath now created. Grabbing the follow on link ;
- data _null_;
+ data &outds;
set &libref2..links;
if rel='createChild' then do;
call symputx('href',quote(cats("&base_uri",href)),'l');
&dbg put (_all_)(=);
end;
+ if method='GET' and rel='self' then do;
+ self_uri=uri;
+ output;
+ end;
+ keep self_uri ;
run;
libname &libref2 clear;