diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 0000000..d1b39f2
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,17 @@
+version: 2
+
+jobs:
+ build:
+ docker:
+ - image: circleci/python
+ steps:
+ - checkout
+ - run:
+ name: Build and install
+ command: 'sudo make install'
+ - run:
+ name: Test dependencies
+ command: 'sudo apt-get update; sudo apt-get install libtest-differences-perl'
+ - run:
+ name: Run tests
+ command: 'make test'
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..288a402
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,11 @@
+language: python
+
+install: true
+
+addons:
+ apt:
+ packages:
+ - libtest-differences-perl
+
+script:
+ - make test
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..501a5e1
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,2 @@
+Paul Lutus
+Shriram V
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..a61f045
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,6 @@
+Version 1.1 07/10/2017. Rekindled.
+
+ * pep8
+ * testcases and todo items
+
+Version 1.0 04/14/2011. Initial Public Release.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8cdb845
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ {description}
+ Copyright (C) {year} {fullname}
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ {signature of Ty Coon}, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1eb0681
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,10 @@
+all:
+
+install:
+ install -D beautify_bash.py $(DESTDIR)/usr/bin/beautify_bash
+
+test:
+ prove -f
+ prove -v -f t_todo || true
+
+.PHONY: all install test
diff --git a/README.md b/README.md
index 984a4db..057131b 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,8 @@ beautify_bash
Code formatter / beautifier for bash written in python by
Paul Lutus (a remake of previous version in Ruby).
+[](https://travis-ci.org/shri314/beautify_bash)
+
For further details please see the following blog record
http://arachnoid.com/python/beautify_bash_program.html
diff --git a/beautify_bash.1 b/beautify_bash.1
new file mode 100644
index 0000000..1e3bfe0
--- /dev/null
+++ b/beautify_bash.1
@@ -0,0 +1,36 @@
+.\" Hey, EMACS: -*- nroff -*-
+.\" (C) Copyright 2017 Mike Mestnik ,
+.\"
+.\" First parameter, NAME, should be all caps
+.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
+.\" other parameters are allowed: see man(7), man(1)
+.TH Beautify-bash 1 "July 9 2017"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh disable hyphenation
+.\" .hy enable hyphenation
+.\" .ad l left justify
+.\" .ad b justify to both left and right margins
+.\" .nf disable filling
+.\" .fi enable filling
+.\" .br insert line break
+.\" .sp insert n+1 empty lines
+.\" for manpage-specific macros, see man(7)
+.SH NAME
+beautify-bash \- A beautifier for Bash shell scripts written in Python
+.SH SYNOPSIS
+.B beautify-bash
+.RI " files" ...
+.SH DESCRIPTION
+This manual page documents briefly the
+.B beautify-bash
+command.
+.PP
+.\" TeX users may be more comfortable with the \fB\fP and
+.\" \fI\fP escape sequences to invode bold face and italics,
+.\" respectively.
+\fBbeautify-bash\fP is a program that indets and stuff Bash shell scripts.
+.SH OPTIONS
+These programs follow the usual dashe (`-') means use stdin/out.
+Files are modified and backups created with tilde (`~') suffix.
diff --git a/beautify_bash.py b/beautify_bash.py
index 091124f..53d49e4 100644
--- a/beautify_bash.py
+++ b/beautify_bash.py
@@ -22,6 +22,7 @@
import re
import sys
+import getopt
PVERSION = '1.0'
@@ -42,103 +43,105 @@ def write_file(self, fp, data):
def beautify_string(self, data, path=''):
tab = 0
+ wrap_tab = ""
case_stack = []
- in_here_doc = False
- defer_ext_quote = False
- in_ext_quote = False
ext_quote_string = ''
here_string = ''
output = []
line = 1
for record in re.split('\n', data):
- record = record.rstrip()
- stripped_record = record.strip()
-
+ test_record = stripped_record = record.strip()
+ # strip out any escaped single characters
+ test_record = re.sub(r'\\.', '', test_record)
+ # remove '#' comments
+ test_record = re.sub(r'(\A|\s)(#.*)', '', test_record, 1)
# collapse multiple quotes between ' ... '
- test_record = re.sub(r'\'.*?\'', '', stripped_record)
+ test_record = re.sub(r'\'.*?\'', '', test_record)
# collapse multiple quotes between " ... "
test_record = re.sub(r'".*?"', '', test_record)
# collapse multiple quotes between ` ... `
test_record = re.sub(r'`.*?`', '', test_record)
# collapse multiple quotes between \` ... ' (weird case)
test_record = re.sub(r'\\`.*?\'', '', test_record)
- # strip out any escaped single characters
- test_record = re.sub(r'\\.', '', test_record)
- # remove '#' comments
- test_record = re.sub(r'(\A|\s)(#.*)', '', test_record, 1)
- if(not in_here_doc):
- if(re.search('<<-?', test_record)):
- here_string = re.sub(
- '.*<<-?\s*[\'|"]?([_|\w]+)[\'|"]?.*', '\\1', stripped_record, 1)
- in_here_doc = (len(here_string) > 0)
- if(in_here_doc): # pass on with no changes
- output.append(record)
+
+ if(here_string): # pass on with no changes
# now test for here-doc termination string
- if(re.search(here_string, test_record) and not re.search('<<', test_record)):
- in_here_doc = False
- else: # not in here doc
- if(in_ext_quote):
- if(re.search(ext_quote_string, test_record)):
- # provide line after quotes
- test_record = re.sub(
- '.*%s(.*)' % ext_quote_string, '\\1', test_record, 1)
- in_ext_quote = False
- else: # not in ext quote
- if(re.search(r'(\A|\s)(\'|")', test_record)):
- # apply only after this line has been processed
- defer_ext_quote = True
- ext_quote_string = re.sub(
- '.*([\'"]).*', '\\1', test_record, 1)
- # provide line before quote
- test_record = re.sub(
- '(.*)%s.*' % ext_quote_string, '\\1', test_record, 1)
- if(in_ext_quote):
+ if(record == here_string):
+ here_string = ''
+ output.append(record)
+ continue
+
+ if(ext_quote_string):
+ if(re.search(ext_quote_string, test_record)):
+ # provide line after quotes
+ test_record = re.sub('.*%s(.*)' % ext_quote_string, '\\1', test_record, 1)
+ ext_quote_string = ''
+ # pass on left side unchanged
+ output.append(record.rstrip())
+ else:
# pass on unchanged
output.append(record)
- else: # not in ext quote
- inc = len(re.findall(
- '(\s|\A|;)(case|then|do)(;|\Z|\s)', test_record))
- inc += len(re.findall('(\{|\(|\[)', test_record))
- outc = len(re.findall(
- '(\s|\A|;)(esac|fi|done|elif)(;|\)|\||\Z|\s)', test_record))
- outc += len(re.findall('(\}|\)|\])', test_record))
- if(re.search(r'\besac\b', test_record)):
- if(len(case_stack) == 0):
- sys.stderr.write(
- 'File %s: error: "esac" before "case" in line %d.\n' % (
- path, line)
- )
- else:
- outc += case_stack.pop()
- # sepcial handling for bad syntax within case ... esac
- if(len(case_stack) > 0):
- if(re.search('\A[^(]*\)', test_record)):
- # avoid overcount
- outc -= 2
- case_stack[-1] += 1
- if(re.search(';;', test_record)):
- outc += 1
- case_stack[-1] -= 1
- # an ad-hoc solution for the "else" keyword
- else_case = (
- 0, -1)[re.search('^(else)', test_record) != None]
- net = inc - outc
- tab += min(net, 0)
- extab = tab + else_case
- extab = max(0, extab)
- output.append(
- (self.tab_str * self.tab_size * extab) + stripped_record)
- tab += max(net, 0)
- if(defer_ext_quote):
- in_ext_quote = True
- defer_ext_quote = False
- if(re.search(r'\bcase\b', test_record)):
- case_stack.append(0)
+ continue
+
+ if(re.search(r'[\'"]', test_record)):
+ # apply only after this line has been processed
+ ext_quote_string = re.sub('[^\'"]*([\'"]).*', '\\1', test_record, 1)
+ # provide line before quote
+ test_record = re.sub('(.*)%s.*' % ext_quote_string, '\\1', test_record, 1)
+ stripped_record = record.lstrip()
+
+ inc = len(re.findall('(\s|\A|;)(case|then|do)(;|\Z|\s)', test_record))
+ inc += len(re.findall('(\{|\(|\[)', test_record))
+ outc = len(re.findall('(\s|\A|;)(esac|fi|done|elif)(;|\)|\||\Z|\s)', test_record))
+ outc += len(re.findall('(\}|\)|\])', test_record))
+
+ if(re.search(r'\besac\b', test_record)):
+ if(len(case_stack) == 0):
+ sys.stderr.write('File %s: error: "esac" before "case" in line %d.\n' % (path, line))
+ else:
+ outc += case_stack.pop()
+
+ # sepcial handling for bad syntax within case ... esac
+ if(len(case_stack) > 0):
+ if(re.search('\A[^(]*\)', test_record)):
+ # avoid overcount
+ outc -= 2
+ case_stack[-1] += 1
+ if(re.search(';;', test_record)):
+ outc += 1
+ case_stack[-1] -= 1
+
+ # an ad-hoc solution for the "else" keyword
+ else_case = (0, -1)[re.search('^(else)', test_record) is not None]
+
+ net = inc - outc
+ tab += min(net, 0)
+ extab = tab + else_case
+ extab = max(0, extab)
+ tab += max(net, 0)
+
+ if(re.search(r'^\s*$', stripped_record) and wrap_tab == ""):
+ output.append("")
+ else:
+ output.append((self.tab_str * self.tab_size * extab) + wrap_tab + stripped_record)
+
+ if(re.search(r'\\\s*$', test_record)
+ or re.search(r'[&][&]\s*$', test_record)
+ or re.search(r'[|]\s*$', test_record)
+ ):
+ wrap_tab = self.tab_str * self.tab_size
+ else:
+ wrap_tab = ""
+
+ if(re.search(r'\bcase\b', test_record)):
+ case_stack.append(0)
+ if(re.search('<<-?', test_record)):
+ here_string = re.sub('.*<<-?\s*[\'"]?([\w]+)[\'"]?.*', '\\1', record.strip(), 1)
+
line += 1
error = (tab != 0)
if(error):
- sys.stderr.write(
- 'File %s: error: indent/outdent mismatch: %d.\n' % (path, tab))
+ sys.stderr.write('File %s: error: indent/outdent mismatch: %d.\n' % (path, tab))
return '\n'.join(output), error
def beautify_file(self, path):
@@ -156,15 +159,29 @@ def beautify_file(self, path):
self.write_file(path, result)
return error
+ def usage_ex(self, err_val):
+ sys.stderr.write('Usage: ' + sys.argv[0] + ' [-h|-t ] [|-]...\n')
+ sys.exit(err_val)
+
def main(self):
+ try:
+ opts, paths = getopt.getopt(sys.argv[1:], "ht:", "help")
+ except getopt.GetoptError as err:
+ print(err)
+ self.usage_ex(2)
+
+ for o, v in opts:
+ if o == '-t':
+ self.tab_size = int(v)
+ elif o in ('-h', '--help'):
+ self.usage_ex(0)
+
+ if(len(paths) < 1):
+ paths.append('-')
+
error = False
- sys.argv.pop(0)
- if(len(sys.argv) < 1):
- sys.stderr.write(
- 'usage: shell script filenames or \"-\" for stdin.\n')
- else:
- for path in sys.argv:
- error |= self.beautify_file(path)
+ for path in paths:
+ error |= self.beautify_file(path)
sys.exit((0, 1)[error])
# if not called as a module
diff --git a/t/basic.t b/t/basic.t
new file mode 100644
index 0000000..198e189
--- /dev/null
+++ b/t/basic.t
@@ -0,0 +1,104 @@
+use Test::More tests => 4;
+
+BEGIN {
+ if (!eval q{ use Test::Differences; 1 }) {
+ *eq_or_diff = \&is_deeply;
+ }
+}
+
+delete $ENV{PATH};
+
+sub a {
+return scalar `/usr/bin/env python ./beautify_bash.py -t3 <<"EOM"
+$_[0]
+EOM`
+}
+
+eq_or_diff a(<<'EOM'), <<'EOM', 'If then else';
+echo
+if [ $? -eq 0 ]
+ then
+ echo
+ else
+ echo
+ fi
+EOM
+echo
+if [ $? -eq 0 ]
+then
+ echo
+else
+ echo
+fi
+
+EOM
+
+eq_or_diff a(<<'EOM'), <<'EOM', 'If then else, in func staircase';
+ func() {
+echo
+if [ $? -eq 0 ]
+ then
+ echo
+ else
+ echo
+ fi
+ }
+EOM
+func() {
+ echo
+ if [ $? -eq 0 ]
+ then
+ echo
+ else
+ echo
+ fi
+}
+
+EOM
+
+eq_or_diff a(<<'EOM'), <<'EOM', 'If then else, in func';
+ func()
+ {
+echo
+if [ $? -eq 0 ]
+ then
+echo
+ else
+ echo
+ fi
+ }
+EOM
+func()
+{
+ echo
+ if [ $? -eq 0 ]
+ then
+ echo
+ else
+ echo
+ fi
+}
+
+EOM
+
+eq_or_diff a(<<'EOM'), <<'EOM', 'If then else, in func, if/then oneline';
+ func()
+ {
+echo
+if [ $? -eq 0 ]; then echo
+ else
+ echo
+ fi
+ }
+EOM
+func()
+{
+ echo
+ if [ $? -eq 0 ]; then echo
+ else
+ echo
+ fi
+}
+
+EOM
+
diff --git a/t/elfi.t b/t/elfi.t
new file mode 100644
index 0000000..2a90581
--- /dev/null
+++ b/t/elfi.t
@@ -0,0 +1,34 @@
+use Test::More tests => 1;
+
+BEGIN {
+ if (!eval q{ use Test::Differences; 1 }) {
+ *eq_or_diff = \&is_deeply;
+ }
+}
+
+delete $ENV{PATH};
+
+sub a {
+return scalar `/usr/bin/env python ./beautify_bash.py -t3 <<"EOM"
+$_[0]
+EOM`
+}
+
+eq_or_diff a(<<'EOM'), <<'EOM', 'elif';
+if [ $? -eq 0 ]
+then
+ :
+ elif [ $? -ne 0 ]
+ then
+ :
+fi
+EOM
+if [ $? -eq 0 ]
+then
+ :
+elif [ $? -ne 0 ]
+then
+ :
+fi
+
+EOM
diff --git a/t/heredoc.t b/t/heredoc.t
new file mode 100644
index 0000000..692ed8e
--- /dev/null
+++ b/t/heredoc.t
@@ -0,0 +1,147 @@
+use Test::More tests => 4;
+
+BEGIN {
+ if (!eval q{ use Test::Differences; 1 }) {
+ *eq_or_diff = \&is_deeply;
+ }
+}
+
+delete $ENV{PATH};
+
+sub a {
+return scalar `/usr/bin/env python ./beautify_bash.py -t3 <<"EOM"
+$_[0]
+EOM`
+}
+
+eq_or_diff a(<<'EOM'), <<'EOM', 'Here doc';
+ cat <<"HEHE"
+ if [
+ then
+
+HEHEFOOLER
+ HEHE
+ if [ $? -eq 1 ]
+ then
+HEHE
+if [ $? -eq 0 ]
+ then
+ :
+ fi
+EOM
+cat <<"HEHE"
+ if [
+ then
+
+HEHEFOOLER
+ HEHE
+ if [ $? -eq 1 ]
+ then
+HEHE
+if [ $? -eq 0 ]
+then
+ :
+fi
+
+EOM
+
+eq_or_diff a(<<'EOM'), <<'EOM', 'Here doc';
+ cat < 2;
+
+BEGIN {
+ if (!eval q{ use Test::Differences; 1 }) {
+ *eq_or_diff = \&is_deeply;
+ }
+}
+
+delete $ENV{PATH};
+
+sub a {
+return scalar `/usr/bin/env python ./beautify_bash.py -t3 <<"EOM"
+$_[0]
+EOM`
+}
+
+eq_or_diff a(<<'EOM'), <<'EOM', 'String literals';
+func() {
+ X1="This is a 'test' about quotes"
+ X2='This is a "test" about quotes'
+ Y1="This
+ is a 'te
+ quotes"
+ Y2='This
+ is a "te
+ quotes'
+ Y3="This
+ is a \"te
+ quotes"
+ echo
+}
+EOM
+func() {
+ X1="This is a 'test' about quotes"
+ X2='This is a "test" about quotes'
+ Y1="This
+ is a 'te
+ quotes"
+ Y2='This
+ is a "te
+ quotes'
+ Y3="This
+ is a \"te
+ quotes"
+ echo
+}
+
+EOM
+
+eq_or_diff a(<<'EOM'), <<'EOM', 'String literals, fake here doc';
+func() {
+ Y1="This
+ this is < 1;
+
+BEGIN {
+ if (!eval q{ use Test::Differences; 1 }) {
+ *eq_or_diff = \&is_deeply;
+ }
+}
+
+delete $ENV{PATH};
+
+sub a {
+return scalar `/usr/bin/env python ./beautify_bash.py -t3 <<"EOM"
+$_[0]
+EOM`
+}
+
+eq_or_diff a(<<'EOM'), <<'EOM', 'Lines with empty spaces';
+ func() {
+ echo
+
+ echo
+ }
+EOM
+func() {
+ echo
+
+ echo
+}
+
+EOM
diff --git a/t/wrapping.t b/t/wrapping.t
new file mode 100644
index 0000000..f811bb6
--- /dev/null
+++ b/t/wrapping.t
@@ -0,0 +1,70 @@
+use Test::More tests => 1;
+
+BEGIN {
+ if (!eval q{ use Test::Differences; 1 }) {
+ *eq_or_diff = \&is_deeply;
+ }
+}
+
+delete $ENV{PATH};
+
+sub a {
+return scalar `/usr/bin/env python ./beautify_bash.py -t3 <<"EOM"
+$_[0]
+EOM`
+}
+
+eq_or_diff a(<<'EOM'), <<'EOM', 'lines ending with \\, &&, |, ||';
+func() {
+ echo alpha \
+ beta \
+ gamma \
+ delata
+ echo
+ echo a |
+ grep x |
+ grep y |
+ grep z |
+ grep p
+ echo
+ echo a &&
+ grep x &&
+ grep y &&
+ grep z &&
+ grep p
+ echo
+ echo a ||
+ grep x ||
+ grep y ||
+ grep z ||
+ grep p
+ echo
+}
+EOM
+func() {
+ echo alpha \
+ beta \
+ gamma \
+ delata
+ echo
+ echo a |
+ grep x |
+ grep y |
+ grep z |
+ grep p
+ echo
+ echo a &&
+ grep x &&
+ grep y &&
+ grep z &&
+ grep p
+ echo
+ echo a ||
+ grep x ||
+ grep y ||
+ grep z ||
+ grep p
+ echo
+}
+
+EOM
diff --git a/t_todo/lines.t b/t_todo/lines.t
new file mode 100644
index 0000000..72cd494
--- /dev/null
+++ b/t_todo/lines.t
@@ -0,0 +1,49 @@
+use Test::More tests => 2;
+
+BEGIN {
+ if (!eval q{ use Test::Differences; 1 }) {
+ *eq_or_diff = \&is_deeply;
+ }
+}
+
+delete $ENV{PATH};
+
+sub a {
+return scalar `/usr/bin/env python ./beautify_bash.py -t3 <<"EOM"
+$_[0]
+EOM`
+}
+
+eq_or_diff a(<<'EOM'), <<'EOM', 'Split long lines';
+func() {
+ if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then echo; else; echo; fi; else; echo; fi; else; echo; fi
+}
+EOM
+func() {
+ if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
+ if [ $? -eq 0 ]; then echo; else; echo; fi; else; echo; fi
+ else; echo; fi
+}
+
+EOM
+
+eq_or_diff a(<<'EOM'), <<'EOM', 'Join short lines';
+func() {
+ if [ $? -eq 0 ] &&
+ [ $? -eq 0 ] ||
+ [ $? -eq 0 ]; then
+ echo
+ else
+ echo
+ fi
+}
+EOM
+func() {
+ if [ $? -eq 0 ] && [ $? -eq 0 ] || [ $? -eq 0 ]; then
+ echo
+ else
+ echo
+ fi
+}
+
+EOM
diff --git a/t_todo/metatags.t b/t_todo/metatags.t
new file mode 100644
index 0000000..0c51aee
--- /dev/null
+++ b/t_todo/metatags.t
@@ -0,0 +1,34 @@
+use Test::More tests => 1;
+
+BEGIN {
+ if (!eval q{ use Test::Differences; 1 }) {
+ *eq_or_diff = \&is_deeply;
+ }
+}
+
+delete $ENV{PATH};
+
+sub a {
+return scalar `/usr/bin/env python ./beautify_bash.py -t3 <<"EOM"
+$_[0]
+EOM`
+}
+
+eq_or_diff a(<<'EOM'), <<'EOM', 'Formatter off/on';
+# @formatter:off
+ func() {
+ echo
+
+# @formatter:on
+ echo
+ }
+EOM
+# @formatter:off
+ func() {
+ echo
+
+# @formatter:on
+ echo
+}
+
+EOM