From 6deb490c4d5e8a798b6a91b5f3c9b59b86e4d1eb Mon Sep 17 00:00:00 2001 From: Sherman Rofeman Date: Tue, 15 Aug 2023 20:16:32 -0300 Subject: [PATCH] Rewrite code base for release v9.0.7 This commits adds all the changes prototyped for the next release: v9.0.7, including: - a full refactor using a new code style. - improvements of error descriptions. - fix of bug in which failed exit codes would never be thrown: the error bit is now properly being set. --- Makefile | 4 +- README.md | 61 +++++--- assets/preview-0.webp | Bin 4308 -> 0 bytes assets/preview-1.webp | Bin 5032 -> 0 bytes assets/preview-2.webp | Bin 5644 -> 0 bytes preview.webp | Bin 0 -> 21118 bytes reveal.c | 353 ++++++++++++++++++++++++++---------------- 7 files changed, 255 insertions(+), 163 deletions(-) delete mode 100644 assets/preview-0.webp delete mode 100644 assets/preview-1.webp delete mode 100644 assets/preview-2.webp create mode 100644 preview.webp diff --git a/Makefile b/Makefile index 57364ca..5f0d258 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ file=reveal -optimization=z +optimization_level=z ${file}: - clang ${file}.c -o ${file} -O${optimization} + clang ${file}.c -o ${file} -O${optimization_level} .PHONY: ${file} diff --git a/README.md b/README.md index 8416ad2..2bbdb96 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,19 @@ # Reveal ## About -A program that reveals information about entries in the Linux's file system, -such as their: contents, type, size, permissions, ownership and modified date. -Its features were implemented in a way to make it a fair good alternative for -scripting. +This repository contains the source code of Reveal, a program that reveals +information about entries in the file system of POSIX-like operating systems, +such as their: contents, type, permissions, ownership and modified date. -Here are some usage preview that you can check out: +It was created with the idea to be an alternative for some utilities from +coreutils: such as `ls`, `cat` and `stat`. By providing much simple and +scalable interactions, it becomes a perfect tool for scripting languages. -- Revealing a directory: in the preview below, `reveal` was used to reveal the - contents of a directory. +Here is a preview that you can check out: in this one, `reveal` was used +to both reveal different types of data from a directory and a file. It also +shows an example of interaction with other utilities from coreutils: `sort`, +`fmt` and `echo`. -![](assets/preview-0.webp) - -- Revealing a file: in the preview below, `reveal` was used to reveal the - contents of a file. - -![](assets/preview-1.webp) - -- Revealing file properties: in the preview below, `reveal` was used to reveal - the size, permissions and user of a file. - -![](assets/preview-2.webp) +![](preview.webp) ## Installation ### Dependencies @@ -28,7 +21,7 @@ In order to install and run this software properly, the following dependencies must be installed: - `git`: required to clone this repository. -- `clang`, `make` and Linux headers for C: required to compile the source code. +- `clang`, `make` and POSIX headers for C: required to compile the source code. ### Procedures Using a command-line utility, follow these steps: @@ -39,6 +32,17 @@ Using a command-line utility, follow these steps: git clone --depth 1 https://github.com/skippyr/reveal ``` +The option `--depth` with value `1` specifies to Git that you only want to +download the latest commit, instead of the whole commit tree it would do +by default. + +Alternatively, you can download its source code directly from GitHub. Access +its page there, click on the button labeled `Code` in the top of the page, +then, on the dropdown menu, click on `Download ZIP`. + +This will download a ZIP file containing the repository, you just have to +extract it somewhere in your machine. + - Access the repository's directory. ```bash @@ -51,16 +55,23 @@ cd reveal make ``` -- Add the binary `reveal`, now in your current directory, in a directory that is - being tracked in your system's `PATH` environment variable in order to turn it - into an available command in your shell sessions. +- Now, `reveal` should be a binary available in your current directory. Add it + to a directory that is being tracked in your system's `PATH` environment + variable, in order to make it an available command in all of your shell + sessions. -- Open a new shell session to reload the environment variables. The program +- Open a new shell session to reload your environment variables. The program should now be installed and available for you to use. + As a quick test, you can make it reveal your current directory: + + ```bash + reveal + ``` + ## Usage -Access its help page by running it using the `--help` options. There you will -find instructions on how to use it, available options and their descriptions. +In its help page you will find instructions on how to use it, available options +and their descriptions. Access it by running it with the `--help` option: ```bash reveal --help diff --git a/assets/preview-0.webp b/assets/preview-0.webp deleted file mode 100644 index 585bddd78cecea5732d479e88b2d193bdd746cad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4308 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 C6l~N0 diff --git a/assets/preview-1.webp b/assets/preview-1.webp deleted file mode 100644 index 4c24ddadd80045042b1e729b8bd1fba0b3a6eda3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5032 zcmV;Z6Ibj~Nk&GX6952LMM6+kP&goz6952^T>zZ{DtG~c06vjKoJysmqM@UcThOo) z31@EnS9pov^gp#zdB}@^r*~z4^d0y;Ab*bC+0Rw!PsjtUbI=3Mo7NNjmPx0if53L> z{r~bu&>twCo%ajZH!OXIYH{-)wtt@f_xq>pPx_Auen0I>_i^IvP;bzQ@{$L=Sn z7qfp;|8x1j=iR9Xgnw4P06&p`I{%03@BFV1PriDBescfM{twv0{-5)olz!trsQ<<8 z75wY_H~jza{jGf{j2xj|4g}ff^;Qww=)a;8`5K94E_o$W{vRPg+V0lK)sg#vG;*iFM7FowhF?ILxw*dyFhGkhdcL!(H<}seL zg$vi+ovjX${{0B$fXQzFB`(ka>{vOe3E6k#uj$J#$|G6@ZIUVr-0{Gmb$-aRgvs}T z1AMV!a+Xj{!`cNOQ7f&tJ)KofAA`^GrBh9;UF!|9n9(%CyI!e`UV9#c7MPA&mI$CN zR7NgmSC4qtx=aNcB3qTU>C&ouwpF`B>O1XE*bp;Awx&Fb#aXv|MKqboQP4w!GABF5 zdsnfUE)K6oB4;G9JY1-9h@Ry|{(73ji+R!Pd9aK<1{-(mt9_~ac8KX`6xRXmPv@ex zi0Ni{0RH}<8D{-yhh4mZkEJFJw4g!Nd{&>8D`**k@nuifa_~LWp_I};f9H{LGZfd_ zarXKUN%7rNRliPtD$dPWhTOii(7AA`eMiYERl@p)d4U>6ad{cc^8XMH|Cz(bpAubE z*$zv#4UY|ExFa;dHPpp&WIbk_YapkL1tEblFzmPSS57aSYG_+J&G4j<%-$>?D2u3* z#vfH&#@B|#*l6y&iNmy_Mq7c;346?Va+6p#YV{pez_YDlk$xcP{f$GL{s{wx8jSB` zfNwK4?)ZLEu5sV8-;=Gnmyl0qc2QGA!1y@Kuwvu8nbwFVOt^oYbsnN`i%{_&BurVm zGa-npwKOS-9Eyf2DjE0azN8IT+Ra6#k}_>CXIGPQ8R_IXw?f`bC$o@W$&>a_;<}Z8 zJo5fO$o6OsVyA4!_c#WIp=_G7RdRKYEdDu9khlT;J>L+^q&}DP`rKFR*tY+%(;f5K zp=U5!qtk{ag`QW{U=qtxvL+}|%7gD53*jW1*--U$xQF~yt2-y)6)PQG83h$=>^9>g z?K4sBP}6)COpyOj-3i}#u;L*ebG-Mv9!!{!X4}uEW+f;~-7hs+4WBCguZ+&vzNIAm zVDl(mO}k_+N|e{-+c(Y@ZDSTA+If9PkZwQy?4lk+>7fULj@g`<*SyjjB;(=zb}(i` z%&s<`zu2Y6N9!eoh8(s}sJZKz2#S;P?~8q#Dso?=hK@bKyUEcrl*48_bqX8(Oo+25 zacnmJ;e#cyEs%;KpaXMto|4+KL=GkOu7B_tnCHUhwXZ21I-n(s{Xu%h4mkYygbQ=t z`56M=06eUIwG6I6%R1T#Kx)@y_}S*ni^Dhdz^{gfnDVapsqF1RUtImBf-3<G!pBn9F;U(=s73@V*E_1}62X@a)DrM=Y9=ZXs{SMDG+pCxctx@S z6z&)}4TuS%@ftUW*4DrjRFL66@V-Q<4VDOeF__rOF5n+NG^qlKZ^Bgm_KxhK<0II~ z45VVv=T=)cGfVk#X!@d0ezZHKF^-0YJf07PS_kV>6OQ||^{BMi>c(MUx}zD`_j&VYK43*{^mQ>>1Tm7FYsvC+Mjn*O=ho&1gpwH9s+G#Oxxw z<{6D1L~a?tF4=URnl#d_5_=%#xJFLX!Z{dNtV$sb9?s=#V7jK2S`;|ob^HQpA6D!@ z$tJJqWD9ik6^~`GF*f(kiY0IVSuEOe7=pqNB~#^g(L;$m>Jtu;Za&E(y2g-ApX`~} zWn?exNFm1j>b|=QzY!ee&kWI@G)(7eSD2fPE&SPH0c!N-htg5u{1$0(2TJw;AV z*2l>|0;T8K6~tktnegit6wF&NRS;EH{V*XNT#!f(yo?6^U#lnK-oK-Cd(`*~*?-2w z6TjkXM4xgpjURjIW{`P=#0oOB&HRO>yr@2w9D-Caz&(QQeM;A$ejl-v-wez%AFmv< z(Il7I=3n=Q`+A?ZALRZy6!Wkfz9ST?`r80d8Ol`>dmrc=%J1g%$~IkwS2@qj8_75?PYP31; zX?GY!df%hxn4x-fO{XzNUvV@@Y?QR~$~0fThG*=^Yq;X~4oR6UjUsQ|*(jJ8fCI_8 z{a-$eqRVvo_?9>ub<%i^!&2AtIg#HQ$1;TLIzZ+Cidm6;sV`!5y~{AqQnBL0!awfr zbPa{=?$8m(1W|?>3ro@Wj8g2>O#*ufo1RqS$<5`gUTXgcqK%$h z`5`e)fT?Ni0vHG|89UQ<=q@0WG74wKR$akS{QsH${pQe#!x@bzFR~B5q+RzlyD$D9 zuguz5eLA6iIN@F-6VY4{n?1L7y@s9`J9I1&X6xa(qgX8o$qJkc0D_QEmal~jz^pmG zyWj{U@Y3np27LhY|0>f$kDD}BKbCkqyNCZo*{E_|JMSf<8Q?N$JT;H1a{pWFgTy7` zj=gg&G=)ctGd(Kcu+yra*jxQ@e?Rb=E~a}^oDJv9KR(UxPj|E@puiEO8s&gr-OZ-R zfxP8sd^0>0St;RxN#061EIq6&G~gQMpOy@0mMGX1lNUDo`>K0W7h@u)EcdoRt!9J& zcALfZ^Bin1?6M}V13>;8iL^q&ci2(wm_a(%mDZ?N^8+`?8Yi8O?-_Qh>wgb8exC2w zKnYMca2-Co^H2n*q+t?94r#AmQmoY9^X1*R zU`dXh9?1@T63pbKMB%e?Cp5gVv97ng6R>zNCv}b`0!hBboERG2;JyAk-y><=bIQyS zT$1m=)!wZc;0eY0aydEqP*DdmSFQBvFdZ8}2UY0;k`%*?V6MarBVsVnqvsC(TrJ)5 z;9Nem$73OYQ2!q9?v1jjpAlB`&^+ti0Tn2UYzUWq%kgmC*b);!sAkCH{tSb+Wl~f~ zy+nYm0eVJ5%`WT(CVowN5p&Nu^mfi+c^IpR?i_0Id%ardb$;?T%oQ6h>qmh0LsL1F9JCAEcQ)KO{?Mg{`?S`P6 zNII?H00000p%p-jvuRUP^>Hrf;j57>oyX!j zMec-WfI9d>>hFYVhd&?ViFq?~e@+X{?Xd>f0Bq89xhF9yz1F(lNT#DLc}=r9E!}|p zUggzRk&ZqneSKuq`NHbJBXmaKeis@0usAYFmZEc`c-6T5EQD|U;5A-3i8j_Q4{A!s zpgRMYI$%M+irQ!no>g2CYkQd5HOWcO3i+b8K zj_-y6FEvbWbTbE;S0t5Ty86Gl0yx{=Vk6(h#w(x>QAktkR~GV|p|8%g_?JYl4%Sc6 zEua~KjQ=KG?2XcoJ(WSxvuf4|kZ=5SujI$W`=PZcpDkw0&~k&eRbMc+X3Zo_^IUCa z4KBbq;XjhafAOI=0w96(L?IjU7IvW9x)4uXR<}n=)o0e~Xidu7vDEC{Sl*;eD%UZZ zHBc<76-9y!zXQ@{(>qTEIERsWgIwd-b*^9v@M(pyZRiY}1K2y?G)H^$K_mVWq|#-H zh?eM*;`kl=Wu4O_k2Vz>Ke3v=u<@d~<`y=m@#XRQvI!6GJDH2^pep6^#jevYtnO*1 zxm%97a?iBWhyYZQF?UJ}-3c-30}}F^$K&MLv8(nCv6s5BWXN1OL8r40@mm}?KCcHV z0wlWZV3)!;x`6VnXAH*j$HtFmMo-DSQuL(CkA>WDgsG6w2!C!ze+2mzsan)> zWLz~>SL(Q^JIK)*D|HE6%Yax&4O&!tJZflUG_|vEQXmJebIEHz4-Ip{SA1RHMsgF> zEaz|d{ILx3x{;hErxUBAUk>Af;Yh^K4;NB-A#sbL{K@~Dh3MQ%gHw3*LN?+UB7u32 zw%)?I*8^Qz^4U>7zwK0c)evLuie-rW^!wU({pC?zZm+OpM_Qc)!gP5Ia3f$4J%=oT z5U9zhMBfv=0jsh`?l8mZUlTGtq_L#p_9K&fT$_X4u2r4hj-TYSD`zT*$BF0mQX_<6 zrm7P^y!fm=DW^?T$*cL`vS=@m&y{C znA6h45+$$WJY}Y6oCYdpW?$9)5}epzb($(X*9>dvG^nMUbSiTlX3{8>wT+Omtky3_ zodPg@JP#u(P62Yw_>o;%w&2iPusYxnipzNX(kICZeh!6gE`HIV)FcBK%g)jz`4<#5 za(Y>`1FgV0tfA$u)t^%i5s(J!_!i8;>2OT2A9H_j{AJF7WPPEEI=6spF+JE+%rML} zFI*fEBe%)!H@{(=reE~Ih1m`&>INJLXgu?lZ5T@UZI_Y6bL`gd+C3e!9z*q&CdmCs z^e}M`6_q9av_YiDYYn^oR%UIA;V)o^k}dpOBr2M$=i)PQN6!E)hunXrl1i{sZ87UJ z0z`u4BJOGcn@fnXvEgmLAbXdiLtXzQZZA@~Lr@|Oo~zaNM-p^2pVVvLypb5#g&l?d z))jiZ#AT^x5njZy)1H=y3&R=(blw zhyPxB(WiPP2aab{`i+HZ#;W5W5;{o59S7WXlE*| zj5MF$3YY@Enwo?ZfB)ExfEkXQN&8s=jz`r>f^~{dORD$q0)C&?s~WycvW%-&SOtuy zs&Ew0fIli$$p2o>DUGE&C{Z5h(NJmGHPW`((75_ToA3s_tp1eQhjNaQ_&~T+25p{VQgYa+>6U&%Uk{D=5>-@qdDGqCiL8A9azxc=2Y~ zTW|8ZB!_F|<7u{cH`r`2!WhlS)3d?ug+g$@o#g@k#|KU)E*y%>Dd;1gH5A9yy*>mfQQ!kYLzx>ztKWm@Ef0*!x<0tsupud*?NdGJ1qy2Ym&cpuC z{-3B9uzx=PNB%4KSNzYThNON9|CRQ~_5u96`VajNT|4r=&;K*nEBXic|Jh%5ukb(g zf35T-^&kE(^S=SV&Ofq$*8dgS)6#qPsFv!)IzJ`X^ZvTYDd`^is0ka<<6C%f17Lq| z`gw@)24n}G2(Y@IR0-hi^B5JD?=RQqT@+q>utqH6#W29Z+t>0v)(h>kw$#!KWcvkbZ_ndoJuMeRA7r zjR9unt@CV zO3uz2u6wst*0y6M#AUwWY)gcYM}J9Tb(|36nC>A13H9VmFmegZ**rz`hpKJ8Kz}lD zz|df1)0?f;5c+i{x?|v))jG;lM0Nv);5|_+(!M|tVjHF7OUv^J4Vy8P56A-XQCWFr zSl-Ppd5^2&ky+y?Npu(%OGKrC*GE|`j+OooTT*#?CdH5ReO=%7X@P%O*!#q4kkU=1 zi|`H*BA%wJWz}=8A^-t~wihe&XO!x|F>4(-$E_6n_+19-;#cg424J+`&ty_4RRYT~ zgS`jMg8jLI7=Ho>_*r5GfM?zwb^T2Ovu=aPq=UgnrawS(|*U>tUmLO+*9=- z1XK0;QLAo#wXZFod`OhmVE!2%rk3~3EKi$Q;B}J&te72S!0RRlOO{5#&PV!27rjiW zltQduundU)J2}p_?^)%7Evs#G$C1b{-XIcpg17&zcsSiKTf1UXu;(d=?>Hf(9_)>*{&G4nB zORGfnvS?5*K?l?IyGI+RIBA2V9QT7@2*s@T5>09}@=E>uS-6{J@Hse*`cH zx>rJ&NLQ_YEj77~X&oOgi0C&MH8kCx6Gf{I(-}95U{n1XzANG@=P{^q|8*WYjid$U z3ssRHpr%wg^MJ1T7g{803LS|bV|Z#~o%?_20Mz-IOErwA^~6nw#>_@lsWrqs<6ds7 zh^wEVM&>wg)$|VL(Z&Ot1rw|mmhTP5+Dd6OSw6o0Za~5$nah_X#SyfCHQ~J2Lgywo zT>fynOKVWiI%z{1{#z&WDeMfEM?*tgMqj7ejbxA^ZIxXH7V-awljtZ7@qF;HP@Dfm zki?qkJ57{*ttY=DRyGZETZ{EZ64^AjXvo*WVdS7RgZ}k$1UJNcm#`*_^TeM!znK}{ z2vTxU1SwM?lh4zEk;-?Dbt5t%Ex?Dx*T0F@l!$%^z>GC>{p+)}PY6l_ojHr{@r_YL z^pc0K!9MJBE2O<}-d?w3)!@;NKM)i$&i6rPqtqQ7KSIEfUk#|4P*%q zY1j?13RtS){-|_Zk+Dof_)tGG{l_C=m{!*A0D#0i)qF`Jq|%hhL7%BJ>1hzqQ|iF8 zhMJ*N9*#N3^Ir2`I=G$#_X?5=?x4~Ixgf!dQt*o^KvqrfCgF|ndS0f5?NOO~e!7w_ zz=bbN>wp4O&AW%Ka9`#u0xj7LKupf8MQ%_H-NfaS66H}FtQ9HDG6&ELVXe9TDE`wF zTx9K)#`dyKPSn>BXayadCTNEe2d%E3mdJorMFM@t>$Y|*<0xX2_QOKkV4c{hrokTS zx5nlmx*&lIySPP{RScE-VR&Y;?x{0xU0)k&o93;d*4`ikO95$b)K<7S>?i*~Zr6zJ z0DUw7`s^qkaBFKUiW2rlSwi)lkQ~On9Te0$xbH|zAfn1c+|1VTOEi3jCc`}u({qc- zY&h78fH}-^0?Z)ki0|m@3X(F!YlTN43oT(I+sHxtws4~V@(ne9dR#LuiHDN1y0B!HHipr(%LjRff?0fv22TLeLK^Q>#KH}~MR=TWb|*Da6z!Zg zZN(`EEHFCBV6uuXG;~XTLIE%sjOW^1n^EHIc%mt+&3d4N2kr0n{4_L_$sG^O*++%Z z)I8@fiy!o{=Zlp>s+}h{_(f;PWLR2=CaqqbaFJaz+qb5?R#B^e&m{G=;oJa(8I$m1 z`p}IJnRRsBQlt~5@dih0p2+-BkAZzR^7xp7z> z4P_U()Y$8XZpGS`RUfx`BNEqQnO745N=2ji;8(h7W*bfonXOcDk+m+y+>mN3&`;bM!PV#m+h-< zt^4qw8W7gBAxd9J=q{%ieaFbI(AQW%Pb9qyx&laszW4kp z+cJV{LJnER>VKhCTSm&8L z6?!+NmKLxVHfxeb3|ByQew%@=ZvueY^W05!et9<}Pbgd^PkUR%dne<1(#TBnS#*OM zZ@mZFNWu5tqg%9248%W4h<7Pr4134Sw1#8xPYTc$JG@3?d)F0Z`n)X_!*r*cME*wb1|q8j_11r%!ww_mlMkmDHFpmw98t5*JHEOB?RcR?D{N)-jGio zRdO4g0ZCcckN*1N#R^w)>m~`W!GmUE_g<8Z)wYU41L2ag&m+_6yXd`sOLCymqp7Z9 z(IqffF?6dX!>)r*W~>bqng8PvF(!B1w&t9)`~CmZ`cu0zM)ROEe=r@(n08PY+&Eol z{2!~qEPb=DWjIEBxqrlB0U>OFRE$kpGbbc!?P|V`wWfIpzR8PHcF~@u{0;}nShYdI zf2}b93+!H_U5n+W{~W7oJRa|^1k`mtx5okQ@Z1E-{{Jvq$Q#7j*&3w2 zO*LcqXQ8v}+lte|McMWt=KhA0DbJ^}$|koT?75wY@dPgkv>@EMli^c zD~O7}@5Yle^~luzDpsD6V1VXKQqyzxd-9NY)m$@q2J2rYhGeKi=pweUcKRFp3JkS% ze(E7Lzz5BtEhrFqZ=2<^PAlQ6JXamc9ull zywtv88k^5^x*>E%xTK}Uk>M?DLwwEas9gBgGu`VZbVm#WA_<2E<4&8V+1*df*|;@H z1Q5>7Q?jdZOpZNbZzG8806XEPY*ClKcZHNJrz--hEqlGReEKk}J}-N{gr|u3G&iQb zWO@W%x%mvl%?C7H7-f`)wDPntK=eJq%a6U`06bb z&!jY;v=7-p(GeH#Nrq@&$l6;WqHI5)HNJ%Lr?+i1rsVG3wRK8@=_{}BDOQI4sR7`-kmJ*q~5YY@X;VxisTUi^Ntd7`#};GQ+0Z0h1cQd_#K zGuyzo?TIwC4-3ff1LNEyDflvD{lz5d#&Gk1(z!YiMCKt2dP<#pNIPUhB?z*1X?Sbw zSgg`EK!0C!rKiJgko7s&X1y-u!$G7TBwye1t+6OF_@XYmDfeT#RA;|XtD0kq+Oj6k zN`HovWHJdjQj5WvnDOOvQe$5r4Smv7Pq)3py)y_S+5$rFA! z6zcY;%FGfhu4nkLQ~+){A?~U*gaS?joQSO4(DXgPR$G+E{PQxGC9S-WeX~Va{Ge82 zNL%0FvERr3yN00y*`i>u4O{Wyl~XcR7aD047MR{@d4zbvAh{77xV?5~k`f#+AC?U7X4NS{C zyQS>KrJ6CPC2kfkqdp{|4Umbo9Cd_68l|afyygqSx8v?10PRL4m7h3J{cPYM`u1u& z1*!AkB}R@|f4=l?L*Sk;5#Lq)4?`mE8#MtGgTA5}6z++_S)WZJ@|IRkYY2^5GN1FE zf?sVkP8UHp0Tew*C>OO3ON|M}{C`3>fQ498Rr%MNoWjPg88<1&!{>PN$DLi#jfw;H z&7)iIZj7u?w#Q>7Vor0}Wm<ApLa#x!&hW_eB?#o7V0bGKAFI8Hh$>Pby*A{z$#*LZW!a^Ce-Yw{TQXV#kGswqJa2Vo zca}qW!gd3%f1z-G>M>5s000ZEw<`5u%CV!)#v_lYf#xX-@c#mf@mSIClF>Z1!|xj& zcg#U#(oIPP<-}#YFvBgrzVGERf7GzBFNbGfCBG39PCUprQ_t$a?7-4lSTY0 zp>cNg!a(e5J2Hon=`!SAWs(6-SeNDQGuU&$01w~}C#~Zw#ZBg0^GixgJ^J{0d0ui5 zCS_5#+f<#$tr*}-Lgh3r`@07L=0a*xUnR06nxei=2rg{?Sa|^{3Xx#oDk+25UsduG zcvd}6&n^# zwt|D5%*q|@`~%IBBFBy+m7d-i>F4?4Y4K;4p;_Kr(5H3~V)~7EA$L|u2Wm5%`-7eoe;!YTylRqLKYFt|hlz2D7rk#rxKb30^6NHtuo=xkaW zl+9c(DA@0?ADpgytW6kqaSqEr23QwyIt2QfF74`s;fSAq<{>-2;;8-ks@Jk}pnC1WG$;(Bs^gYNB8O0z1>tKj0~i5a>(38VC%?g&XwLIUYjfd7n|u^-a)p zycFrGIz^Fe0hGm&U%s!AX@dZnA*u_w?|)}MGQX0qRZsX^{LX$o-@8A5 zKltzVJ9)Qw@!wp(ZQuHDexHAvzk+^bzxuz{U+_n5m3MFaC;aw2*lzOgf3JSSUqGK1 zFZ6GFyME(-_kX+>=s)gn`ig%-zaPI%-|~0h^WcyAm-sh*gTLb5ZGY%P)VFD}f4P5-eQAxbe0M^pdKh*7YmDUFLW?6269mofQQ&{EUkLxW zxrpLex+P{+m5PBQ1IPY-NOyyiY%{Wzu%S;Eey6MGWKk^_ERMEqw;VA4#zVa1NUs3x z7JPjOw6kc7TG1A^=h>@J`#L+{^{fZ&7o48V2EN;;C&3c}$P}fH0SZEO{Lu%jGj97e(WcZCHazEq1S1m4Y&0`>VP`PweLIe8uJ_J48<*@+Pz=GSdE|A??h&psD` zsT4Mk;8Da!pKj;m7vnta(*g)=J0mUf{JMD?>=A@{a{8*7sEb{!kz@2DWViC6|0B_#Y3{84)sqXUh>Ksxq1NJ(bw-EQgwe>fZsKwNURwoz+=VUN;XyHd zgLWR~|Cg2Q1`&6O{I8{v$j_;ct#~e#Iiq?nFBzZwRONa&la=0xRh?iHw7Nc_DO)!%p!4IwIL2(U!7nOwZ#_ zEvf2={7UsnW`~oZJXX%WpqoTHqW>0EQjBfnaJ8Sv$`iurFXbBp{~oV=rxgebh90%> z5nf|OMj|Qnr5e&!Yf(Vj8|F4zA^diAe_q+)qyU-MUL(htrFytSrfQIwbAv*>zg;K> zbs#>#AuhmZ64lgER_EfYz-A@vSTTDn^?2GQ0wx7lIo0@JK_m-0itGRP}73gfR(k_*2YXa}%o z`}D%qg-=Jt<>SyrlJb@;(9{mZtN+=WG840iiy)zkqexU-)ec~BVYC_*!3K}U6k6T+ zfx6Pck{(!WMv~ce7{Kp*enoS5I#aaW3RgsMtJ;$du}V8m7;LOA%d?)yCZcH_ z@ z&G!yyzgplhe$>V&(Sdp1INNnMvugD$XgE{qF2$S)L{CAC-jLNA648C_z;WUlv^ z{#3`Ckkxs)o8G7{^Ya2>uwgRF zXiIY5M^Q@$mES+}<0Eh=-P+bzqls-oj(P%;bJG;9*3xDG`(9paw214-7!1>Knf&j{ zXI2qRorF4HMJNATFaTYWV@`(+GAXZMDr~Yi>CjRyaWca?3UQpkeRRBoQ|Dx9kvn|R z2S`%X#|v+`xG$vhsP5|B}vx;6LEhdfHa0yTeLY1|0$sLvG;p#)tGz)tqsa|TC?bi6Gx9drZK9`i_tc^5@L8k z8I^hWa-$1wD)@ijf(dRTM_FU6(02YHHl2`!QPG%2HByEn>BI)dj;QlW!`ahXa>mMFK9riaO z+o5RpF1YHIQpg-QA8M0b{y!G}Z(GV^1_KTfG`PPaB->$@jz_tqjGi&LYN}k@ps@VE zi2Z*s@!uS6bJ1o9ev5An_F+Q*)A0YT1mM*C0hU-ww#z9ro#Gf(rH|6=u_a)y0xm%< zzCca2og8M5in0Ik3jDYH`_~Qr!kBMLCfBPjr@zgPtA9j;Vvgqjn|A-p@C4F5OQk~H z*q@=g8%#%H9Bp49M;Ib3wr07N^{@#4KL>8-CdI-hiAAUKjg9&;tIh<$MTz5jRs8jY zgP=$d%(z@JpPA31;oB}6y>pgkor&uV$;Y2gPI&?(0c$?z+e@id!8*PY8Gts32s^Zw zRIZ0`-sAf}@Edp3CtOjT<>E{KSY=Ikmx=LhEY^qsrtWDe@?*2}Fwg4up6;{1Gut_X z=eJeN+?Eb~w-DJ3Fu2|}S;%ZxEIlfU@{v3Hf4htSsr&{2fM1&sx^H{$4~+fY7(GBb z+1b#n_4g1+3{@mi6LKf0o7#1(v3fa4ysSVA-+Ad2cEAsOfi5+$UT`*G9H3I!3csK* z+$0C>?R^P~h^s0uFf zAF7qC`?+bP#@dt9pg8MkBh;QsARBB>rn}6kK!u^IL(TnZvw1bDAYKWwliB5{0;rFM z?R`-${;vvL`gRF+cN#Udx7(OPgRnJkwVuv>#2ImRA)@4n=lCD9?z^l`v=3g(Z;n)S zT0`^*41n{$nc$x60QevAPBHf8f-9->MBwy-OBD2Vr*APfs^H}c7E=L$|Bw)LcR!fw zaF7ZOy>tW63IrSE0WgsFC_I5fp1N5g&W)}X?d3$**q?dJ&luZ-K0Wq7jz+1oW~vRK zKJmqMMR%B15pN5TnDrBp`LJg*v1&r-i`ztf5_FEY;^_>lHOd zSq+wP@XA7|?|0@wB@U~%cS1TwIKgu0rmBN0x}Gs;n5xDLn!J>TAoz%tSwFt79>BMm zQ}dR1x`&tiiB`6l9rDh`-@XxQk-Xl}?}EX;#@K3V!S>3VUmhSQ0G$@&b5<72VhQMi ziX`pJOzfQ2nv5+oA!+l#*&W-A?B}J)@3|LS<5B&;$RYn2EsTmM-SBtalMJals)_2K zlt9Y?sACg6);NR_PPp&}xUq$(5u_E36D7k!YN^0{Mbq_`yeT+>NeD$ot5%q9>^X5# zo9);~PR&=Rc>Xvj#Y4&3hyhIYH{aTi;h(OD%JbnoB8BF2m-Cc8q%-C-WlKIG%*#9d&d_^8}Sc3RW``H`nW#%YwI zfhum*`k^j`6Ez5MF!Ex6lMnhSXAzM@OG^I}_FpMX#gN}@Nirr-$^wRcTx@nh@(7BL zJ4!qxZax7;>mE~}Q?(nt@oo+WcY5jrmhek`m|+JMh-K1dcDe-waCkjC<|WG8#j{zK z-Gp#H5s@*~a@rEN&1lzuN4{#IVL0=7lNgZ_Jyhy{9o4jfW?C&H2ZYr@ym%Pk@-ZMx z2|H0tN~7BS;bsDE}+_i0Avrc5Qd3xunG$in{+{uAb4*HyukynfA7 zmXOQE`G7D3Hn^WScr~CVgNs0<9Pct8p%B(Ac;MRA&RTA%`vH{jrl_RWbm}m(J;nmv z%D!#B3-d=S;Q_K3pZ!x!F|AS8@ijA*XPgMUgNRYOuq{B@MJhfT{~RL^*YkhGA<2j$`_djS98`?T7A4JdBQ=XQtC(LH zkBT@Yz8!nYCcZ8pTVcN@;Q3yJ%+RPuhEAFFyZvX}lkdUy`I;OD-PDKT6phhVkkgX& zxcN+ax^4ye$nhrGJ*QFeq3o=@hG)5t(2~whU0PTfQQ->KfiJm=yk_c0O)(G9MPM6d zckjo&o4zd)*8oA*frCse!%Y%ouSLS;{-aDHt|}`Ha2! zIUYq+E3pU7&vZ&kSf?H+C+e%z<*W&c$ws14bXf`6;m)?w&h!R-6AZ~G;Cg*~>pP&q zTrsn4LZ65%VtDx(awc{o#+mdN5NP>bJvVyXdpfkr#fTLI_mr22LOgF#SzA5MfhDW` zx`RiS00}0vrG1-lVE5kA^Zhlb($+wz*M{H><+^ji5XSaMgiLV@2H~7f8oH#G(k#`$ zcA{^42pO=zf4fl^Rg^EA?fg1XW?Nu_^6p}9O}}enyB)yaSnLuGBbjp8$c_bY9!LEr z#@tewPIuMm#YmX)vuM}r3Z)9s;$Iz)UvyKW`=Rlr{kSH5_ z2)NfC82~|HB`d8I)btxh8KcC)v7Jj0?JgCG)0T1D`UK!gs%0+FidH*DY3I;*`^}4f z0m~K&+;~~}SW+GvF`r)wFR}HN5}WjASv3*I;NR`}7T25SoDEAG-CgEuEe~X8hsTbZ zpM^9UeA!mE@Jxxd)g2NM`W(v&2j;a^fh$VKK3opP9*=tvPU)iSR)2;rpgWQ0M4%gF zz&Mtjo2sZWhlyUzr=|WLyAOop!|%T3Nt$szu)Rq*r^(l45_5bIWt|77v^tuI**<&k zx=%EwN86&4P&O;`yx%KLm5P)k{ow|(PR!xb>#4UN*W=> z*G*FPxPy3=P3A`u;aq~YT9i1o6P(zG$$GuxVhXg0>e@$`73NPOVo7S13SajU!-sfFR!ak#ykJwEr?j?Kyd z$8jL>9~3Js&d4Ax3bc5qRfI_QdT8~@v8Os+b0|MV(oGe;M8U}fmuTLC?(%}ss#ln? z8&4sbe9~NDraabj43+tjEq)d*(Km2Uyr|$y+p5v(Ct5j#%6)lq^T-J*#PQF3DIgfl zDBt~V5F>px%`=xM@@o1i6FIsz{;}-fTz>sbCvg%jJx|F+N}NAgvDfPbd54_n3O_Pk z(I&{4@{U&+mch(cy`G%4v|k9=!|xrjNiqm)=2S~9U`SgfK`QJLb+Puq2j6Ni_0F1l zA5^UpqSjl&AFwGn%Aa*GRMZx$6qvMnUI9tu{FRI}@rVYS*EmKgoaLGOvo~Sa>iN!> z;c(SR*KaE#)Q}8Q^^O4`j8dYwZ%DsZo(Bf>(l)bVy!Y<;biVvXd_;rvolhFUG9Sp8Smo2Gmu1DB^ zRR`*2;^gGYZrpdLzKQmGC~0H1L5Y$mi|7(=im9G8K~4Y^EysN3Ku)%Yp0}Rdy~gGs z4Z1n00fAD7>x8dSX6YPOZ_JVjs&7AeWmcn3lem4MJQ_D^a{1_B8%meP**@H*i*1o% ztTqF{l98g=0!Sy(XnDSBQ~tS%Kez<*0;YBdIL(ts+aBG8a6;vD8ORAOthQZJk5p*d z!kop=QRj%7b8N_yAer%Uz;|agn+^f}Na>trSx7`#p92?_C|#d!Y7B~~mv-1XDJeZaz;T`#k1kS+-6%1h^>$7gDyNa;uLH{Dk$!Y5N&$OhFTz&Gytsyy{RXe zV;0oP-vJmJ=+AoZFc2bc!^}{>6ml+)m@mRuPcp|Z>##rG1-(-HuaOSdg>^?)HPyc| zh@!QZp3^Ht*Tyn65qgGz)<$ziG-Pc%%FcaS*i9Yxdv$y0WYsu?SAtl2<8Y z{0*+UD$4{bfZ(Ss!IG@=D$(YfStVUeXy?B!)v=KAB zAbFB0yK_fGb#;*31`rpbNc%uYsG(U#cu#Q~u|=C4+FJE)vO~>iDfs)-h>yX#5f2G4 zMdl0)KM46kuHdyU3Q1U07@BaJ_sj$WtjHh$NzBwMS$!P3{HC?xu)ntBwy05L!AFL!Qq@D&nOj_hq z4i!GOEElM{Oi|I+!^kqz29XooVkLhCG1=cwr7jS49IfZ=h;MQEs_EZzb8g}pa}GC# z!W+-)EqOoo7*s@Q_ygSHNfoBx{A+&^>fnIW7XYN?Z-k7V2zO|yPDcA&bWFgrcmT}W z;2(AH9WXJmzUVAEub&bdNTKB6Qh*yAd@=AkHyGBUSPu9bnxa5yQ+`MaE@ga(`38@TmFcg8eEM@Vd=RNW>O(N5_8g1Hzx4VkJ zi>{98XarpIwJZAc@#!w;Ici+B^|L+_@wmx-Z?L0Cq1c~lrSc7u4*mif$gZ~fk|f9| z760s5JD_D{UbX6XcR1^-DxvXiqDMU(DU=p5Fr0V%=CdOaN)yuln-G*@#EVo3p{d%C zI9BrGKX2xZG7c#=^@2h2l!2nWLQwK?>E$j_+?@SHMJ>&!?##gzef>VprS9kcIAN`*bpUoq(fL`tCbvEZ3D-W*EQe-bP zf)Sl6n_$W-BNr?4LQVbmI`?|OznTo{clvV=T|a0$k#{B&AWDGB&sjG|fb@@puvgPW zsf*+HyDD=e384s zws~;opw30M6v6wIoCCn4{y%WFm zopQn8P{tEWPUb{h?)_@?+YnLZ9r@%=^~Bxdp~7l(uo$0#g`6sY)_xFwUOMtuE!8uu z$wmvn7!A&!q>dxhI(~cyH|_h$^yvu~M+@wB$H3IvvWO;c8%*JiN>1)k^Z)*uzGT$k zbF*t}w8{WGzDw)%_Uq~WxHAth5z}_K;8;Ply6Y=yz3ZqelV6wfxv<>WDHV;w64y}- z_~gyGoJs@BC?=^b1;0Y{N84_j$6pohJS<>OYBtDKJ|34)x8@GApd zcwmZkK(zsw(Hr(Ite*l21eNkieIF6&=58&NLI>zwZuL0%g5$RoFxCq&Os=(6N8_d; zVr(<{#aEg(U6jj{vUG*jcy?0vLB4bcMAUs7|I*LASH|;uCcakZ5+L70mo#mTSmWX= zT>;QTLHcpyVStvh;)KKK%<%ppNV}9_VXd5LU5Y>=s9rO?@a9+rLj$FeuX&^^Tyk%k zo0fU(=(tGaP`Fx*lmq+;pX)|{qzXh3A<3R6m=t~Sz4Fwx%Gquc4b0q}V3FkgHQE;B zv3c~OKHc6&SeDbFm6m5GH;}Ev7M&pKervmCwPX7gT1i17r@Q4aUHBP`NrBD6AE#){htB@4un3}|mP4skeS zm;#q7J%Afw@Wx-6IHQd~OB=dvpZXF1&|102FlckC!IGre-sgAvqvgd9#Ja`_R+CaF z(|=iMSfonRD`icKrRLzHCbeOz7Dc~yCn+4$n2*EN1SLK?t(4lUbxjd#~M|1L@HC<>Ys8+~T7f}Qz1Qc!aF zUJ_|LGC#5>+eUIb?Ll}p=ANe{yC}mBr8s3-cIlNyijiNU28m>4+5PFNP%OeVr)v@@ z9xHG7=5?kkz{FM)@Spw|L`&D zuOUT?k%lQT8eLu&!Af;ACxqJus+2lLjm)lFbW+sry8YL76kbC(xw8>VrNQm_mzb#8 zL)#)| zq$1)EOapIQq8TemPggYfG{@78(m<~YmhtFIZKb~$$90gY9>*HY>1*7hDf;-tLE4Zo z9ff%bicWX^TC02B950ZYfc4{__nw4os)O$q`=In_XVCH;&vNwe#Hlu%Gt(qE4q&&_rzl`NpA9FQN~s$-B9MjZZo~-n zIVMNC_5wCW_CPEPP$6{Rz^6c)fe;p_?R6(} z+dZsGA=0>00i_yJHu|aVVR#n@*R~aLbXKLvF_LxB&bXsTtN~itox}fJ#Qeo}&%bf* z!|G)AKF6DoUv5fMW38_cUD<|x?ojOQ$5GHi>$fgA=r3^c0akeW^gM^81nJ3^^MwC+ zfcYjlS1~=$Qrvtf-h9DmY@LS()yM!Y+4jollfZu>}YH0KC_{^?n}y<>lj zV62Yd{Mls=q2zOJ1A!s*WB4km@Dm>M$nL9Bt}uW`nrgcX+BaHtPbFj68$hj#Zu&;b zfwladE$HGF;Yg9O{7>YeNgXMqC1$5yLVIkLo2V6p>xO0V;7L;HHX^<7%aIl^0uw!L z3>c^(f{~XmNNTWIdE?6;KeRIaS$P2|q{#^8yrjFuf_Bl@uEul-P3N5<*NcF{pI~-s z;}sNI15K&sPqDEKe5<_3B&d77cwGFBdYEs7WAlU3157N^|pL?D)ej?l*nH;MAOOd$-8um6if@(= zm3It}tG+>(Qwx58mODe%g z1*I_N3Z?NiV=pn|_!3e`JI?CB9udjn3cfjv#xAT&Y|XQIGn)KGWYgYjB%PFWvhQ1e zQ$a8tayJq@5>yi*O$i38yD0Nbqo;$W6d3BY=gbU!d&b1hx+jNB&q5_A=x^6~X?zE_ zS5bkaEyxNf#%zkjPr>^*3LsPgC3er3q;pwUyD^f&!R=qSA?AnMjBni_kF9)EZVx1D zBKk}_o7KjgiRwQC1BD-ptEg;9y+NT(-`?XoijFl`!R2oN#M+TC~-pZ-} zObxA1G$9CTG}R(Pi0ZAb+w^CRE|vyf34+0Lfj+cILswYpae>bcv~UxHMU6CQBx(fN zeiK!yptXDV?UU+WcvIvfy{y-Op?b({nAu}Y(O#3 zxZQ-0jIUKMeWup_UC1fyY`5@)dETIG7&C9sHY4!NgK3c^7Y<_QzU0EE%3HJP>pA_y zmG?~YkCH=L;4pWTT|T?Md`7uCNKp18CVh0L{SuNde==-avaXS-Yj?(q?yI zLpeUQ^aLEP-xGJ0E4pDJ+OxHk|M!4GQ-h+W3Mwk>^~603<=K9Rg77Iw#3t-VwCWmyVbNy0THds z5}BfWPzt{X(NUkDGU<#;o^DG8HV!Ltnqyp$g=8fOrKZ5}zYHBBB1CbNe)yS53 zFZGq>SPd-|MU|Z?E96Og)Y#JKvzlN7zWtR}Ww^EmSK-aFF%8BlA2`)76MmMi9Mf9> z8y%LYT(X^swFrdW$#iH3s4V z_;b7;d<;jQlszzLZ4cEi671oL>QAJtMi~L>ah`>Gi#TDk_7t4C7{PRbnDvBMGdP{w zy_4FUwes6kN!-ovk}THtDuAJJZpIePjMG^)y;5kfyDA1bpfDLjNXm>k`1U7ggws?E z9&i2u&3zW)P~DW)cGc!E3Iu!}P7`}cflN^)?ROh#wVA})5~Z7u3rLOB`h_K+5oKFr z6B_b1(B$b#+Y|`$0QEOvC)cY|KQ~3X5E`VAIdm#!I?J59KGQ4fikmU-nZ+3Dcev=c zv|_=Z|05mYa(aeXK$alPlLf9)DoWvkUh=;OQI>1Gs-Xq^*!f%f=H7qrn{hTPM0!>>SOTHg;qZ1MsQOG^5Q|pfmilCuCIC}3 zG7V{IMuEtEp_^}AkbVn&YWcVqjWreNR&?qWB4=u0Jq#HoMp(lOCA)w{ToF%ZS6dCI zIaFf2(mtJTV)^yJpg^mM$ZY#x7q=xC=3vkMr4VHmDO2w#E~ULiG-| z5psPR#0HEOhjo--XnY$10EFA-bNun6x10gWz`x3QIQxc$1TPey3O)Tal)w}$QK{4d z99pUeF_+sX)HEJlyL%`xXvkP{#~hb$weXC{t)p%`a>bM-qO;MiFSZd6tKs;Y7o|<# z8q@nMS&r4icFg1X6{fMjZgxa6TyVP<*6y)`Zqarc%G^7dH-q#- z9T&*jvn)<4GVE}!T_y2^&Hoz?2nqoNQBA7P>ZZTGf0qooWq80`{XfM^6o*(3#5y0~E?jxSixWff z9p#BZ&=XEc7{I%htzg|neGd)f0*Kh4X|==q>6}V6GdW%KjZ|Y42jq_|r?Q7D@nH4M zngfUt#{DC(mU^nAkzCKfdb!x-E~8ldl9$SQz9#ilqtJzRL4NCo@rromeywwt5>E@f z$!g+Qz^b=^Kz{BmC3G6@bL33~4Qd`AA~g$t_@1+la|gZ(G3HehN`g3bqWbhop!s_q z8@AH51|wF-(%AQ?7eG%tBmgk_(Fg&D;qbtrWx!xcFuq!_ZD4czJBdx;ei{#`vHUY$~ZpUXH z+>XjkE?QMRe`Brdi&e;f1{A8!c{#9kP@eS3pmI?s#5l_n@@N8$R73X75g&4j5Qwf8 zrLb68u`Dh#HRD1da47HkZ|ASM)T{ZXv|$O+vEMyQB%r=4KR1zE+oy&8$T4A9$%{9$ z!TgiW+ih@rq$u%}&#DY7C9C;n7Qm}vg0#9}8^tr1bk#)rSyF`sz47kl&4^RDv;|VV zUT%t@R6#G!4Ij=c!qY4M`;{k}wd49L^Lh@cFFpTusT`6AL7*N;b~M$&yfH1g4E>p# z&H7L~_0vuAlB>MwWYt0xxE&29xX78FmUyKaEf<>GnnUH`;cyw&*Q2#g{*$^uPSsq) zm6vlt)z@;?7_gvJu;Ti0s+Wd~u?-)LY`%i?40~<$>*9VT-X7-z-M7iggAFDvNn?Q7 zQv6dx;!8)*#<}X<11*M~d)g%_+yRjAP@lB}dYb=T#w8Fdhyz_-Qj7^C{2c5xr39?YX08I?7Xi0NwrCmVi2$+ZrKiejB#qt!nL(pzgZxwC8@ihy?y>3R zSvKC|$$`w^Uo|ay(u1^`Ej2B+HvC{|q;L*wJHw1?lJX|7!8}fT$Q%>v98Z%G^)EMx z!a)QC6eM5=gEN}T{%W`{m#rhwV>`q3ZfOWU%qL6rU9R8(ASX0B^Oo?!j!Ddcia{59 zQfRX@o?!1K?t=A-^!ltui_>6J5(4X7(;?C7Au$Hcj;)roXul}LaGYmAG0KuYyd-Gh zc@PhN!+-WikS1ACkHMt7*%0iud#bw4A`J?9%yyH_R~#Oll-tBs;&AlDSbp{r7$re` zc!dg`{eX{)kCSR@Do$^LtM}Ow7Kxen18Sa(x!{L!g|e}Yn725n8CJXS7j&h;Y|X58 zozM~7iu@BFgncka-QQehAIAe0jPsx&my!LgE--jx9y0U`GQ}+$R}8PLZ#f}Zgxt`t zz`PcvtDyyjP|DXz*!^03ZCdLpu@Wjq%zeH;s3RJA9qpPh5b=C=bQP59Zg6n20Pe-@ z9+rF4c3itX%g4CQ=l@Zt-IJL^Mfp>JVej>hVVXAs5x9NG^ai|nRUJp_7$^LRynds^ z|0lHy_K|BV4WRa$qG|udjMv=ml5`Z@=(tkPZENU|B1eT|;ENXjm8w42$q&|sqSagQ z+hrpVeLKT{`kvb#f#fg4!UQt*Z-24i#M$Nm4tE&6%4(7K zv^LrF(|oTj*Y?#>$l4#A*?y|oA1sjkafx26PXI8X~rF| zVcR4|$b+khWYXIHZ3375ftj`n1DjP_bQj(@Cqv~|*a;32Q#UO%ea}kZ+-eJCuwW{zBEnt5_&oj}| zREBSSRBLWNpdW397nYfGRGcJAgSoyvCjbixn)FkHOPX=Dt8b{Tc4WZ_SW0EbJ`0H> z3jlFlc?d01zY!ElnZ5Y(Rm6Iheo8{MYyrS>2jmu}Xw{+ZzsdRRwh1u7UC3BkXQWRi zl@^R7tLujsJ`^!JRrV*UYEq|4W8-|+LE$1cVi&GEBAJPvxw=a@J(A4OZ2kTU1u@3U z7L{KyT4yJ;!hsRJ6*C1~U@%g}YU=^Q^nru3l5~sZQAYF2a6?^_uN-8=j%IB##J;oQ zi<+>cLq-R_%~3Whi#ldGhck;d=2$xw%}Jr5E%~Mhuos8N3@p)kr-EzLK=r*axOarI zxgmI5cB5M_>IoJ=&EF&_?hhbwIknbCKQP!8+n#wI!f`Js)2DqS&=)7=d^@;%l|?<= zf_90AgiDxZtOx}sbqB5jqVrx!vo>-vIr~wpGo!*`Q-1&eA5FLvud2##@J5CLi=-SS zfK74Nd}BE0cU?T^j9b6IHAg?X2 z4I7Uf!uM8!045o1+yt-H%4{e#*{T@c3jxwY!_ahmw@!xe5IfV)TGxw4gLlttevC96 zAQh3IbC-D_N7;W4x#NmvrgIg)|7k?vG~OmI{g-%<(|JkS0vicZ6-Tj-D1Jc}T|`A8 za2zA7*r}0fmyZQiW}XW*luYg?WdEP?*2pjWmf17EEYv#DF?ByXI-UR|If&&;Jf_2N zb6SZr)*r6&s(>C8_k`z#8P(n$4$-KRqw!J+ zyjgYdPFIw4o2xnZtHtdy4-6G#7_cgf;-$<2Q-dBhPD#!m0_oLEH%J7AdbaP&8#PRX zZ?>NnWf6c7*gqa_bw?Pc?)ptDezG9Mo&%&#@_?)z+Usz47-MERz zFdjFRAjJgw%WyKz@<3H9#UWfrPX2sbFH7eMs(#X_vx*p+wQi4KyqvsMS(Bi+%HQuP zV6a|QWMEhuKM>B!>%?CKzGqW_@w@{%QimhGiw3B5&R+W(Wn&= z?bgarMn#z^A9;kS>v7o^J?FniyDlPhsc=}CfQ2T5LLnVlp)4ozn@iz*MMq@WQ;(TW zAP&3jG~w^NYh_o2q|^Qe!}ExD@{6Pz`l@#tl)K!m+kHz8ke5Ikou5a~sgU#G=_$>8 z;N|>!n2sxIOW=EMM8#yTgtae}$K$in+I`ou#*a_(8iW4snj%dG*#sMKi|=D6Wfqc5 ze7p$+lxJXm4Ko?|5cF6?YCe(2EPzLUb!b{u2(39EkBXUl`cPTm-*Qy%)%VBxcNM-2 z=FPJ69ZYa*l|hpElWfG%n?9-zG*dYE=mv@L|5R76&__J;rfKZ`K6pP+SPYn(Xy(Y4 zmIw)?G zL{sdn)4gP*yWSnHQofsjzbYLJSe98y^b^Hg2?!eT1ThlU4>^h@YDv5)*ko;|O35~? zHHm$@x1VOol|e7IX+Sm)(^~Cu`zfe@5WZ8ISHAePTe>ctbu1SjOez|mHH)^C8!_4z zN__(}stpyhZhJ(5maO;T-mRk)pfzY(sZa$5v%&nMOwk(84g2YwSGh2YbEXuidbGb0 zD5@ZNmielY-!wgr1_%}D)q_(+-?Ym!a#DU8@3nM6l&WO+?u&bMo>sq+q-t!$Uqu(> z7lpUK!LaBTa+s}DFG!cfM~@J|)`ciK)oVO8k8rRf<{**(aNN!r(NFH* zfIBwKTROyC?NR*hSqrbX(tfoIRY6*I$oC$QF|CbWi^%+5yjjv8xcCRzn|vYM)h|(` z?R~xjUWUe_?J{hVZDTt($G6?zC@r@n8T$@+)8?EBZknb1L(}l}sb2R1vCI#1Iq;hm zNFLq~cl}I>jMSY)2T9=D(0W&2MmNc=YDap!>tHumAqhlmLee3(XhP??lels^U4}!O z*KOeV8DjL-GiqQ5s@r($o_2`Q!nC%4ZR8t$370)gi)8e<8~7_~YmM(Lj>zwE z)*Mh}#0qK_l5ua+vzyO#Va6o;mN0{88)W3^Er8z|uSGqHVJ9 z{Wi}A{>(vupUPnq#T?(F8=>`Q_HJ!BD5F5#B7=PY$+$X00l|pELvwS{$=_d5w<8@H zG3sH7;Wcfq(E(D>D|1`;D3s<0ZfJMaXqbRs+SUnY+N$Huv*G~D0`XW z&}*Hq`*b3W!wSjlCW6KN+i2s7L!J}AA(xD?_ag(_GOW?m$SH*tLav>(GsiY8SY}-f z0{Xnp4hy0^k#xJ-GP0IiU2Imr3Cb(XtBx72hgCEVw_B;$3@A&6P0!;Us+ z%gXMEO0g=I1XYrzP7lvF9P1CqLxyrG8uWGV%rQ7|{ zFKd~g6LT!jCIlK?5q6pznWvb}sXjY=y4!F;W!Zd#p~tCfM`b@3HpHZwaE4d~J^_NL z8{$jsdL)uIj(g-ZO9s#LK>iZo!ck<`BL11oR<0pp5FIfomco`<&p#$Sw@r|?5vs;z z*ovg1Auci@R_|86ARSgd%@OqL%`OqYNldsx$6i z93|*5I`ZK1YwosV0$^M?rd8cCw4QpE?!&V*{QUr89{0H`pz8qM((`pZ@i+MMb@nC9 z$0DKzj72!<of?xg1U60k{Nc7HraIjB@J-;T6r!?a2$9Pqa8tGz)M@R`fh?dbH}` za*wgC9I;jO;C17V2Byy4gxAuMAI(QlwBMVU3h*;D$gvc~OlD{W$cJ}xUUnDm15oD^ zCfcd(DoP|-GZ!=w5~q@fW(2@4bWCeW;Y-AvJpt}L$q77k*IC>SqbxhuWr^fa^$ljE zGByDOlp#73G}!Nxz9oOITO(5L`rvSP#@f&Y#kHT_-91xpSBN&QiD3icroiwNuHls* zO{_wR^{rryIT>XjI@$E49uFbm{@5_bk|`r?YL{K=ee~i_m{rSX5d}Z;*g*Rk`nb>J z3E^>!Yg;Y260lVX7PY(rJf}Qbv0dbdVN$%237PkrHcZ$&BZH8|Ne*s#wvxl~-ki_S z-rk%MRIUO#;JQ}RhhQ2UHIx4?hROM?sgVa!3hjRM;b512fbm{*Y+8IXEm1pjh1S>K z!zI|L3P!!EKEt61Y$WoOu0h5fp# zHO8#(4*+P}%U7w3HGlsUQF1}0{zEjdmqc-UJX%siv>zcid)nT@)|G$8!Mz?%9*!U4 zR#bx4#LurjI)vfpvi3esJA+v-?%jnt!@b8q!DL>2U)fW55y?KolDsFF$J1psV8cHJ zN41LbJBbbw!*jcT0wr}uj=BgY5M=8=6WR=769P~E&#eM*@|Ti(SSI8V5{W0(7kl`Lw}ECXesWx9prUxNKs^Y;yZ=9^l~Ait=Xu#YHWtkK!RTJ)D}~{t z(?IFHsr+-tEgDf^2miTC88NmTa7UU1wn<89C+G4t_=83q2xDaz>0{|esjQIhbCoU0 zUe|+1_97CT&H_0C<6z*ygz!9=-?Um*1H#?eRudX$=!ynMpUH+bdchIDLrs4}GZN{< zXNAy&2E3SJ#PMvX%73EBbN8KxeS&H$9^?4uv>jj(-!1Iemu!J>LXU!eC@w*MNd!{V z-^z%3P9^7nd{b+0bjIdI;nW8`Llu%)3|2@zj5!|id9-G)ddepcE*>LS#V7GT0jQ|x z(tvowNM(C<2Jz%0O%8&x-O~vvomkl@@3fH%HT{1DWD%S0$7=;SV4Pj_>acdyU1Fsq zdPI;h+Ii%B7zq7>!t&d(oQtswA$G_T$i}WfDuXv``r7_~K&M~40Ojp9md;$YEc!sm z5cE9x3f16%o8UTMJJSMM;k7PTPvR8+P6FVvcReN43pcW(aE;IraiT9CbGD2!EQ~b5w3ukE52PIM zH5G~z?>pJ`-Nq?57&!v0@JNLcwM|IE>Oc$6*lN|!3yXPqKcPNRO}FdAUDLmD0D?o* z-}!FOAJ@ZJILtXCYVHzaV9~ivVK7Dq18HAcHiyxqNjUvnJPZ)Ovk2vQPd@=nOJ<%i1l<0~8 z*6(|!F?JPmS*M)$bF|trh20&w`)J5KKTKn&XU5Fxk=HF?o{7da@o8N1-8hUqcPM<5 z9oZ0|<0fx1nw6;7?)UHla<6&utX!-OQV)fQB$%#!-8Bq1N16Y+rn0ZbSBZm;13PvI zk8`uT$7+`yJ{-{HEdm!;w_xQ8UD;EHR<6z^AL!-{7I=(%)SBl%KFiF{G&d~-8?Yc9 z&{wkOqT$OzR3RhYMmPqISep}ER-_3_yK0g>8*PC68btxk752hsApZ1J8&|4uk zUNz(J`7>(lHz`MJ%4?J0004v#dg>_liI3)Z;2$Sq@Q?Y_<1AR{F+RaaFHDSw9ChsX zdM5Q}^y-`sR;AzdPAK9zfZgM(CsZD<9Y|#IoY$`~1Q_5U6PVD@k*Z;YOxA)3Q1Nz! zlc?(n5QqTd>ElsVC_8CNkW$XXW0M^BR(4&St1ckd^?I4Ek8J{ym6=yapq%z8$Cjar zTN5{_)(8Lq0M{cwgu@AkF4le@8YWoj3Q%(pHM~%dNV_B9c)<#{lC~;cat3{JPoupd zkzdY;3|I_|9#zBsA2uk2EVKi?BVPnL{CT8cXb_ZLsV;6W8>8ROa+(~1P##tptnFRP zt!_hl91yENxCTkYhn4VVo1y=^Mi_GnPdc#Zt8*GeqngdM4F(!$D z)1~12{@uz)3)qmeQ9A~i!ab0pZrkDSU^zWFNElmod{1G+OOutP{VWpdgKaVf?mTa; z#GO3|-zT9eLD&1k@NJ9=K>82UVlA=DOIy0*ee-zV?$N-A#6_&FvS@g|;p6_-?r$ED z_m)5CjxWQMYZQSam5<5hzPoeh@+becM`yFHzYo}c9?rv8qX3S9HM5%fLR`_)mR)%i z4DMfmkh6*t^pqpXPEBB@EtTGzS!DloC+d^ogXpNp)cHYz#4!)-LUsO8-&`#6cLRRUGbvl4z&p`SA_aBC0qb zNE%T==)r~Oq_cl7HR-~a##KI5YzzQp5CU{iA6c1%F)BcgAroPLu) zDyXK&VVE9f;^{VUMD9h z$?x@@zBPs$GbO>Lj|2Y9L&~zj&)!m956HFCh1ld=B^o3+oIuko`I{ihR2pA*mLcug z+vc9A{A3p8Dic~;&zqFe9dkIgpXH^Q?Z*>4-RhREWWmXQ6nUc?WDU=>3SIh}Yhs`b zp7@G82hCjB&^&#@SRb_Uj>`$4Y&NABzI@RO22~p^Q4elkDI;VPE5WY8QEQfU+HYXE zmiD!3oc3Lc^GxcjK7K_CVpRci1m$dOQGXJxlc_KhE1Z}&W&i*H1+pfwe22rTJY3G~ z+7PZz${oFPOHpHM~t2NtB&Mnkk$10I4nvw4M4=YrsRj}+Fi^CHf}Rmr8_;89371MY_S)g@2Qk&M7fy-#K+mK5(EPs$|OF z72E61aB=m6y4^d$43HAru(I(zKmY&$ECp5e$Kv!T2E3ucAN%(CoAkk$#z_4_+qFZB z2@#KI5ai7}{)qAt0G3!^wm}XV!(k7;hP2-IJ(UuxnsM9IRC+et zzazG;Fd=gfb@2rq4S&8}`DN?h4O5Yt$GJFpsUyjee6N7yQ|do{=R`%rRLcoNqda$d z{dP&(M}dmdRIZK{%YZO;(Ah7?Va;u``kyzB7FKZU<@`Y?E1=Pm6+cm_RXsaH#e~a_ zLf$m(k7ecFf5#Zf(MAi1HG27Mpc)BfZ&14VtxUJs28bVO*xT<9kYD^tHJP^8W?bIVb!tFUG)14jtKq`>Zftz_nM#Cr|N2Io0Xd@n(s135WBS2P@ z7`|$#Jf)f8>bWBI6KAt9arhO|R`fD}AuaUczeMQ8VW4US;Dv#(&E6D$7LRNkOePUE z^vcY&2>%w|1n_ z>;w1tKF90$0000PHC^gmrK2Zh#(u#tf^h>+<%P~v$yX5~q3pi5kcruM(-yGELQNj2 zkqNR`2!(oCwvPCQ3=6k8lgu^c{OKpWJ$dy|g)89RMtY$ZcI-d7wwUk6Bq8(Mluo#@ zRFAvUd38|ZFK}Vn&ryV&AvD2o;7XfQfN3Rop0hZ^sLjvyKifr_oltrg89fYAaIAg?2q}R}l4yue z8#QT;uNig4?UDt2bnS}GjiG09bj>odKg6SmQ6Tz4*A8F*ue4~1{DhvfAR^G?Q#TqiU^G1YIB5pL!P+Iu$NYIY{MoVvaDX7TEW~%_ zAIW^({!v+H%m2FZK@Gxo+8#}J91MiRCL5epJIy}451AbiUd*u>#ixa|#st$`a(LSL z0Q=e^`xN;%G=rLDQZy37u2z=CoqT~mpxyxFl=D2t0XH9VqvHC$b2j-uyU%g4apXjuZKzuXb$_7#*(5lp^TDh( zXo>9!u6gfSPfE9<*u_%kD@}eiBQ+|6F%4r&o=9i}aG}5wCor$Gqrp3WI8a;q{=RGz z8)tlXm3@tZy65*Ip?d5K?H@2c83zUxl_i>2IBscLrv2oO2gk1Xne7;eRiP6=uB*0a zP_iu+FFQ>A#iWw(M*a9|jd10Z{CiNBiXb7Zkyb^b#i;?geHaZ(QGcf+`hIKJidrPc zkv?`R_-ufoThz2fLzt8>QtIe-k}W zHH-|4u*pU4z(?&;KOaC3UQP|{{y7|PXcjI&Y+WMX*g2fVlQ|U^X`n3KP`LME&*m5C zPuRiYk&_sR2S3`BNcsCvewQalOt1nezQCZWc;&VkoDIE_4rI2z?l9vhidxw17@>l> zqDSWCdVYF(H_ZhR>pKn#(dS$W?VeYrb{^3sZtlm$kF+ij2km4vFuqkiD}gEKlUflt z!pTBG?iGsX)I>~1*%2i0CiQto&jVz`v%KSbFG5N8zT&o)@P;aTNb?U-JbvEP98hZH zFMS+Eh6adxWlW7^xwEMs@vGS+J}TaYhA>`vo}F-=Jiw4+ZA4N}J|$k9AlIa@sYP>( zOtT8DTs7DhxjdgXAN9SXp#*NGdMOk0;B(XPpG7-J3ctU(%x}9a;-Y_3#dwGF+}t;^ zZN=4t3zIr^ixx|zS<@)r zMKsZH^<5Y!sDC1;Gd*wpa|gu8z3-6M5QnN1<91|B-*0E1>zk68&bZ{j3B>0}%E}yI z0j;hwoqBPppUq+4z+TYnt0sp^9@SidOc?p`hea;o-+}-D0%_wkU?`fM!+VyWVGbgN zDHVK7^q}U;cHry3#@{XoKc6IkzsUs#3-Z#Wk$?8|PIhu7f0tKzRl|qd?Lj@CmGNP( zz6t^k<;&=}Ki8o1Id#e3q_+(1`hZ07%o)n9u0!eRBhehOHZhf$x8p9vc{Bqod8_yj zxLA(+%NRv3)sM`q(e-if!tVw|-$P6Q5Yq58)jUH^SgR272ehNqKZo=hjb#`CLVWik z$exOl<0esmcfhs`G`^7===ZsVW=#}yRkU__QKe|ysw*PwS;bl+7E#nfmBv)(;im_| zKN+#0{^5`Y@g!1adn#C?gzBsBVWD2IWG#N*71%|+p4!@raj4Tlajvd!b2IhA`9Od> z##Bfk$yEL^>$R}~nsc?4M+*4@c}d@$SzjrGq}ey?ue{F6@3>OWTRp`29ikkubGa5^ zlS2%1RX8J-E6?&XI}6*L?h`l@Pc0i%M7Sh{x!d8B|421jp+hX`yP&(H&)xlhq+5@% zkt=D54jq&?;6Qsf?9j+mV8xMxX<{_w4afkS$jYzdDxpq #include -#define program_name__ "reveal" -#define program_version__ "v9.0.6" +#define program_name__ "reveal" +#define program_version__ "v9.0.7" #define program_help__ \ "Usage: " program_name__ " [OPTION]... [PATH]...\n" \ "Reveals information about entries in the file system.\n\n" \ @@ -46,33 +46,16 @@ " --unfollow-symlinks symlinks will not be followed.\n\n" \ "If none is used, the one marked as default will be considered.\n\n" \ "EXIT CODES\n" \ - "It will throw code 1 if an error happens and 0 otherwise.\n\n" \ + "It will throw exit code 1 if an error happens and 0 otherwise.\n\n" \ "SUPPORT\n" \ "Report issues, questions and suggestions through its issues page:\n" \ "." -#define is_expecting_path_bit__ (1 << 5) -#define is_following_symlinks_bit__ (1 << 6) -#define had_error_bit__ (1 << 7) -#define non_data_type_bits__ (is_expecting_path_bit__ | \ - is_following_symlinks_bit__ | had_error_bit__) -#define gigabyte_in_bytes__ 1e9 -#define megabyte_in_bytes__ 1e6 -#define kilobyte_in_bytes__ 1e3 -#define read_permission_character__ 'r' -#define write_permission_character__ 'w' -#define execute_permission_character__ 'x' -#define lack_permission_character__ '-' -#define is_last_argument__ (argument_index == total_of_arguments - 1) -#define Parse_Null_String__(text) (text ? text : "") -#define Parse_Size_Multiplier__(multiplier, multiplier_character) \ - size = metadata->st_size / (multiplier); \ - if ((int)size) { \ - printf("%.1f%cB\n", size, multiplier_character); \ - return; \ - } -#define Parse_Permission_Bit__(permission_bit, permission_bit_character) \ - putchar(metadata->st_mode & permission_bit ? permission_bit_character : \ - lack_permission_character__); +#define is_expecting_entry_path_bit__ (1 << 5) +#define is_following_symlinks_bit__ (1 << 6) +#define had_error_bit__ (1 << 7) +#define non_data_type_bits__ (is_expecting_entry_path_bit__ | \ + is_following_symlinks_bit__ | \ + had_error_bit__) #define Parse_Case__(value, action) \ case value: \ action; \ @@ -80,35 +63,59 @@ #define Parse_Return_Case__(value, action) \ case value: \ return (action); -#define Parse_Puts_Case__(value, text) Parse_Case__(value, puts(text);) +#define Parse_Puts_Case__(value, text) Parse_Case__(value, puts(text)) +#define Parse_Null_String__(text) (text ? text : "") +#define Parse_Size_Multiplier__(multiplier, multiplier_character) \ + size = metadata->st_size / (multiplier); \ + if ((int)size) \ + { \ + printf("%.1f%cB\n", size, multiplier_character); \ + return; \ + } #define Parse_Option__(option, action) \ - if (!strcmp("--" option, arguments[argument_index])) { \ + if (!strcmp("--" option, arguments[argument_index])) \ + { \ action; \ } #define Parse_Metadata_Option__(option, text) \ - Parse_Option__(option, puts(text); return(0)); -#define Parse_Data_Type_Option__(option, data_type) Parse_Option__( \ - option, \ - if (OPTIONS & is_expecting_path_bit__) { \ - Reveal(last_path); \ - } \ - OPTIONS = data_type | is_expecting_path_bit__ | \ - (OPTIONS & non_data_type_bits__); \ - if (is_last_argument__) { \ - Reveal(last_path); \ - } \ - continue; \ -) -#define Parse_Non_Data_Type_Option__(option, action) Parse_Option__( \ - option, \ - if (is_last_argument__) { \ - Reveal(last_path); \ - } \ - action; \ - continue; \ -) + Parse_Option__( \ + option, \ + puts(text); \ + return (0); \ + ) +#define Parse_Data_Type_Option__(option, data_type) \ + Parse_Option__( \ + option, \ + if (OPTIONS & is_expecting_entry_path_bit__) \ + { \ + Reveal_Entry(last_entry_path); \ + } \ + OPTIONS = data_type | is_expecting_entry_path_bit__ | \ + (OPTIONS & non_data_type_bits__); \ + if (is_last_argument) \ + { \ + Reveal_Entry(last_entry_path); \ + } \ + continue; \ + ) +#define Parse_Non_Data_Type_Option__(option, action) \ + Parse_Option__( \ + option, \ + if (is_last_argument) \ + { \ + Reveal_Entry(last_entry_path); \ + } \ + action; \ + continue; \ + ) -typedef enum { +typedef enum +{ + Return_Status__Success, + Return_Status__Error +} Return_Status; +typedef enum +{ Data_Type__Contents, Data_Type__Type, Data_Type__Size, @@ -122,176 +129,248 @@ typedef enum { Data_Type__Modified_Date } Data_Type; typedef const char* const String; +typedef const char* const* const Array_Of_String; typedef const struct stat* const Metadata; -uint8_t OPTIONS = is_following_symlinks_bit__; +static uint8_t Throw_Error(String description_split_0, + String description_split_1, + String description_split_2, String fix_suggestion); +static void Reveal_Type(Metadata metadata); +static void Reveal_Size(Metadata metadata); +static void Parse_Permission_Bit(Metadata metadata, uint16_t permission_bit, + const char permission_bit_character); +static void Reveal_Permissions(Metadata metadata); +static void Reveal_Octal_Permissions(Metadata metadata); +static uint8_t Reveal_User(Metadata metadata, String entry_path); +static uint8_t Reveal_Group(Metadata metadata, String entry_path); +static uint8_t Reveal_Modified_Date(Metadata metadata); +static uint8_t Reveal_File(String file_path); +static uint8_t Reveal_Directory(String directory_path); +static void Print_Unsigned(unsigned value); +static uint8_t Reveal_Entry(String entry_path); -void Print_Unsigned(unsigned value) { - printf("%u\n", value); -} +static uint8_t OPTIONS = is_following_symlinks_bit__; -uint8_t Throw_Error(String description_split_0, String description_split_1, - String description_split_2, String fix_suggestion) { +static uint8_t Throw_Error(String description_split_0, + String description_split_1, + String description_split_2, String fix_suggestion) +{ fprintf(stderr, "%s: %s%s%s\n%s%s", program_name__, Parse_Null_String__(description_split_0), Parse_Null_String__(description_split_1), Parse_Null_String__(description_split_2), Parse_Null_String__(fix_suggestion), fix_suggestion ? "\n" : ""); - return (1); + OPTIONS |= had_error_bit__; + return (Return_Status__Error); } -void Reveal_Type(Metadata metadata) { - switch (metadata->st_mode & S_IFMT) { +static void Reveal_Type(Metadata metadata) +{ + switch (metadata->st_mode & S_IFMT) + { Parse_Puts_Case__(S_IFREG, "regular"); Parse_Puts_Case__(S_IFDIR, "directory"); Parse_Puts_Case__(S_IFLNK, "symlink"); Parse_Puts_Case__(S_IFSOCK, "socket"); - Parse_Puts_Case__(S_IFIFO, "fifo"); - Parse_Puts_Case__(S_IFCHR, "character"); Parse_Puts_Case__(S_IFBLK, "block"); + Parse_Puts_Case__(S_IFCHR, "character"); default: puts("unknown"); } } -void Reveal_Size(Metadata metadata) { +static void Reveal_Size(Metadata metadata) +{ float size; - Parse_Size_Multiplier__(gigabyte_in_bytes__, 'G'); - Parse_Size_Multiplier__(megabyte_in_bytes__, 'M'); - Parse_Size_Multiplier__(kilobyte_in_bytes__, 'k'); + const float one_gigabyte_in_bytes = 1e9, + one_megabyte_in_bytes = 1e6, + one_kilobyte_in_bytes = 1e3; + Parse_Size_Multiplier__(one_gigabyte_in_bytes, 'G'); + Parse_Size_Multiplier__(one_megabyte_in_bytes, 'M'); + Parse_Size_Multiplier__(one_kilobyte_in_bytes, 'k'); printf("%ldB\n", metadata->st_size); } -void Reveal_Permissions(Metadata metadata) { - Parse_Permission_Bit__(S_IRUSR, read_permission_character__); - Parse_Permission_Bit__(S_IWUSR, write_permission_character__); - Parse_Permission_Bit__(S_IXUSR, execute_permission_character__); - Parse_Permission_Bit__(S_IRGRP, read_permission_character__); - Parse_Permission_Bit__(S_IWGRP, write_permission_character__); - Parse_Permission_Bit__(S_IXGRP, execute_permission_character__); - Parse_Permission_Bit__(S_IROTH, read_permission_character__); - Parse_Permission_Bit__(S_IWOTH, write_permission_character__); - Parse_Permission_Bit__(S_IXOTH, execute_permission_character__); +static void Parse_Permission_Bit(Metadata metadata, uint16_t permission_bit, + const char permission_bit_character) +{ + const char lack_permission_character = '-'; + putchar(metadata->st_mode & permission_bit ? permission_bit_character : + lack_permission_character); +} + +static void Reveal_Permissions(Metadata metadata) +{ + const char read_permission_character = 'r', + write_permission_character = 'w', + execute_permission_character = 'x'; + Parse_Permission_Bit(metadata, S_IRUSR, read_permission_character); + Parse_Permission_Bit(metadata, S_IWUSR, write_permission_character); + Parse_Permission_Bit(metadata, S_IXUSR, execute_permission_character); + Parse_Permission_Bit(metadata, S_IRGRP, read_permission_character); + Parse_Permission_Bit(metadata, S_IWGRP, write_permission_character); + Parse_Permission_Bit(metadata, S_IXGRP, execute_permission_character); + Parse_Permission_Bit(metadata, S_IROTH, read_permission_character); + Parse_Permission_Bit(metadata, S_IWOTH, write_permission_character); + Parse_Permission_Bit(metadata, S_IXOTH, execute_permission_character); putchar('\n'); } -void Reveal_Octal_Permissions(Metadata metadata) { +static void Reveal_Octal_Permissions(Metadata metadata) +{ printf("0%o\n", metadata->st_mode & (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH)); } -uint8_t Reveal_User(Metadata metadata, String path) { +static uint8_t Reveal_User(Metadata metadata, String entry_path) +{ const struct passwd* const user = getpwuid(metadata->st_uid); - if (!user) { - return (Throw_Error("can not discover user that owns \"", path, "\".", - "Ensure that the entry is not broken.")); + if (!user) + { + return (Throw_Error("can not discover user that owns the entry \"", + entry_path, "\".", "Ensure that the entry is not " + "dangling.")); } puts(user->pw_name); - return (0); + return (Return_Status__Success); } -uint8_t Reveal_Group(Metadata metadata, String path) { +static uint8_t Reveal_Group(Metadata metadata, String entry_path) +{ const struct group* const group = getgrgid(metadata->st_gid); - if (!group) { - return (Throw_Error("can not discover group that owns \"", path, "\".", - "Ensure that the entry is not broken.")); + if (!group) + { + return (Throw_Error("can not discover group that owns the entry \"", + entry_path, "\".", "Ensure that the entry is not " + "dangling.")); } puts(group->gr_name); - return (0); + return (Return_Status__Success); } -uint8_t Reveal_Modified_Date(Metadata metadata) { +static uint8_t Reveal_Modified_Date(Metadata metadata) +{ char modified_date[29]; if (!strftime(modified_date, sizeof(modified_date), "%a %b %d %T %Z %Y", - localtime(&metadata->st_mtime))) { + localtime(&metadata->st_mtime))) + { return (Throw_Error("overflowed buffer intended to store modified " "date.", NULL, NULL, NULL)); } puts(modified_date); - return (0); + return (Return_Status__Success); } -uint8_t Reveal_File(String path) { - FILE* const file = fopen(path, "r"); - if (!file) { - return (Throw_Error("can not open file \"", path, "\".", "Ensure that " - "you have permissions to read it.")); +static uint8_t Reveal_File(String file_path) +{ + String read_mode = "r"; + FILE* const file_stream = fopen(file_path, read_mode); + if (!file_stream) + { + return (Throw_Error("can not open the file \"", file_path, "\".", + "Ensure that you have permissions to read.")); } char character; - while ((character = fgetc(file)) != EOF) { + while ((character = fgetc(file_stream)) != EOF) + { putchar(character); } - fclose(file); - return (0); + fclose(file_stream); + return (Return_Status__Success); } -uint8_t Reveal_Directory(String path) { - DIR* const directory = opendir(path); - if (!directory) { - return (Throw_Error("can not open directory \"", path, "\".", "Ensure " - "that you have permissions to read it.")); +static uint8_t Reveal_Directory(String directory_path) +{ + DIR* const directory_stream = opendir(directory_path); + if (!directory_stream) + { + return (Throw_Error("can not open the directory \"", directory_path, + "\".", "Ensure that you have permissions to read " + "it.")); } - const struct dirent* entry; - while ((entry = readdir(directory))) { - if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) { - puts(entry->d_name); + const struct dirent* directory_entry; + while ((directory_entry = readdir(directory_stream))) + { + if (strcmp(directory_entry->d_name, ".") && + strcmp(directory_entry->d_name, "..")) + { + puts(directory_entry->d_name); } } - closedir(directory); - return (0); + closedir(directory_stream); + return (Return_Status__Success); +} + +static void Print_Unsigned(unsigned value) +{ + printf("%u\n", value); } -uint8_t Reveal(String path) { +static uint8_t Reveal_Entry(String entry_path) +{ struct stat metadata; - if (OPTIONS & is_following_symlinks_bit__ ? stat(path, &metadata) : - lstat(path, &metadata)) { - return (Throw_Error("the entry \"", path, "\" does not exists.", - "Ensure that you did not misspelled it.")); + if (OPTIONS & is_following_symlinks_bit__ ? stat(entry_path, &metadata) : + lstat(entry_path, &metadata)) + { + return (Throw_Error("can not find the entry \"", entry_path, "\".", + "Ensure that you did not misspelled its path.")); } - switch (OPTIONS & ~non_data_type_bits__) { + switch (OPTIONS & ~non_data_type_bits__) + { Parse_Case__(Data_Type__Type, Reveal_Type(&metadata)); Parse_Case__(Data_Type__Size, Reveal_Size(&metadata)); Parse_Case__(Data_Type__Byte_Size, printf("%ld\n", metadata.st_size)); Parse_Case__(Data_Type__Permissions, Reveal_Permissions(&metadata)); Parse_Case__(Data_Type__Octal_Permissions, Reveal_Octal_Permissions(&metadata)); - Parse_Return_Case__(Data_Type__User, Reveal_User(&metadata, path)); + Parse_Return_Case__(Data_Type__User, Reveal_User(&metadata, + entry_path)); Parse_Case__(Data_Type__User_Uid, Print_Unsigned(metadata.st_uid)); - Parse_Return_Case__(Data_Type__Group, Reveal_Group(&metadata, path)); + Parse_Return_Case__(Data_Type__Group, Reveal_Group(&metadata, + entry_path)); Parse_Case__(Data_Type__Group_Gid, Print_Unsigned(metadata.st_gid)); Parse_Return_Case__(Data_Type__Modified_Date, - Reveal_Modified_Date(&metadata)); + Reveal_Modified_Date(&metadata)) default: - switch (metadata.st_mode & S_IFMT) { - Parse_Return_Case__(S_IFREG, Reveal_File(path)); - Parse_Return_Case__(S_IFDIR, Reveal_Directory(path)); + switch (metadata.st_mode & S_IFMT) + { + Parse_Return_Case__(S_IFREG, Reveal_File(entry_path)); + Parse_Return_Case__(S_IFDIR, Reveal_Directory(entry_path)); Parse_Return_Case__(S_IFLNK, Throw_Error("can not reveal the contents of " - "symlink \"", path, "\".", "Did " - "you mean to use the " + "the symlink \"", entry_path, "\".", + "Try to use the " "\"--follow-symlinks\" option " - "before it?")); + "before it.")); default: - return (Throw_Error("can not reveal the contents of \"", path, - "\" due to its unreadable nature.", NULL)); + return (Throw_Error("can not reveal the contents of the entry \"", + entry_path, "\" due to its unreadable nature.", + NULL)); } } - return (0); + return (Return_Status__Success); } -int main(const int total_of_arguments, const char** arguments) { - if (total_of_arguments == 1) { - return (Reveal(".")); +int main(const int total_of_arguments, Array_Of_String arguments, + Array_Of_String __environment_variables) +{ + if (total_of_arguments == 1) + { + return (Reveal_Entry(".")); } for (int argument_index = 1; argument_index < total_of_arguments; - argument_index++) { - Parse_Metadata_Option__("version", program_version__); + argument_index++) + { Parse_Metadata_Option__("help", program_help__); + Parse_Metadata_Option__("version", program_version__); } - const char* last_path = "."; + const char* last_entry_path = "."; for (int argument_index = 1; argument_index < total_of_arguments; - argument_index++) { + argument_index++) + { + const uint8_t is_last_argument = argument_index == total_of_arguments - + 1; Parse_Data_Type_Option__("contents", Data_Type__Contents); Parse_Data_Type_Option__("type", Data_Type__Type); Parse_Data_Type_Option__("size", Data_Type__Size); @@ -308,11 +387,13 @@ int main(const int total_of_arguments, const char** arguments) { OPTIONS |= is_following_symlinks_bit__); Parse_Non_Data_Type_Option__("unfollow-symlinks", OPTIONS &= ~is_following_symlinks_bit__); - if (Reveal(arguments[argument_index])) { + String entry_path = arguments[argument_index]; + if (Reveal_Entry(entry_path)) + { continue; } - OPTIONS &= ~is_expecting_path_bit__; - last_path = arguments[argument_index]; + OPTIONS &= ~is_expecting_entry_path_bit__; + last_entry_path = entry_path; } return !!(OPTIONS & had_error_bit__); }