/* * Copyright (c) 2020 Roger Seguin * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ static char rcsid[] = "$Id: expandtabs.C,v 1.1 2020/08/29 10:36:45 RogerSeguin Exp $"; /* Description: Replace tabs with spaces Usage: expandtabs outputfile expandtabs inputfile outputfile expandtabs infile */ #include #include #include #include #include #include #include #include #include namespace { int m_tabSz = 8; // How many spaces per tab } using namespace std; int usage(const char *prg) { cout << rcsid + 5 << endl; cout << prg << " - Replace tabs with spaces" << endl; cout << "Usage:" << endl; cout << '\t' << prg << " [-t n] [outputfile]" << endl; cout << '\t' << prg << " [-t n] infile [outputfile]" << endl; cout << '\t' << prg << " -h" << endl; cout << "Options:" << endl; cout << "\t-t n\tset tab size to n spaces [2..8] (default=" << m_tabSz << ")" << endl; cout << "\t-h\tdisplay this message and exit" << endl; cout << "Description:" << endl; cout << "\tIf no parameters are given, input is stdin and output is stdout" << endl; cout << "\tIf only one file name is indicated, the repacement is in-place" << endl; cout << "\t\t(original file backed up with suffix .BAK)" << endl; cout << "\tIf 2 parameters are given, read input file and write output file" << endl; return (0); } // usage() class ReadWriteError: public exception { virtual const char* what() const throw() { return "ReadWriteError"; } }; // class ReadWriteError int readchar(int fd) { char c; int n = read(fd, &c, sizeof (char)); if (n == 1) return (c); if (n == 0) // Nothing to read return (EOF); /* Should never reach here */ if (n == -1) perror("read()"); throw (ReadWriteError()); return (-1); } // readchar() int processFileNumbers (int fdin, int fdout) { /* Read fdin, replace tabs with spaces, output to fdout */ const char SPACE = ' ', NEWLINE = '\n', TAB = '\t'; try { bool loop = true; while (loop) { int c; int pos = 0; while ((c = readchar(fdin)) && (c != EOF) && (c != NEWLINE)) { if (c == TAB) { do { int n = write (fdout, &SPACE, sizeof (char)); if (n != 1) { perror("write()"); throw (ReadWriteError()); } } while ((++pos) % m_tabSz); } else { int n = write (fdout, &c, sizeof (char)); if (n != 1) { perror("write()"); throw (ReadWriteError()); } ++pos; } } if (c == NEWLINE) { int n = write (fdout, &NEWLINE, sizeof (char)); if (n != 1) { perror("write()"); throw (ReadWriteError()); } } else /* EOF */ loop = false; } } catch (...) { cerr << "Exception caught" << endl; return (-1); } return (0); } // processFileNumbers() int processFilenames (const char *infile, const char*outfile) { int fdin, fdout; struct stat stat; int status; fdin = open (infile, O_RDONLY | O_LARGEFILE); if (fdin == -1) { perror (infile); return (-1); } status = fstat (fdin, &stat); if (status == -1) { perror (infile); return (-1); } fdout = open (outfile, O_CREAT | O_TRUNC | O_WRONLY | O_LARGEFILE, stat.st_mode); if (fdout == -1) { perror (outfile); close (fdin); return (-1); } /* Replace tabs with space */ status = processFileNumbers (fdin, fdout); close (fdin); close (fdout); return (0); } // processFilenames() int main(int argc, char *argv[]) { /* Check commandline parameters */ bool fUsage = 0; for (char c; (c = getopt (argc, argv, "ht:")) != EOF;) switch (c) { case 'h': // Help fUsage = 1; break; case 't': // Key value m_tabSz = atoi (optarg); fUsage |= ((m_tabSz < 2) || (m_tabSz > 8)); break; case '?': default: fUsage = 1; } int nfiles = argc - optind; fUsage |= (nfiles > 2); if (fUsage) { usage(basename (argv[0])); // GNU version exit (1); } /* Replace tabs with spaces */ string filename1, filename2; int status; switch (nfiles) { case 0: status = processFileNumbers (0, 1); // (stdin, stdout) break; case 1: filename2 = string(argv[optind]); filename1 = filename2 + ".BAK"; status = rename (filename2.c_str(), filename1.c_str()); if (status == -1) { perror (filename2.c_str()); exit (~0); } status = processFilenames (filename1.c_str(), filename2.c_str()); break; case 2: filename1 = string(argv[optind]); filename2 = string(argv[optind + 1]); status = processFilenames (filename1.c_str(), filename2.c_str()); break; } return (status); } // main() /* $Log: expandtabs.C,v $ Revision 1.1 2020/08/29 10:36:45 RogerSeguin Initial revision */