Problems with lposix on win32

classic Classic list List threaded Threaded
44 messages Options
123
Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

Chris Marrin
Rici Lake wrote:

> A few things:
>
> On 15-Jan-06, at 12:43 PM, Chris Marrin wrote:
>
>> The original lposix package had this, but it had a third 'overwrite'
>> param. I don't think this is needed, since you can see if a variable
>> exists with os.getenv() and then avoid calling osex.setenv() if so.
>
>
> setenv with overwrite (should be) atomic, which you cannot achieve with
> getenv/setenv. However, I don't think anyone actually uses this feature
> as a locking mechanism, so I don't see any need to reproduce it.

I didn't think about that "feature". But I agree that it is overkill.

>
> Given that, it seems like a table interface to getenv/setenv is much
> more natural than a function call interface. Moreover, it's faster. It
> also makes it easy for a sandbox to provide a fake environment. So
> personally, I'd prefer to scrap both getenv and setenv, and just provide
> an os[ex].env table.

That sounds fine to me. But for now I would just implement it internally
as calls to getenv() and putenv() and let the community deal with
"protecting" it with a private environment. Also, unless an installation
chooses to take it away, the standard os package will still have a getenv().

...
> Almost all of your remaining functions have to do with the filesystem,
> not the operating system; the exceptions are sleep() and possibly
> errno(). I think sleep() is uncontroversial; the remaining
> filesystem-related calls should go into a filesystem library, not an os
> library (imho).

It's interesting that you would say sleep() is uncontroversial since
even it has detractors. Such is the way of a concensus library, I suppose!

>
> errno() is very difficult to use from a scripting language...
>
> On the whole, I think that errors should be retained with OS-like
> objects, which is essentially the semantics of standard C i/o, for
> example, where each FILE has its own error indicator (which can be both
> read and cleared.) That would be possible to implement, and I think it
> would be easier to implement that in an OS-independent way. In addition,
> it would be easier to use. Say, for example, that both files and sockets
> had a lasterror() method (or a lasterror key). That would make it much
> easier to write loops which respected error conditions:
>
>   for line in f:lines() do
>     ...
>   end
>   if f:lasterror() then   -- or possibly "if not f.eof"
>     ... report the error
>   end

Since Lua has multiple return values, why not just always return errno
as the second return value of each (relevant) call?

>
> I personally think that open() should be a member function on a
> directory object, rather than a global. An implementation should provide
> both a root directory object (sort of the analogy to stdin) and (if
> possible) a cwd() function; regardless, there would be some object onto
> which open() errors could be attached. This would simplify programming
> of directory traversal functions as well.

I would rather not globalize the discussion into existing functionality.
I just want to get some needed functions online ASAP!

--
chris marrin              ,""$, "As a general rule,don't solve puzzles
[hidden email]        b`    $  that open portals to Hell" ,,.
         ,.`           ,b`    ,`                            , 1$'
      ,|`             mP    ,`                              :$$'     ,mm
    ,b"              b"   ,`            ,mm      m$$    ,m         ,`P$$
   m$`             ,b`  .` ,mm        ,'|$P   ,|"1$`  ,b$P       ,`  :$1
  b$`             ,$: :,`` |$$      ,`   $$` ,|` ,$$,,`"$$     .`    :$|
b$|            _m$`,:`    :$1   ,`     ,$Pm|`    `    :$$,..;"'     |$:
P$b,      _;b$$b$1"       |$$ ,`      ,$$"             ``'          $$
  ```"```'"    `"`         `""`        ""`                          ,P`
Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

Rici Lake-2

On 15-Jan-06, at 7:25 PM, Chris Marrin wrote:

> Since Lua has multiple return values, why not just always return errno
> as the second return value of each (relevant) call?

Fair enough. In that case, errno() is unnecessary (and not obviously
implementable on non Posix systems).

POSIX (and Win32) standardize the names of the errno constants; I agree
that these should be strings (just simple strings, like "ENOENT"; the
WSA_ prefix can be deleted, for example, so that the names coincide.
There's a errno translation mechanism in APR which you could
stealxxxxrepurpose :) in general, I think looking at APR's API is a
good idea because a lot of people have thought hard about how to make
an API which is both portable and easy to use.

Just to explain my reasoning:

In order for functions which might be used as iterators to be usable as
iterators, it is important that they return <false, error>, rather than
<nil, error>. However, having thought about this a bit, and tried both
interfaces, I actually remain convinced that being able to access
errors "after the fact" leads to simpler code. It is certainly possible
to write:

   for in, errmsg in f:lines() do
     if errmsg then
       -- handle the error
     end
     -- handle the non-error case
   end

but it requires every script to think about the possibility of errors
in order to avoid a runtime type error (boolean used where a string is
required.) Maybe that's not a bad thing, but in many cases it is
actually ok to just let a loop die if an error is encountered.
Furthermore, errmsg in the above is local to the for loop, which is
also a trifle awkward.

> I would rather not globalize the discussion into existing
> functionality. I just want to get some needed functions online ASAP!

Good point. Sorry. I'll try and focus my thoughts about filesystem
interfaces for some other thread. :)

Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

Rici Lake-2
In reply to this post by D Burgess-4

On 15-Jan-06, at 6:19 PM, D Burgess wrote:

> I agree with this.
>
> On 1/16/06, Edgar Toernig <[hidden email]> wrote:
>> PS: I don't like the name osex.  I'd simply put them into os.

I agree with this, too. Loading the extended os package should extend
the existing os package, not create a new one. Truly platform specific
os interfaces could go in separate packages or subpackages (os.macosx)


Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

Edgar Toernig
In reply to this post by Chris Marrin
Chris Marrin wrote:
>
> Edgar Toernig wrote:
> >
> > errno is pointless.  Unlike C, Lua has multiple return values.
> > And I would suggest to use strings like "ENOTEMPTY" instead of
> > errno numbers.  The numbers are totally useless.
>
> It's no more pointless than in C,

errno in C is and has always been a work-around for missing
multiple return values.  It's usage is extremly fragile - just
look at lposix.c where every single use of it is broken. [Uhh, see PS]

> If errno is a value for which I don't have a symbolic constant, I would
> return nil for the string and still return the number. That gives us
> fully general functionality.

I would suggest to never use numbers - always return short strings
like "EPERM" or, for unknown errors, "E42".  A function that usually
returns a string and only sporadically a number is error prone.
The same for the full text message: "unknown error E42".

Ciao, ET.

PS: Oh, I just noticed, lua-5.1-rc has the same problems with
    errno: between the syscall setting errno and the usage of
    errno there are several function calls that may destroy
    its value (i.e. lua_push* may call lockmutex, malloc,
    generate fp-errors, etc).  And ferror doesn't set errno
    at all.
Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

Roberto Ierusalimschy
> I just noticed, lua-5.1-rc has the same problems with errno: between
> the syscall setting errno and the usage of errno there are several
> function calls that may destroy its value (i.e. lua_push* may call
> lockmutex, malloc, generate fp-errors, etc).  And ferror doesn't set
> errno at all.

* lockmutex: users use this facility at their own risk...

* malloc: as far as I know, a correct malloc should not change errno.
An error in malloc generates a MEMERR, so errno is not used.

* I think only lua_pushinteger may generate fp-errors. We do not call
it before using errno. (Well, ok, a pushstring may even call a gc
tag method...)

* "And ferror doesn't set errno at all." What is the point here?


We will try to improve that.

-- Roberto
Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

Rici Lake-2

On 16-Jan-06, at 6:45 AM, Roberto Ierusalimschy wrote:
> * malloc: as far as I know, a correct malloc should not change errno.
> An error in malloc generates a MEMERR, so errno is not used.

Sadly, that's not true.

   "The setting of errno after a successful call to a
    function is unspecified unless the description of
    that function specifies that errno shall not be
    modified."

POSIX malloc() does use errno, and it does not specify
that errno is preserved by a successful call. So it
might change. (And, in fact, I've observed this
behaviour from some mallocs which internally use
features like mmap().)

In general, the only safe way to use errno is to
save it immediately after a call to a function
which generates an error return.

I agree with ET: errno is an awful mechanism for error
reporting.


Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

Roberto Ierusalimschy
> > * malloc: as far as I know, a correct malloc should not change errno.
> > An error in malloc generates a MEMERR, so errno is not used.
>
> Sadly, that's not true.

  "The value of errno is zero at program startup, but is never set to
   zero by any library function."  (ISO/IEC 9899:1999, page 186)

(So, it may change errno, but only to another error code...)


> I agree with ET: errno is an awful mechanism for error reporting.

I guess we all agree on this point. But does C have another mechanism
that we could use?

-- Roberto

Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

Alex Queiroz
Hallo,

On 16/01/06, Roberto Ierusalimschy <[hidden email]> wrote:
>
> I guess we all agree on this point. But does C have another mechanism
> that we could use?
>

     Good, Roberto was trapped and is now involved. :-)

--
-alex
http://www.ventonegro.org/
Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

Rici Lake-2
In reply to this post by Roberto Ierusalimschy

On 16-Jan-06, at 9:43 AM, Roberto Ierusalimschy wrote:

>>> * malloc: as far as I know, a correct malloc should not change errno.
>>> An error in malloc generates a MEMERR, so errno is not used.
>>
>> Sadly, that's not true.
>
>   "The value of errno is zero at program startup, but is never set to
>    zero by any library function."  (ISO/IEC 9899:1999, page 186)
>
> (So, it may change errno, but only to another error code...)

The point is that a *successful* malloc could change errno to any value
(other than 0). (The value of errno is undefined after a successful
call to any Posix function which is not specifically documented to not
change errno.)

So correct code would be something like:

   int saved_errno = 0;
   int rc = 0;
   rc = some_function();
   if (rc == -1) saved_errno = errno;
   ...

   lua_pushinteger(L, saved_errno);


By the way, the requirement that errno be set to 0 at startup has been
dropped from IEEE Std 1003.1-2001. There wasn't much point, anyway.

>> I agree with ET: errno is an awful mechanism for error reporting.
>
> I guess we all agree on this point. But does C have another mechanism
> that we could use?

The best API design I know of is to associate errors with objects, as
the standard C library does for file errors. That's manageable in a
binding interface if there is always an object available to attach an
error indication to, and that was the source of my musings about making
open a method of a directory object. (With the understanding that an
implementation does not have to provide more than one directory object;
an embedded system without directories would provide only the "root"
object, which would be the same as the "cwd" object.)

Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

Edgar Toernig
In reply to this post by Roberto Ierusalimschy
Roberto Ierusalimschy wrote:
>
> > I just noticed, lua-5.1-rc has the same problems with errno: between
> > the syscall setting errno and the usage of errno there are several
> > function calls that may destroy its value (i.e. lua_push* may call
> > lockmutex, malloc, generate fp-errors, etc).  And ferror doesn't set
> > errno at all.
>
> * lockmutex: users use this facility at their own risk...

Those were only examples.  I took lockmutex just to show that
simple functions like lua_pushnil may eventually change errno.
Afaics, lua_pushstring may even call the garbage collector and
invoke GC methods.

How about a note in the manual: No API function preserves errno.


> * malloc: as far as I know, a correct malloc should not change errno.

I wouldn't bet a dime on that :-)


> * "And ferror doesn't set errno at all." What is the point here?

Code like:

  if (ferror(f))
    return luaL_error(L, "%s", strerror(errno));

There are more places, just grep for ferror.


> We will try to improve that.

Considering that nobody has noticed it in a lot of years I guess
it's not that urgent.

Ciao, ET.
Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

Edgar Toernig
In reply to this post by Roberto Ierusalimschy
Roberto Ierusalimschy wrote:
>
> > errno is an awful mechanism for error reporting.
>
> I guess we all agree on this point. But does C have another mechanism
> that we could use?

Nothing that I'm aware of.

The point was, one shouldn't export this error reporting mechanism
to lposix via an errno() function.  Lua can do better with multiple
return values.

Ciao, ET.
Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

Wim Couwenberg-2
In reply to this post by Chris Marrin

>> PS: I don't like the name osex.  I'd simply put them into os.

> I don't think that's possible using the 5.1 package model. I suppose it
> is possible to make:

>      require "osex"

> add things to the 'os' table, assuming it exists. But that seems, at
> least, poor practice.

It is possible in the 5.1 package model, but you still
have to know what you're doing:

in file os/ex.lua (or where-ever):

    -- make sure that "os" is loaded first, otherwise
    -- subsequent require("os") calls would only return our
    -- extension set...
    require "os"

    -- now use the same table as package "os"
    module "os"

    -- all your os.ex stuff goes here...

    -- don't forget to return our own module table or subsequent
    -- require("os.ex") calls will simply return true...
    return _M

Both Diego Nehab and myself have proposed alternatives for this (no
need to actually require "os" first nor to return the module table.)
Maybe this sort of thing is just not common enough (or poor practice,
like you said?)

--
Wim

   

Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

Chris Marrin
In reply to this post by Rici Lake-2
Rici Lake wrote:

>
> ...
> In order for functions which might be used as iterators to be usable as
> iterators, it is important that they return <false, error>, rather than
> <nil, error>. However, having thought about this a bit, and tried both
> interfaces, I actually remain convinced that being able to access errors
> "after the fact" leads to simpler code. It is certainly possible to write:
>
>   for in, errmsg in f:lines() do
>     if errmsg then
>       -- handle the error
>     end
>     -- handle the non-error case
>   end
>
> but it requires every script to think about the possibility of errors in
> order to avoid a runtime type error (boolean used where a string is
> required.) Maybe that's not a bad thing, but in many cases it is
> actually ok to just let a loop die if an error is encountered.
> Furthermore, errmsg in the above is local to the for loop, which is also
> a trifle awkward.

Since Lua is single threaded and coroutines do not implicitly yield, we
could just have an errno property in the osex package variable. But then
you have to think about dealing with errors if you ever need to yield
and it does not allow for hooks that implicitly yield, like has been
discussed here.

Ignoring the existing io package issues for a second, returning an error
for most of the osex functions proposed is easy. But what object could
be used to hold the error for the files() iterator? Any syggestions?

--
chris marrin              ,""$, "As a general rule,don't solve puzzles
[hidden email]        b`    $  that open portals to Hell" ,,.
         ,.`           ,b`    ,`                            , 1$'
      ,|`             mP    ,`                              :$$'     ,mm
    ,b"              b"   ,`            ,mm      m$$    ,m         ,`P$$
   m$`             ,b`  .` ,mm        ,'|$P   ,|"1$`  ,b$P       ,`  :$1
  b$`             ,$: :,`` |$$      ,`   $$` ,|` ,$$,,`"$$     .`    :$|
b$|            _m$`,:`    :$1   ,`     ,$Pm|`    `    :$$,..;"'     |$:
P$b,      _;b$$b$1"       |$$ ,`      ,$$"             ``'          $$
  ```"```'"    `"`         `""`        ""`                          ,P`
Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

Chris Marrin
In reply to this post by Rici Lake-2
Rici Lake wrote:

>
> On 15-Jan-06, at 6:19 PM, D Burgess wrote:
>
>> I agree with this.
>>
>> On 1/16/06, Edgar Toernig <[hidden email]> wrote:
>>
>>> PS: I don't like the name osex.  I'd simply put them into os.
>
>
> I agree with this, too. Loading the extended os package should extend
> the existing os package, not create a new one. Truly platform specific
> os interfaces could go in separate packages or subpackages (os.macosx)
>

In looking at the 5.1 code, it seems that loading the os package goes
through a different line of code for registry (luaL_register) than an
external package (ll_module). The code is somewhat different, for
instance, luaL_register does not put an _NAME propertry in the table.
Can I therefore do a 'require "os"' and have something reasonable happen?

--
chris marrin              ,""$, "As a general rule,don't solve puzzles
[hidden email]        b`    $  that open portals to Hell" ,,.
         ,.`           ,b`    ,`                            , 1$'
      ,|`             mP    ,`                              :$$'     ,mm
    ,b"              b"   ,`            ,mm      m$$    ,m         ,`P$$
   m$`             ,b`  .` ,mm        ,'|$P   ,|"1$`  ,b$P       ,`  :$1
  b$`             ,$: :,`` |$$      ,`   $$` ,|` ,$$,,`"$$     .`    :$|
b$|            _m$`,:`    :$1   ,`     ,$Pm|`    `    :$$,..;"'     |$:
P$b,      _;b$$b$1"       |$$ ,`      ,$$"             ``'          $$
  ```"```'"    `"`         `""`        ""`                          ,P`
Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

D Burgess-4
In reply to this post by Chris Marrin
On 1/18/06, Chris Marrin <[hidden email]> wrote:
> Since Lua is single threaded and coroutines do not implicitly yield, we
> could just have an errno property in the osex package variable. But then
> you have to think about dealing with errors if you ever need to yield
> and it does not allow for hooks that implicitly yield, like has been
> discussed here.

Here is a problem. Consider two OS threads with two Lua VMs.
On some OSs errno is not threadsafe.
I would not have errno as a property. Drop it from the osex, it
gains very little.

>
> Ignoring the existing io package issues for a second, returning an error
> for most of the osex functions proposed is easy. But what object could
> be used to hold the error for the files() iterator? Any syggestions?

nil, errorstring - error
nil,nil  - ok termination


>
> --
> chris marrin              ,""$, "As a general rule,don't solve puzzles
> [hidden email]        b`    $  that open portals to Hell" ,,.
>          ,.`           ,b`    ,`                            , 1$'
>       ,|`             mP    ,`                              :$$'     ,mm
>     ,b"              b"   ,`            ,mm      m$$    ,m         ,`P$$
>    m$`             ,b`  .` ,mm        ,'|$P   ,|"1$`  ,b$P       ,`  :$1
>   b$`             ,$: :,`` |$$      ,`   $$` ,|` ,$$,,`"$$     .`    :$|
> b$|            _m$`,:`    :$1   ,`     ,$Pm|`    `    :$$,..;"'     |$:
> P$b,      _;b$$b$1"       |$$ ,`      ,$$"             ``'          $$
>   ```"```'"    `"`         `""`        ""`                          ,P`
>
Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

D Burgess-4
In reply to this post by Chris Marrin
On 1/18/06, Chris Marrin <[hidden email]> wrote:

> Rici Lake wrote:
> >
> > On 15-Jan-06, at 6:19 PM, D Burgess wrote:
> >
> >> I agree with this.
> >>
> >> On 1/16/06, Edgar Toernig <[hidden email]> wrote:
> >>
> >>> PS: I don't like the name osex.  I'd simply put them into os.
> >
> >
> > I agree with this, too. Loading the extended os package should extend
> > the existing os package, not create a new one. Truly platform specific
> > os interfaces could go in separate packages or subpackages (os.macosx)
> >
>
> In looking at the 5.1 code, it seems that loading the os package goes
> through a different line of code for registry (luaL_register) than an
> external package (ll_module). The code is somewhat different, for
> instance, luaL_register does not put an _NAME propertry in the table.
> Can I therefore do a 'require "os"' and have something reasonable happen?
>
Methinks Edgar had in mind that you would use

require"osex"   or require"os.ex"

given that you writing a C module , you can populate whicever table you
like, in this case os.

Or did I misunderstand your question?
DB
Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

Rici Lake-2
In reply to this post by D Burgess-4

On 17-Jan-06, at 11:54 AM, D Burgess wrote:

>> Ignoring the existing io package issues for a second, returning an
>> error
>> for most of the osex functions proposed is easy. But what object could
>> be used to hold the error for the files() iterator? Any syggestions?
>
> nil, errorstring - error
> nil,nil  - ok termination

That doesn't work; it has to be false, errorstring.

In Lua's iterator protocol, the nil will terminate the for loop and the
errorstring will never be seen. Hence my comment that it has to be
<false, errorstring>, and the consequence that it has to be dealt with
explicitly in every for loop with that architecture.

Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

Rici Lake-2
In reply to this post by D Burgess-4

On 17-Jan-06, at 12:10 PM, D Burgess wrote:

> Methinks Edgar had in mind that you would use
>
> require"osex"   or require"os.ex"
>
> given that you writing a C module , you can populate whicever table you
> like, in this case os.

That was my thought. In fact, you can even return the os table. In
fact, you can even return the os table *unmodified*. So an unextended
osex package is possible :)

In other words:

local os = require "osex"

throws an error if you have an implementation which is not aware of
osex.

If it doesn't, you can check for the existence of an API:

local sleep = assert(os.sleep, "This package needs sleep")

Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

Chris Marrin
In reply to this post by D Burgess-4
D Burgess wrote:
> ...
> Methinks Edgar had in mind that you would use
>
> require"osex"   or require"os.ex"
>
> given that you writing a C module , you can populate whicever table you
> like, in this case os.

But if you 'require "os.ex"', it will first do an implicit 'require
"os"' and that's what I'm worried about. Will that succeed?

--
chris marrin                ,""$,
[hidden email]          b`    $                             ,,.
                         mP     b'                            , 1$'
         ,.`           ,b`    ,`                              :$$'
      ,|`             mP    ,`                                       ,mm
    ,b"              b"   ,`            ,mm      m$$    ,m         ,`P$$
   m$`             ,b`  .` ,mm        ,'|$P   ,|"1$`  ,b$P       ,`  :$1
  b$`             ,$: :,`` |$$      ,`   $$` ,|` ,$$,,`"$$     .`    :$|
b$|            _m$`,:`    :$1   ,`     ,$Pm|`    `    :$$,..;"'     |$:
P$b,      _;b$$b$1"       |$$ ,`      ,$$"             ``'          $$
  ```"```'"    `"`         `""`        ""`                          ,P`
"As a general rule,don't solve puzzles that open portals to Hell"'
Reply | Threaded
Open this post in threaded view
|

Re: Problems with lposix on win32

Chris Marrin
In reply to this post by Rici Lake-2
Rici Lake wrote:

>
> On 17-Jan-06, at 12:10 PM, D Burgess wrote:
>
>> Methinks Edgar had in mind that you would use
>>
>> require"osex"   or require"os.ex"
>>
>> given that you writing a C module , you can populate whicever table you
>> like, in this case os.
>
>
> That was my thought. In fact, you can even return the os table. In fact,
> you can even return the os table *unmodified*. So an unextended osex
> package is possible :)
>
> In other words:
>
> local os = require "osex"
>
> throws an error if you have an implementation which is not aware of osex.
>
> If it doesn't, you can check for the existence of an API:
>
> local sleep = assert(os.sleep, "This package needs sleep")

I sure don't like the idea of a successful load of "osex" NOT populating
some table with a well known set of functions. If sleep() is part of
osex, then it should be there if the load is successful.

I am liking the idea of 'require "osex"' populating the real 'os' table,
if it exists. If not, osex should load the equivalant of the functions
from 'os' and then add its own and return that table. That way 'osex' is
always a superset of 'os' and all the functions are in one place. In
fact, I have never seen any implementations where 'os' is supplied as a
loadable module, so why not just name this extension 'os' and always
have it include the proposed functions as well as those in the existing
'os' package?

--
chris marrin                ,""$,
[hidden email]          b`    $                             ,,.
                         mP     b'                            , 1$'
         ,.`           ,b`    ,`                              :$$'
      ,|`             mP    ,`                                       ,mm
    ,b"              b"   ,`            ,mm      m$$    ,m         ,`P$$
   m$`             ,b`  .` ,mm        ,'|$P   ,|"1$`  ,b$P       ,`  :$1
  b$`             ,$: :,`` |$$      ,`   $$` ,|` ,$$,,`"$$     .`    :$|
b$|            _m$`,:`    :$1   ,`     ,$Pm|`    `    :$$,..;"'     |$:
P$b,      _;b$$b$1"       |$$ ,`      ,$$"             ``'          $$
  ```"```'"    `"`         `""`        ""`                          ,P`
"As a general rule,don't solve puzzles that open portals to Hell"'
123