Announcement

Collapse
No announcement yet.

Problems with Shell

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Problems with Shell

    Hello, I've been using the following code snippet with success
    for some time now:

    --------------
    PROCESS:
    Close #3
    Shell "Process_1"
    Shell "DEL File_1"
    Shell "DEL File_2"
    GoTo MAIN
    --------------
    All of the sudden, it started to give me problems on one pc.
    It shells to "Process_1" and executes that .exe just fine. It
    even continues to del file_1 and file_2. However, the command
    window never closes and the program "freezes", it does not "GoTo
    Main".

    Does anyone have any ideas why this may be happening? Could it
    be a memory problem? I'm totally in the dark on this!

    Thanks for your help.

    Bob


    ------------------

    #2
    Code:
    Shell "DEL File_1"
    Shell "DEL File_2"
    New code:
    Code:
     ON ERROR RESUME NEXT 
     KILL "file_1"
     IF ERR THEN
        PRINT "Error #" & STR$(ERR) & " Killing file 'file_1'"
     END IF
     KILL "file_2"
     IF ERR THEN
        PRINT "Error #" & STR$(ERR) & " Killing file 'file_2'"
     END IF
    MCM

    Michael Mattias
    Tal Systems (retired)
    Port Washington WI USA
    [email protected]
    http://www.talsystems.com

    Comment


      #3
      Thank you Michael for your reply. However, I am a little confused
      as to why this will solve my problem. Deleting of the two files
      is working ok. The problem is that the shell to Process_1.exe
      is leaving the command window open in what appears to be a program
      "hang".

      I'm not being argumentative...just trying to learn while getting
      my problem fixed!! I'm still a beginner....

      Thanks,

      Bob

      ------------------

      Comment


        #4
        Mike's right ..

        An additional thought is to perhaps avoid using RESUME NEXT, if at all
        practical in your code and substitute hard numeric line labels. Yhat
        will let you take advantage of PB's excellent error handling and
        reporting capabilities which have so thoughtfully been assembled for
        your use.

        Yes, I understand numeric line labels have some unttactivelness for lots
        of people, but if you are doing this to get away from PB then I can
        only offer you another tip. Grin, it also helps even if you never leave
        the wonderful PB fold.

        You can build your own pointers to the practical line 'number' at which
        an error takes place by simply creating a global variable name for
        it. Then you hand create an equate at critical places which increment
        and also decrement that pointer as your code executes. By doing this
        on a global basis, I've successfully imitated the PB error handling in
        translation work of PB to C/C++ for research purposes.

        When your programs begin to include, or if they currently include the
        CALL or FUNCTION features, even if they are deeply nested into subroutines
        inside of subroutines, by carefully planning numeric line numbers to
        the allowable 65000 or so line numbers allowd, you can feed critical line
        numbers into all your work. That lets you go back, for example, and
        direct program operations to different places, depending on, in your own
        example, which of the lines blew up.

        The use of RESUME NEXT doesn't let you plan for alternative exits to
        the problems which can surface. RESUME #### as provided in PB does.
        And if you develope the habit of creating your own pointer variable to
        this, you can then use RESUME and then a CUSTOME LABEL, even if
        needed, that is a non-numeric label to continue.

        One place where this becomes very, very helpful, is in the use of the
        Btrieve files systems from Pervasive. Failures from within their own
        routines contain a complete custom set of error messages and a numeric
        pointer to each. You build a similar error to the way you have from PB
        to display and record these to an error log file to let you trace
        blowups that way, even to what to do when a customer comes calling, and
        you ask them to read the error dump file line for your report file,
        which is all taken and reported from this use of both hard numeric line
        numbers from the PB tools, plus your own pointers as needed, as well as
        what, say Btrieve told you too. It may even be helpful, for example,
        if this shell example you posted involves files that are on a different
        box somewhere on a LAN, to learn to use the INT21H custom error messages
        and others which really can drill down to what is wrong. Think about
        that. PB can't even reasonably be expected to cover all those complex
        error possibilities which you might really need some day.

        Last note. Courtesy of the VERY helpful teaching of Lance here, when
        you start to use CALLS, SUBS, and so on, especially if they are nested,
        use the concept provided in EXIT FAR to make sure the code collapses
        to the desired place where you really want to handle many errors. Then
        to isolate this stuff into local places in SUBS and so on, should what
        you be showing us be in one of them, you use the ON LOCAL ERROR game to
        try recovering locally what you can, prior to the final EXIT FAR which
        is the catch all rain bucket.

        Finally, consider investing carefully in the inexpensive tool called
        ULTRASHELL for PB's executables. I realize you showed us a simple
        command line level example here that causes you problems. But you
        can have problems that surface in the shell game which are memory
        space related problems. This tool gives you the ability to slush the
        main program code into upper memory creating far more memory for your
        use in the shell child process. Alan has done a wonderful job of that
        which greatly extends the issue.

        And as a post script, consider redirecting the video output of the
        shell operation into a NUL location. For some, but not all work,
        that way the user will never see what is going on in the shell game.

        You will grin from ear to ear the first day you pick up that shell
        and under it is the real trace data which tells you how that stray
        penny got there! It might even be a US 1909 SVDB cent which if all
        the wheat tips are on it would be worth over $500 that day!





        ------------------
        Mike Luther
        [email protected]

        [This message has been edited by Mike Luther (edited August 20, 2003).]
        Mike Luther
        [email protected]

        Comment


          #5
          The way you described your problem it sounded like your program was 'hanging' on the "delete_file_2" line

          Why I did it the way I did? Why SHELL a file delete when you have a function (KILL) which does the same thing, and using KILL your program has total control of the delete complete with error codes?

          As far as the command window staying open, either.. (and I'm not very good at these)..

          There might be a system option which controls "window behavior" of Console programs.
          ** OR **
          The command window might be closing, but then re-opening to do the "del" command.

          MCM


          Michael Mattias
          Tal Systems (retired)
          Port Washington WI USA
          [email protected]
          http://www.talsystems.com

          Comment


            #6
            Hi, I'm still playing with this program and it appears to me
            that when I execute the code...

            Shell "Process_1" that my program is running this exe but it is
            not waiting for it to completely finish before it moves to the
            next line of code, Shell "DEL File_1".

            I thought a Shell command would wait until the shelled program
            was completely unloaded before it moved to the next statement??

            --------------
            PROCESS:
            Close #3
            Shell "Process_1"
            Shell "DEL File_1"
            Shell "DEL File_2"
            GoTo MAIN
            --------------

            Thanks, Bob

            ------------------

            Comment


              #7
              Michael wrote...
              The way you described your problem it sounded like your program was 'hanging' on the "delete_file_2" line

              Michael, I appologize for not being very clear. The command window
              that remains open is from the Process_1.exe program. I display
              a counter on the command window for that program and you can still
              see the the counter on the "frozen" window. However, File_1 and
              File_2 do get deleted.

              This is why I'm not sure what is going on. If my program does
              delete File_1 and File_2, I was assuming that it was done shelling
              to Process_1. And if it is done shelling to Process_1, then
              why is the command window not going away? Or does it shell to
              Process_1 and without waiting for Process_1 to finish, it
              deletes File_1 and File_2.

              Thank you...

              ------------------

              Comment


                #8
                Bob,

                I do not know about other operating systems, but, on my Win98SE
                machine, if a DOS program prints anything to the display, then Windows
                will not close the window when the program has terminated. You have
                to manually close the window with the "x" (system menu close button).
                On my box, if my DOS program does NOT print anything, then Windows
                automatically closes the window after the program has ended.


                ------------------

                Comment


                  #9
                  Hi Clay, I did not write the cobol exe that is displaying
                  output to the dos window. However, I do know that in about
                  ten other installations, the dos window always closed on its own.
                  There must be some programming that closes the window.

                  It's really odd because all of the sudden, I'm having this issue.
                  The dos window does not close and the program freezes.
                  Frustrating!!

                  Thanks

                  ------------------

                  Comment


                    #10
                    Bob ..

                    The issue issue of whether a shell child reqmains open and the next
                    one opens while the first one is still open is even more complicated
                    than many people think.

                    I know you are likely not operating under OS/2, but let me share how
                    this works even within the same executable and batch files here in OS/2
                    in an attempt to help you.

                    If you execute two SHELL statements in succession like that from within
                    a PB 3.5 for DOS program, running in an OS/2 DOS-VDM program, in general
                    it does exactly what you think is happening. The first one will execute
                    and close. Then the second one will execute and close. Any errors that
                    show up in the process will be focused, if I can use that word loosely
                    here, in the actual shell process, but not necessarily carried back to
                    your PB 3.5 program at all. Remember, the DOS programs, on exit, can
                    leave error exit values from the program. We often do this as creators
                    of our programs so as to provide exit instructions which can branch
                    from a program that let the host program decide on some form of a
                    pecking order with how to behave if certain exit conditions are
                    different than others.

                    If you are attempting to use this technique in a SHELL operation in
                    PB 3.5 for DOS, operating in a DOS-VDM program in OS/2, exactly
                    what straight DOS does will be done with your code ...

                    mostly ..

                    But that depends! Here is why and how. If the program you are trying
                    to run does not necessarily behave in the exact manner on exit that
                    you are contemplating, the HOST program that SHELLed the child, may
                    not know that the process is really over, or .. may not know how it
                    terminated as to any errors that were spawned on exit by the child.
                    What does this mean.

                    It has at least TWO different meanings in OS/2, and also as WINDOWS
                    matures, as far as I know. First, your program which is the target
                    of the SHELL as a child, may be just a common DOS program. But it may
                    not! And depending on how the operating system is designed in which
                    you are running this SHELL child deal, two different things can take
                    place.

                    In OS/2, running as a DOS-VDM so-called pure DOS program, your will
                    get largely what you expect. But wait! It is possible to use a
                    technique, and some programs have this technique built in, to spawn
                    a different than straight DOS program or a Rational 4DOS program or
                    other program. You can even call what you, Bob, think is a pure DOS
                    program ... and .. under the covers, it is *NOT* at all such. For
                    example, it is possible using PASCAL and evne the MircroSoft PB7
                    Professional Bascom Compiler operation, to write code which will
                    actually operate under DOS ... or ... even Native OS/2 in 16 bit mode
                    as conventional DOS-like basic 16 color with bright makes 16 more
                    color programs that run under native OS/2 or DOS and you can't tell
                    the difference and how they are running on the OS/2 system just by
                    looking. That even though it is a shell child, from a DOS program!

                    Most folks really do not know that very early WIN-95 programs,
                    with a few drivers installed, will run just fine on an OS/2
                    system. You can even run several different ones at the same time
                    on the desktop, too.

                    Then what is the difference? Simple, in OS/2, the operating system
                    is a VERY well orchestrated PRE-EMPTIVE operating system. While the
                    SHELL statement you issue for that first command is clear to it, and
                    your will be done, if, by chance this is a program that internally can
                    be treated as ... an independent thread .. guess what?

                    Suprise! You also have to 'tell OS/2' .. and perhaps the operating
                    system in which your SHELL child group is running, whether this child
                    can be assigned an independent thread and life of its own, or, must
                    the big DADDY in the sky, the operating system, must specially restrict
                    your child to FINISHING its job, before the next one runs. If, I,
                    for example, use an OS/2 loader to run an OS/2 native program, in
                    the SHELL child operation from PB 3.5 for DOS, and do not tell OS/2
                    to make sure this program runs to exit BEFORE returning 'control'
                    to the program that SHELLed this child ..

                    -------> The first SHELL child will remain running on its own
                    -------> and the second SHELL child will spawn right away!

                    That is exactly, in a way, here, what may well be happening to you.
                    Even though you are not running OS/2, but some form of WINDOWS, Bob.

                    Even more interesing, you can SHELL to a BATCH file (.BAT) as well.
                    When you do that, it gets even more interesting. Using the right
                    tools, you can even get fouled up in the .BAT file. Or, in OS/2,
                    these files are .CMD files and not .BAT files. But you have to be
                    very careful to tell the operating system in a multi-tasking one,
                    at times, exactly how to handle this stair-step child operation.

                    Going backwards from native OS/2 programs and calling a DOS-VDM as
                    a shell child is, grin, child's play. Except that, here too, you
                    have to one way or another tell the system what to do from one
                    child to the next. You REALLY have to watch your step in this
                    world of mine if you go from native OS/2 to DOS children. And you
                    even can call BATCH files, as SHELL children too.

                    How do we do this in batch files, if you have no internal control
                    over the program to do this as part of the batch operation?

                    Simple, and you can do this too.. You can set a flag file on the
                    disk that shows that the first child is active. Then, as part of
                    the second file's operations sequence, you check for the presence
                    of that flag file. You erase that flag file only after the first
                    child is through running. The second program runs inside a timing
                    loop. If the flag file is present, the second thread (child) enters
                    a timed loop. At the end of the timed loop, the second child goes
                    back and looks at the presence of that flag file again. If it is
                    gone, it knows to execute, and off we go.

                    You, of course, have to provide timed and error level exits so that
                    if something goes wrong in the first child process, the second child
                    will, perhaps, not run at all, or run in a different way since a
                    condition that it needs for a normal run is missing.

                    Complex error trap handling, if you will, but it can even be, to some
                    extent, handled even within a BATCH file that is called by ONE of
                    your SHELL statements, eliminating the need for the two separate
                    ones you presently have, as I have suggested. Doing it this way,
                    creating the batch file with a simple file write, is easy. Write
                    the batch file from your program. SHELL to it. Then delete that
                    batch file on re-entry to your program. Secret soy sauce and
                    nobody normally will know what you are doing...

                    In reality, when we get to the real level of programming for use
                    with multiple task operating systems, this is all handled by what
                    I know as semaphores, not disk flag files and so on. But you can,
                    as illustrated, even in your world of PB DOS put this together in
                    such a way that your DOS program can be run in the later world
                    of mor complex systems. Just pay close attention to making sure
                    how you can sequence those SHELL statements so you can be sure
                    that they run in the sequence you desire ... and understand.

                    WIN2000, WIN-XP and these later WINDOWS operating systems all are
                    steps in this complex direction. Although I have the PBCC products
                    and do not use them, I am sure that the PBCC tools have the way
                    to do this built in to them as such.

                    Just trying to help by trying to show how the process works from
                    the way this happens for me all the time in my little world here.


                    ------------------
                    Mike Luther
                    [email protected]
                    Mike Luther
                    [email protected]

                    Comment


                      #11
                      Originally posted by Bob Haynes:
                      Hi Clay, I did not write the cobol exe that is displaying
                      output to the dos window. However, I do know that in about
                      ten other installations, the dos window always closed on its own.
                      There must be some programming that closes the window.

                      It's really odd because all of the sudden, I'm having this issue.
                      The dos window does not close and the program freezes.
                      Frustrating!!

                      Thanks


                      If the code acts differently on this one machine, there's something different about the machine.

                      As a guess, I'd say you should look at the DOS window's properties; I'd bet there's a difference between the properties of this one's window(s) and the properties of the window(s) on the other machines.

                      This does not sound to me like a PB problem.




                      ------------------
                      Don't sweat it, it's only ones and zeros

                      Comment


                        #12
                        Hi,
                        it's difficult give an advice on basis a tiny piece of code, but
                        try:
                        PROCESS:
                        Close #3
                        Shell "Process_1"
                        sleep 1
                        Shell "DEL File_1"
                        sleep 1
                        Shell "DEL File_2"
                        sleep 1
                        GoTo MAIN

                        Maybe, first of sleeps will enough.
                        It looks stupid but helps sometimes.
                        Lubos


                        ------------------
                        Lubos
                        A view on PowerBASIC
                        Lubos
                        A view on PowerBASIC

                        Comment


                          #13
                          Lubos ..

                          The advice to use the SLEEP function in PB in a case of multi-tasking
                          task session access error problem isn't at all correct in my humble
                          experience in OS/2's very good pre-emptive multitasking world.

                          As discovered a long time ago, at least here, the SLEEP function is
                          one of those portions of the PB toolset which appears to have no
                          awareness of operating in a multi-tasking environment. I can't tell
                          you the code which was assembled to provide it, but it loops closely
                          enough so that the CPU is swamped with those loops. With no other
                          way to operate since it dominates the CPU, many other sessions and
                          tasks never do what they are supposed to do either.

                          Yes, because OS/2 is PRE-EMPTIVE in nature, as a multi-tasker, a PB
                          task with SLEEP running in it won't totally shut down the whole computer
                          for the operator. Thus, for example, I can mouse over the desktop, hit
                          another key to operate the desktop, or .. in some cases, continue to
                          see a TCP/IP application in another task still running ... slowly ...

                          More important, the issue, as seen from running my programs under the
                          earlier forms of WINDOWS 95 and 98, is so serious, that there is simply
                          no way to run, say several PB 3.5 applications with certain functions
                          in use in them on these earlier WINDOWS platforms at all without totally
                          rendering the computer useless for other work. Examples of such uses
                          are trying to run my six-line telephone monitoring communications port
                          program with fully interactive networked database information server
                          and screen pop program in one session, my networked printer server for
                          forms output, my facility management program, my database mining
                          application; say those four different PB applications at the same time
                          on a desktop. Even without the use of SLEEP, there are also other PB
                          operations which make this difficult, or impossible unless the operating
                          system is PRE-EMPTIVE in nature, which at least the earlier WIN versions
                          simply could not do.

                          And .. as noted, even in OS/2, there are MANY different DOS applications
                          which have this problem. DOS was never designed as a multi-tasking deal
                          at least as it evolved through the Microsoft era, that is for sure. So
                          even in OS/2 .. we have what are called TASK BUSTERS that can force,
                          in most cases, even a DOS program to give up time slices more applicably
                          to the other things which must go on, like surfing the net, while
                          watching and logging a phone line in an application written in PB.

                          But there are other levels of task busting which can be used by a careful
                          and knowledgeable PB programmer which make much of the use of his or her
                          program able to release time slices on the CPU without the user ever
                          realizing this thoughtful action has been done. In order to do this we
                          must substitute some different code for the PB function which does not
                          have this time slicing awareness.

                          To do this, the first thing we need to know is what kind of am operating
                          system is being used on which the PB application is running. Is it a
                          WINDOWS box? Is it an OS/2 box? I've never even tried DOSEMU in LINUX
                          so don't ask me about it. but that's got to be thought about too now.
                          Then, depending on that answer, there are different ways to hand code
                          what used to be the SLEEP function, which return the same functionality,
                          but give up time slices to the CPU during the idle time they are seeing
                          so that other things can be done.

                          Remember that SLEEP can be broken by a keystroke! So too must you code
                          the substitute for that PB function, if you wish to make it work like
                          SLEEP was intended to work. This means the entire keyboard I/O routine
                          must be then involved in your alter-SLEEP you write. And don't forget
                          that the MOUSE, if you are using one, has to be involved in this here
                          too, complete with the click and data return for it.

                          In fact, what you realize, after you have done the development work
                          to properly handle what is required, you are having to re-write the
                          entire PB single byte INKEY$ function just to provide for this. In
                          every PB program I ever write, just the INKEY$ function has to be
                          surrounded by some 500 added lines of program code, to do what's needed
                          in my total suite, as well as to provide for the absolutely needed time
                          slice releasing for use on not only OS/2, but for WIN-95, WIN-98 and
                          who knows what all else in the world of WIN for the user.

                          Just your one line which says SLEEP in your suggestion, in at least one
                          proper way to do what you suggested, is really, I think, a sigle INKEY$
                          call which is bound into a time slice releasing timer loop. You have
                          to time slice the whole box while you loop the system in your own code
                          the hard way, by looking at the system clock and so on, until the right
                          time period has passed.

                          In my suite, that 500-odd lines of PB code, including in-line assembler,
                          to properly do what you suggested, is a SUB which is CALLED to do this.
                          And just like building communications towers or railroads, there sure
                          is more than one way to do this properly. I'm just citing my case here.
                          As thought .. you might wish to look at the in-line assmebler which it
                          takes to do this job here.

                          Code:
                          INIFIVA:
                               IF ISTRUE BIT(pbvHost, 8%) THEN '   In WIN environment
                                  ! push DS
                                  ! mov AX, &H1680
                                  ! int &H2F
                                  ! pop DS
                               ELSE '                      Not in a WIN environment
                                  ! push DS                ; Save DS for PowerBASIC
                                  ! mov AX, &H8600         ; Place function $AH68in AX
                                  ! mov CX, &H00           ; Use less than 1000 MS delay
                                  ! mov DX, &H0A00         ; Try &H0A00 for a delay in DX
                                  ! int &H15               ; Call timer interrupt
                                  ! pop DS
                               END IF
                             Z0$ = INKEY$
                          That's just for the keyboard I/O stroke capture. All the mouse code
                          which similarly has to be time slice releasing aware is lots and lots
                          more code as well. Plus if you wish to alter the granularity of the
                          time slice release, you'll have to provide for other than the hard
                          coded time set I show above.

                          So just asking the computer to SLEEP to break this isn't going to work
                          in any application that I've found, which is to be run in a multi-tasking
                          environment, even in a good pre-emptive one.

                          You also might want to be aware that the entire arena of ON TIMER, and
                          the communications port stuff, is also bound up in these same time
                          slice release problems for PB for DOS as used in other than straight
                          DOS. While you are SLEEPING, as you suggest, maybe another PB program
                          is controlling the loading chute for unloading cows out of a trailer
                          into the pen! If you are fast enough, you might even be able to remove
                          the whole chute for a few seconds to use it on another trailer if there
                          are now cows close enough to the end of the trailer for a moment! But
                          if you do, that chute had better be back in place for the next cows in
                          the bunch .. or else they will all fall out the trailer and break
                          their legs kawhomp!

                          Don't laugh too hard. That is exactly what happens to real-time comm
                          programs if you use SLEEP as you suggest, as well as comm port I/O
                          routines in PB under multi-tasking work. The cows, in humor here, are
                          the data bytes in the I/O stream which have no place to go, even if
                          they are buffered, if SLEEP has destroyed the real-time cadence needed
                          to keep all this together. And that in multiple PB applications as
                          well like I use here.

                          And it is *NOT* fair to criticize PB for no awareness of this. PB has
                          done, I'm sure a wonderful job of taking care of the special needs of
                          this in the work with PBCC and so on. I don't use the products for
                          WIN though I have paid for them and have them here. So the SLEEP
                          function may well be coded in them such that this is not an issue if
                          you use the WIN tools for WIN work.

                          **********************************

                          Hint! I would *LOVE* to see the next release of PB platform aware!

                          **********************************

                          But you sure can't use the PB 3.5 for DOS tools for it and expect to not
                          get in trouble in the multi-tasking world, as DOS originally provided
                          for how to go to SLEEP!

                          When In DOS we go to sleep, no fourteen angels watch to keep.
                          Unless'n you put'em der, Massuh!





                          ------------------
                          Mike Luther
                          [email protected]
                          Mike Luther
                          [email protected]

                          Comment


                            #14
                            Hint! I would *LOVE* to see the next release of PB platform aware
                            What does that mean?

                            A. The IDE knows on what platform it's running and does something different?
                            No thanks, I like my IDE predictable.

                            B. Generated code is optimized for the system on which it is compiled?
                            No thanks, I don't use the same equipment as my customers.

                            C. The generated code contains conditional executable code; which code executes is a function of the system on which the software is running?
                            No thanks.. or come to think of it, you've finally come up with an excuse for me to try the #BLOAT metastatement.


                            ??????

                            MCM


                            Michael Mattias
                            Tal Systems (retired)
                            Port Washington WI USA
                            [email protected]
                            http://www.talsystems.com

                            Comment


                              #15
                              Your problem statement was:
                              -------------------------->
                              PROCESS:
                              Close #3
                              Shell "Process_1"
                              Shell "DEL File_1"
                              Shell "DEL File_2"
                              GoTo MAIN

                              All of the sudden, it started to give me problems on one pc. It shells to "Process_1" and executes that .exe just fine. It even continues to del file_1 and file_2. However, the command window never closes and the program "freezes", it does not "GoTo Main"... The problem is that the shell to Process_1.exe is leaving the command window open in what appears to be a program "hang".
                              <--------------------------
                              I can't see where you have ruled out the possibility that your SHELL to Process_1 never completes.

                              What if File_1 and File_2 were not actually created? They would then "appear" to have been deleted when you "hang." The symptoms you describe are really indicative of a failure in Process_1 when the two files were never created, and you never "got to" the DEL functions or to MAIN because the Shell to Process_1 never completed.

                              I think the code you are looking at may be a "stalking horse." Check to see if the files actually get created and look for a reason why Process_1 fails to complete. Your problem suggests that you have a COBOL program (Process_1) that has compatibility issues with one of your PCs.
                              ------------------
                              Jim C.



                              [This message has been edited by Jim Cody (edited October 12, 2003).]
                              Jim C.

                              Comment

                              Working...
                              X
                              😀
                              🥰
                              🤢
                              😎
                              😡
                              👍
                              👎