github.com/xyproto/orbiton/v2@v2.65.12-0.20240516144430-e10a419274ec/fstab.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  )
     6  
     7  // formatFstab can format the contents of /etc/fstab files. The suggested number of spaces is 2.
     8  func formatFstab(data []byte, spaces int) []byte {
     9  	var (
    10  		buf       bytes.Buffer
    11  		nl        = []byte{'\n'}
    12  		longest   = make(map[int]int) // The longest length of a field, for each field index
    13  		byteLines = bytes.Split(data, nl)
    14  	)
    15  
    16  	// Find the longest field length for each field on each line
    17  	for _, line := range byteLines {
    18  		trimmedLine := bytes.TrimSpace(line)
    19  		if len(trimmedLine) == 0 || bytes.HasPrefix(trimmedLine, []byte{'#'}) {
    20  			continue
    21  		}
    22  		// Find the longest field length for each field
    23  		for i, field := range bytes.Fields(trimmedLine) {
    24  			fieldLength := len(string(field))
    25  			if val, ok := longest[i]; ok {
    26  				if fieldLength > val {
    27  					longest[i] = fieldLength
    28  				}
    29  			} else {
    30  				longest[i] = fieldLength
    31  			}
    32  		}
    33  	}
    34  
    35  	// Format the lines nicely
    36  	for i, line := range byteLines {
    37  
    38  		// Get the previous line, if possible
    39  		var prevLineTrimmed []byte
    40  		if (i - 1) > 0 {
    41  			prevLineTrimmed = bytes.TrimSpace(byteLines[i-1])
    42  		}
    43  
    44  		// Get the current line
    45  		thisLineTrimmed := bytes.TrimSpace(line)
    46  
    47  		// Get the next line, if possible
    48  		var nextLineTrimmed []byte
    49  		if (i + 1) < len(byteLines) {
    50  			nextLineTrimmed = bytes.TrimSpace(byteLines[i+1])
    51  		}
    52  
    53  		// Get the next next line, if possible
    54  		var nextNextLineTrimmed []byte
    55  		if (i + 2) < len(byteLines) {
    56  			nextNextLineTrimmed = bytes.TrimSpace(byteLines[i+2])
    57  		}
    58  
    59  		// Get the next next next line, if possible
    60  		var nextNextNextLineTrimmed []byte
    61  		if (i + 3) < len(byteLines) {
    62  			nextNextLineTrimmed = bytes.TrimSpace(byteLines[i+3])
    63  		}
    64  
    65  		// Gather stats for if the lines are blank
    66  		prevLineIsBlank := len(prevLineTrimmed) == 0
    67  		thisLineIsBlank := len(thisLineTrimmed) == 0
    68  		nextLineIsBlank := len(nextLineTrimmed) == 0
    69  		nextNextLineIsBlank := len(nextNextLineTrimmed) == 0
    70  		nextNextNextLineIsBlank := len(nextNextNextLineTrimmed) == 0
    71  
    72  		// Gether stats for if the lines are comments
    73  		prevLineIsComment := bytes.HasPrefix(prevLineTrimmed, []byte{'#'})
    74  		thisLineIsComment := bytes.HasPrefix(thisLineTrimmed, []byte{'#'})
    75  		nextLineIsComment := bytes.HasPrefix(nextLineTrimmed, []byte{'#'})
    76  		nextNextLineIsComment := bytes.HasPrefix(nextNextLineTrimmed, []byte{'#'})
    77  		nextNextNextLineIsComment := bytes.HasPrefix(nextNextNextLineTrimmed, []byte{'#'})
    78  
    79  		// Gether stats for if the lines have content
    80  		prevLineIsContent := !prevLineIsBlank && !prevLineIsComment
    81  		nextLineIsContent := !nextLineIsBlank && !nextLineIsComment
    82  		nextNextLineIsContent := !nextNextLineIsBlank && !nextNextLineIsComment
    83  		nextNextNextLineIsContent := !nextNextNextLineIsBlank && !nextNextNextLineIsComment
    84  
    85  		if thisLineIsBlank {
    86  			if prevLineIsContent && nextLineIsComment {
    87  				buf.Write(nl)
    88  			} else if nextLineIsComment && nextNextLineIsContent {
    89  				buf.Write(nl)
    90  			} else if prevLineIsComment && nextLineIsComment && (nextNextLineIsContent || (nextNextLineIsBlank && nextNextNextLineIsContent)) {
    91  				buf.Write(nl)
    92  			}
    93  		} else if thisLineIsComment {
    94  			if prevLineIsComment && nextLineIsContent {
    95  				buf.Write(nl)
    96  			} else if prevLineIsBlank && nextLineIsContent {
    97  				buf.Write(nl)
    98  			} else if prevLineIsContent && nextLineIsContent {
    99  				buf.Write(nl)
   100  			}
   101  			buf.Write(thisLineTrimmed)
   102  			buf.Write(nl)
   103  		} else { // This line has contents, format the fields
   104  			for i, field := range bytes.Fields(thisLineTrimmed) {
   105  				fieldLength := len(string(field))
   106  				padCount := spaces // Space between the fields if all fields have equal length
   107  				if longest[i] > fieldLength {
   108  					padCount += longest[i] - fieldLength
   109  				}
   110  				buf.Write(field)
   111  				if padCount > 0 {
   112  					buf.Write(bytes.Repeat([]byte{' '}, padCount))
   113  				}
   114  			}
   115  			buf.Write(nl)
   116  		}
   117  	}
   118  	// TODO: Find out why double blank lines are sometimes inserted below, when pressing ctrl-w twice,
   119  	//       to avoid using bytes.ReplaceAll.
   120  	return bytes.ReplaceAll(buf.Bytes(), []byte{'\n', '\n', '\n'}, []byte{'\n', '\n'})
   121  }