Basically a place that Chris can post solutions to problems so he can easily find them later https://cjhaas.com Just another WordPress site Tue, 09 Jul 2019 13:39:25 +0000 en-US hourly 1 https://wordpress.org/?v=5.2.2 Turn on spellcheck for any web page https://cjhaas.com/2019/07/09/turn-on-spellcheck-for-any-web-page/?utm_source=rss&utm_medium=rss&utm_campaign=turn-on-spellcheck-for-any-web-page https://cjhaas.com/2019/07/09/turn-on-spellcheck-for-any-web-page/#respond Tue, 09 Jul 2019 13:38:51 +0000 https://cjhaas.com/?p=930 Bring up the console and enter these two: document.body.contentEditable='true';document.body.spellcheck='true';

The post Turn on spellcheck for any web page appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
Bring up the console and enter these two:

document.body.contentEditable='true';document.body.spellcheck='true';

The post Turn on spellcheck for any web page appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
https://cjhaas.com/2019/07/09/turn-on-spellcheck-for-any-web-page/feed/ 0
MySQL DUAL table https://cjhaas.com/2019/05/21/mysql-dual-table/?utm_source=rss&utm_medium=rss&utm_campaign=mysql-dual-table https://cjhaas.com/2019/05/21/mysql-dual-table/#respond Tue, 21 May 2019 22:03:44 +0000 https://cjhaas.com/?p=925 Here’s code that should work on any MySQL server regardless of databases and tables: SELECT 1 FROM DUAL Apparently they copied it from Oracle.

The post MySQL DUAL table appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
Here’s code that should work on any MySQL server regardless of databases and tables:

SELECT 1 FROM DUAL

Apparently they copied it from Oracle.

The post MySQL DUAL table appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
https://cjhaas.com/2019/05/21/mysql-dual-table/feed/ 0
PHP Include Path Surprises https://cjhaas.com/2019/05/21/php-include-path-surprises/?utm_source=rss&utm_medium=rss&utm_campaign=php-include-path-surprises https://cjhaas.com/2019/05/21/php-include-path-surprises/#respond Tue, 21 May 2019 19:36:00 +0000 https://cjhaas.com/?p=916 While diagnosing a potential unconfirmed problem with a certain popular WordPress plugin I did something that every good developer should do every once in a while, which is to humble themselves and read the most basic and obvious documentation for the simplest parts of a language or framework that you know by heart. If you […]

The post PHP Include Path Surprises appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
While diagnosing a potential unconfirmed problem with a certain popular WordPress plugin I did something that every good developer should do every once in a while, which is to humble themselves and read the most basic and obvious documentation for the simplest parts of a language or framework that you know by heart.

If you aren’t a programmer, what I mean is the equivalent of reading a driver’s education book even though you’ve been driving for years or decades. And I don’t necessarily mean “what’s the yellow blinking arrow mean”, I mean basic like “how do I make the car go left?”

And when I say read, I don’t mean skim, I really mean thoroughly read. For instance, to make a car go left, you don’t “turn the wheel left” but instead, when going forward you turn the wheel counter/anti-clockwise! The top of the wheel goes left but the bottom of the wheel goes right. Stupid and obvious, I know. But how about when going backward. Puzzle that through, I think there’s actually a two-part answer there (for how most people use reverse at least). And then, just for fun, work through the scenario with a trailer (although I don’t think they teach that in driver’s ed).

Anyhow, back to code.

I’ve always known that there have been peculiarities about PHP’s include system, and I’ve always tried to use absolute paths just to avoid the peculiarities, but I never really dove into what the specific nuances were until this morning.

The below code is talking about include however it applies to require and require_once just the same.

From the documentation:

Files are included based on the file path given or, if none is given, the include_path specified. If the file isn’t found in the include_path, include will finally check in the calling script’s own directory and the current working directory before failing

That might be a “duh” moment for some people, and maybe you’ll stop reading this because you think I’m an idiot for writing about obvious stuff.

But here’s the problem: what do they mean by “file path” exactly? Going to the next paragraph in the documentation it says:

If a path is defined — whether absolute (starting with a drive letter or \ on Windows, or / on Unix/Linux systems) or relative to the current directory (starting with . or ..)

Do you get where I’m going yet?

Let’s look at a common bit of code:

require_once 'file.php';

The second quote above basically says that for the argument to this code to be considered path-based, the string must begin with either a slash (be absolute) or one or two periods (relative). If these neither of these two conditions are met, the current folder will not be looked at until after the include path is checked.

Read that again. Or let me say it again. If the argument to require/include doesn’t start with a slash or dot, multiple directories will be (potentially) scanned before the current directory.

Let’s make a quick proof-of-concept demo:

<?php

//Make a subfolder if it doesn't exist
if(!is_dir('sub_folder')){mkdir('sub_folder');}

//The PHP script that we'll write to each file, just echoing the script's directory
$contents = '<?php echo __DIR__ . "\n";';

//Create two files, one "here" and one in a sub folder
file_put_contents('sub_folder/file.php', $contents);
file_put_contents('file.php', $contents);

//Unless you have this file in your own path, these two should be the same
require 'file.php';
require './file.php';

//Change the current include path
set_include_path(__DIR__ . '/sub_folder');

//Re-run the exact same code as above, the first result is now different than the second!
require 'file.php';
require './file.php';

You can save that somewhere as runner.php and then run php -f runner.php from that directory.

What do you get when you run it? Here’s what I get:

/home/demo_user
/home/demo_user
/home/demo_user/sub_folder
/home/demo_user

Did you notice that third one? That’s the call to just require 'file.php' which, because it doesn’t start with a slash or a period, searched the include path first looking for a match, not the current folder!

Now, if you’re running in your own private code base and server, this might not be a big deal. The perf problems of this are probably very negligible, too, to the point that I don’t think you’d ever notice.

But if you’re creating code for other people to consume, you cannot guarantee that they don’t have an include path set, and you also cannot guarantee that they don’t have a file with your exact structure in that path. Probably not, or, hopefully not.

But the fix is actually easy. Just always use absolute or relative paths.

If you want a file in the current directory, just always prepend with ./. (However, scroll to the additional problem to see why this breaks, too!)

Or, better yet, in whatever you think should be your “entrance file(s)”, define a constant that holds the value of __DIR__ for that file and use that with concatenation for your files.

//In your entrance file
define('MY_APP_ROOT', __DIR__);

//In your other files
require_once MY_APP_ROOT . '/path/to/my/file.php';

That’s exactly what WordPress does.

Even better than all of this is to avoid require and include in general and always use composer to handle things on your behalf, but that’s not always an option.

A thought from a security perspective

As I write this, I’m thinking about a pretty interesting exploit that would be hard-ish to debug. First, you’d need to get code onto someone’s server and second, you’d need to get it into the execution path. We’ll wave our hand and pretend that’s done somehow. Once that happens, that code could set the include path to include its own code just by naming files the same as other code that gets included with a true path. This exploit code could even then re-require the legitimate code, effectively making it a trojan that no one notices (depending on what the payload does).

Once again, they’d need to have elevation to start with, but once in, this would be almost impossible to notice if the payload is silent enough, like logging passwords or something.

An additional problem

I’m going to take the initial code same I gave and tweak it a little bit. This time, we’re going to create an extra sub folder that has the same file.php with the exact same contents as the others. Also in that folder we’re going to create one additional file with the only job of running our main original code via require.

(Also, I’m using better ABS paths for creating things because with the jumping around for demo purposes things get weird otherwise. The actual spirit of the original demo is still here, just more specific.)

 <?php

//Save this file as runner.php

//We're going to use two sub folders
$FOLDER_ALPHA  = __DIR__ . '/sub_folder';
$FOLDER_RUNNER = __DIR__ . '/sub_runner';

//Make them if they don't exist
if(!is_dir($FOLDER_ALPHA)){mkdir($FOLDER_ALPHA);}
if(!is_dir($FOLDER_RUNNER)){mkdir($FOLDER_RUNNER);}

//The PHP script that we'll write to each file, just echo'ing the script's directory
$contents = '<?php echo __DIR__ . "\n";';

//Create three files, one "here" and one in each sub folder
file_put_contents("${FOLDER_ALPHA}/file.php",    $contents);
file_put_contents("${FOLDER_RUNNER}/file.php",   $contents);
file_put_contents('file.php',                    $contents);

$second_runner_contents = '<?php require dirname(__DIR__) . "/runner.php";';
file_put_contents("${FOLDER_RUNNER}/runner.php", $second_runner_contents);

//Unless you have this file in your own path, these two should be the same
require 'file.php';
require './file.php';

//Change the current include path
set_include_path($FOLDER_ALPHA);

//Re-run the exact same code as above, the first result is now different than the second!
require 'file.php';
require './file.php';

Now, from a command line where you saved the above run php -f runner.php and you’ll get something like:

/home/demo_user
/home/demo_user
/home/demo_user/sub_folder
/home/demo_user

No surprises, the previous code talked about this. From that same folder now run php -f sub_runner/runner.php and you’ll get:

/home/demo_user
/home/demo_user
/home/demo_user/sub_folder
/home/demo_user

Once again, still the same.

Lastly, we’re going to cd into our new inner folder and run that runner:

cd sub_runner/
php -f runner.php

Which now gives:

/home/demo_user/sub_runner
/home/demo_user/sub_runner
/home/demo_user/sub_folder
/home/demo_user/sub_runner

Wow, that’s new. We’re invoking almost the exact same code, just in a slightly different way. This time, we’re tripping a different rule from the include documentation (same as above, sub-setted below):

include will finally check in the calling script’s own directory and the current working directory before failing

I’m not actually sure the documentation is correct here, or, more specifically, I can’t get “calling scripts own directory” to be any different than “current working directory”. If someone can get me an actual working difference I’d love to see that.

Back to the results, because are working directory was changed, and because there was a new magic-named file in that folder, our main script used that file for the non-path version. But also notice that the explicitly relative paths also broke!

The path ./file.php isn’t relative to the file it is written in, it is relative to the current working directory! This basically means that the only absolutely safe method to include files is to always use the absolute path format, unless you have 100% absolute control over everything.

The post PHP Include Path Surprises appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
https://cjhaas.com/2019/05/21/php-include-path-surprises/feed/ 0
Drupal Status Color Codes https://cjhaas.com/2019/05/16/drupal-status-color-codes/?utm_source=rss&utm_medium=rss&utm_campaign=drupal-status-color-codes https://cjhaas.com/2019/05/16/drupal-status-color-codes/#respond Thu, 16 May 2019 18:51:13 +0000 https://cjhaas.com/?p=910 This has bugged me for a long time. If you visit any project issue page on Drupal.org you’ll find a bunch of different color-coded table rows. What the heck do the colors mean!!! It has always bothered me, but today I decided to finally dig in. This process wasn’t hard in any way, I just […]

The post Drupal Status Color Codes appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
This has bugged me for a long time. If you visit any project issue page on Drupal.org you’ll find a bunch of different color-coded table rows.

Example of multiple issues on Drupal.org's website for the Feeds module showing many different colored rows, where the specific usage of color for the row isn't obvious.

What the heck do the colors mean!!!

It has always bothered me, but today I decided to finally dig in. This process wasn’t hard in any way, I just really wanted a key somewhere to understand the rationality behind the order. Below I present that.

1Active#f9f9f9
13Needs work#ffece8
14Reviewed & tested by the community#f1ffe8
15Patch (to be ported)#f1ffe8
16Postponed (maintainer needs more info)#eff1fe
17Closed (outdated)#eff1fe
18Closed (cannot reproduce)#fddddd
2Fixed#d7ffd8
3Closed (duplicate)#eff1fe
4Postponed#eff1fe
5Closed (won’t fix)#eff1fe
6Closed (works as designed)#eff1fe
7Closed (fixed)#fddddd
8Needs review#ffffdd
  • Generally speaking, green-like status represent done or mostly done things
  • Yellow means a review is needed
  • Blue-ish/purple-ish is “not going to fix” for many different reasons
  • Red-ish is either fixed or confusingly, needs to be worked on (the latter has a different shade of red but it is hard to tell unless it is side-by-side with the former)
  • Grey is an active bug

To be clear, this isn’t a judgment of any of the statuses, this is just a key which I needed.

The post Drupal Status Color Codes appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
https://cjhaas.com/2019/05/16/drupal-status-color-codes/feed/ 0
You have requested a non-existent service “media_entity.cli” https://cjhaas.com/2019/05/07/you-have-requested-a-non-existent-service-media_entity-cli/?utm_source=rss&utm_medium=rss&utm_campaign=you-have-requested-a-non-existent-service-media_entity-cli https://cjhaas.com/2019/05/07/you-have-requested-a-non-existent-service-media_entity-cli/#respond Tue, 07 May 2019 18:58:42 +0000 https://cjhaas.com/?p=907 We upgraded a Drupal site to 8.7 that had used media_entity previously and we needed to get it converted over to core’s media module. We followed all of the directions but we kept running into the error: You have requested a non-existent service "media_entity.cli" After a bunch of trial and error we decided to just […]

The post You have requested a non-existent service “media_entity.cli” appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
We upgraded a Drupal site to 8.7 that had used media_entity previously and we needed to get it converted over to core’s media module. We followed all of the directions but we kept running into the error:

You have requested a non-existent service "media_entity.cli"

After a bunch of trial and error we decided to just hack the module by adding a test for the existence of the service before consuming it.

In the file media_entity/media_entity.drush.inc before the line the contains \Drupal::service('media_entity.cli') we added:

if(!\Drupal::hasService('media_entity.cli.cli')){
return TRUE;
}

The post You have requested a non-existent service “media_entity.cli” appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
https://cjhaas.com/2019/05/07/you-have-requested-a-non-existent-service-media_entity-cli/feed/ 0
WordPress CSV upload problems https://cjhaas.com/2018/12/18/wordpress-csv-upload-problems/?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-csv-upload-problems https://cjhaas.com/2018/12/18/wordpress-csv-upload-problems/#respond Tue, 18 Dec 2018 17:46:00 +0000 http://cjhaas.com/?p=899 The post WordPress CSV upload problems appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
Just an FYI, the WordPress 5.0.1/4.9.9 (and others back to 3.7.x) security update broke uploads for CSV files under certain circumstances.

The trac ticket is here and the workaround is here.

This note says that it will be addressed in 5.0.3 which will probably correlate to 4.9.10 (unless 5.0.2 also includes security updates).

The post WordPress CSV upload problems appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
https://cjhaas.com/2018/12/18/wordpress-csv-upload-problems/feed/ 0
I hate Font Awesome like I hate jQuery https://cjhaas.com/2018/12/07/i-hate-font-awesome-like-i-hate-jquery/?utm_source=rss&utm_medium=rss&utm_campaign=i-hate-font-awesome-like-i-hate-jquery Fri, 07 Dec 2018 18:20:41 +0000 http://cjhaas.com/?p=888 The post I hate Font Awesome like I hate jQuery appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
First off, if you don’t know me then you might be offended or enraged by that statement. Please, keep reading.

Font Awesome is a truly great collection of icons that can be easily deployed with a simple CSS or JS include. A ton of work has gone into it to make it as small as possible and they’ve even encouraged use of things like subresource integrity which is just great to see. Font Awesome is truly awesome.

I guess to be fair, I don’t actually hate Font Awesome, I hate that people use it blindly.

Once again, if you don’t know me, I’m a penny pincher when it comes to HTTP requests, DNS lookups and bytes crossing the wire. When someone says “pull the data in with AJAX” I say “can you just inline the data on the server-side in a JS block?” When someone else says “here’s how to do something with jQuery” I’ll always come back with the vanilla JS version. Just load the jQuery from a CDN because the user might have it cached already? Nope, I’d rather host myself. One less DNS lookup and TLS handshake to worry about, not to mention a potential point of failure.

Back to Font Awesome, including it the way that most people blindly use it you’ll end up with a minimum of two calls, one for the CSS and one for one of the font files. But, how I usually see it is that someone wants a Twitter icon, and exclamation mark, and a check mark, which, if you don’t already know actually causes three font files to download! The Twitter logo is part of the brand fonts, the exclamation mark is solid fonts and the check mark is part of the regular fonts. In total you’ve added 150 KB to the page, just for those three icons that could have been inlined for probably 2 or 3 KB (and that’s not counting compression.)

Now, if you don’t care about 150 KB on your initial request, that’s fine, I’m not judging you. But 150 KB for X plus another 100 KB for Y starts to add up over time. And sites never shrink over time, they always grow. Tomorrow or next week or next year you’ll add a new feature which adds another 100 KB for feature Z.

My further hatred of how people use it, and you’ll see this very often in the WordPress world, is that every plugin requests their own copy of Font Awesome, some on version 4, some on version 5, so you end up with multiple downloads of Font Awesome. On a recent site we audited we found 500 KB of Font Awesome data on the home page! That’s half of a megabyte! For icons!

Anyone still reading? Probably not.

I just had to say something, it was bugging me. Font Awesome is a great way to get started for fast designs. It is also a great tool if you have dynamic or unknown data entering your site that you need to support. And further, if you defer or lazy load things you can ease the burden even more. But just don’t use it blindly.

On the off-chance that someone actually reads this I turned comments off because I don’t care, I just wanted to rant.

The post I hate Font Awesome like I hate jQuery appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
WordPress, rel canonical missing https://cjhaas.com/2018/10/30/wordpress-rel-canonical-missing/?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-rel-canonical-missing https://cjhaas.com/2018/10/30/wordpress-rel-canonical-missing/#respond Tue, 30 Oct 2018 14:53:36 +0000 http://cjhaas.com/?p=890 The post WordPress, rel canonical missing appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
We’ve got a site with WP SEO and WPML that we needed <link rel="canonical" /> on but we couldn’t get it to output no matter what we tried.

First, we actually usually disable that tag on most sites so we checked that it wasn’t us. Good there.

Second, we poked through Yoast/WP SEO and would that in fact the do disable it by default. However, they also re-enable it correctly and output exactly what you’d want, so good there, too, as far as we could tell.

Third, we found a spot in WPML that could be doing it but that turned out wrong, so good there, too.

Finally, we found this post which explains that Yoast does not output that tag if a site’s robots.txt policy is set to disable indexing. That makes sense. Canonical URLs are used mostly by spiders to avoid duplicates, and if you don’t want spiders you don’t need that tag.

The post WordPress, rel canonical missing appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
https://cjhaas.com/2018/10/30/wordpress-rel-canonical-missing/feed/ 0
UniFi Controller, Let’s Encrypt and “This site can’t provide a secure connection” https://cjhaas.com/2018/07/30/unifi-controller-lets-encrypt-and-this-site-cant-provide-a-secure-connection/?utm_source=rss&utm_medium=rss&utm_campaign=unifi-controller-lets-encrypt-and-this-site-cant-provide-a-secure-connection https://cjhaas.com/2018/07/30/unifi-controller-lets-encrypt-and-this-site-cant-provide-a-secure-connection/#respond Mon, 30 Jul 2018 18:05:59 +0000 http://cjhaas.com/?p=866 The post UniFi Controller, Let’s Encrypt and “This site can’t provide a secure connection” appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
You cannot use import_cert to import a cert created by Let's Encrypt. That command is instead supposed to complement and finish the new_cert command.

However, if you did accidentally do this, in your data folder (which on my Linux box was /usr/lib/unifi/data) there should hopefully be a keystore.before_import file that you can copy back onto keystore and then just restart the unifi service with sudo service unifi restart.

The post UniFi Controller, Let’s Encrypt and “This site can’t provide a secure connection” appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
https://cjhaas.com/2018/07/30/unifi-controller-lets-encrypt-and-this-site-cant-provide-a-secure-connection/feed/ 0
A font for machine instructions https://cjhaas.com/2017/11/10/a-font-for-machine-instructions/?utm_source=rss&utm_medium=rss&utm_campaign=a-font-for-machine-instructions https://cjhaas.com/2017/11/10/a-font-for-machine-instructions/#respond Fri, 10 Nov 2017 16:04:43 +0000 http://cjhaas.com/?p=845 The post A font for machine instructions appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
No, not a font for programmers, or at least not directly. This is a font for actually encoding machine instructions.

For instance, the standard programming constructif is composed of the ASCII codes 0x69 and0x66(or Unicode U+0069 and U+0066). This singular concept takes two codes to write and uses a language that's only spoken by a fraction of the planet.

Think that last part through. Almost every major programming language is based on English. Wow.

Anyhow, imagine if we could remove the dependency upon the English language and instead encode the concept of "if" in just a single character. People could then create fonts that renders glyphs appropriate to the user's language and writing system!

If you read the last post here you'll find a font called Quest PIOC 009 along with a sample text file that uses plane 15 from Unicode (the private use area which people often use for testing) to do just that.

Here's a sample of the font rendering five parts of the if-then-elseif-else-endif concepts (font on the left, human interpretation on the right):

The if concept above is represented by the Unicode code-point U+FA02A which is encoded in UTF-16 LE as A8-DB-2A-DC. So, although we now need 4 bytes to encode the concept of "if" we can do it in a language-agnostic way!

There's other language and low-level constructs in there including registers, loops, display output via "putpixel", mathematical functions (cos, arc, etc.) and even actual functions. At the bottom of this page there's a screenshot showing everything that's included in version 9 of the font that's based off of draft 5 of William Overington's proposal.

Overall, I'm not sure if this is going to go anywhere. This was all from 2010 and there's a post in the Unicode mailing list that says:

As Andrew pointed out, you are wasting your time and ours. "There is 
absolutely NO CHANCE that the UTC or WG2 will accept (or even waste their 
time discussing) your proposal as it is totally out of scope for character 
encoding standards." 

Honestly, at first glance I thought this was weird, esoteric and possibly even stupid. (Sorry William!). But as I was writing this and started to think more about it I've come to the decision that I think this could actually be a really good idea, although possibly not for reasons that the author might have really wanted.

Each code point used in the portable interpretable object code would represent a command to a virtual machine that would be obeyed by the application program, such as a document reader, that processed the Unicode characters as software. Thus dynamic illustrations and indeed interactive illustrations could be added into a text document using a non-proprietary format that is also in Unicode plain text format. This could perhaps have far-reaching implications for the future of information technology.

I'm still trying to wrap my brain around that but I generally believe that encoding programming instructions, with the intention of actually being executed relative to the surrounding content is very dangerous. Most email clients don't even all JavaScript for this specific reason. And remember those wonderful macros and VBA scripts from the 90's and 2000's?

But, I think that actually encoding these programming concepts themselves could have potentially great value. I don't know what the learning curve is for bringing people in that don't have a grasp of the English language to start programming. For instance, a quick translation of if from English to Tamil gives me என்றால். And in Vietnamese you get nếu.

And maybe the language barrier isn't a big problem, I don't know. Here's a simple for loop written in Chinese BASIC. I can tell you that except for the line numbers, this is a show-stopper for me!

10 卜=0

20 入 水, 火

30 從 日 = 水 到 火

40 卜 = 卜+對數(日)

50 下一 日

60 印 卜

Finally, here's the full rendering of the font as promised above. Remember, each box is a single glyph encoding a concept, and that this specific font is intended for English speakers although someone could make another font for other languages.

The post A font for machine instructions appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
https://cjhaas.com/2017/11/10/a-font-for-machine-instructions/feed/ 0