267 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			267 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* fmt.c */
 | 
						|
 | 
						|
/* usage: fmt [-width] [files]...
 | 
						|
 *
 | 
						|
 * Fmt rearrages text in order to make each line have roughly the
 | 
						|
 * same width.  Indentation and word spacing is preserved.
 | 
						|
 *
 | 
						|
 * The default width is 72 characters, but you can override that via -width.
 | 
						|
 * If no files are given on the command line, then it reads stdin.
 | 
						|
 */
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
 | 
						|
#ifndef TRUE
 | 
						|
# define TRUE	1
 | 
						|
# define FALSE	0
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
 | 
						|
int	width = 72;	/* the desired line width */
 | 
						|
int	isblank;	/* is the current output line blank? */
 | 
						|
int	indent;		/* width of the indentation */
 | 
						|
char	ind[512];	/* indentation text */
 | 
						|
char	word[1024];	/* word buffer */
 | 
						|
 | 
						|
/* This function displays a usage message and quits */
 | 
						|
void usage()
 | 
						|
{
 | 
						|
	fprintf(stderr, "usage: fmt [-width] [files]...\n");
 | 
						|
	exit(2);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/* This function outputs a single word.  It takes care of spacing and the
 | 
						|
 * newlines within a paragraph.
 | 
						|
 */
 | 
						|
void putword()
 | 
						|
{
 | 
						|
	int		i;		/* index into word[], or whatever */
 | 
						|
	int		ww;		/* width of the word */
 | 
						|
	int		sw;		/* width of spacing after word */
 | 
						|
	static int	psw;		/* space width of previous word */
 | 
						|
	static int	tab;		/* the width of text already written */
 | 
						|
 | 
						|
 | 
						|
	/* separate the word and its spacing */
 | 
						|
	for (ww = 0; word[ww] && word[ww] != ' '; ww++)
 | 
						|
	{
 | 
						|
	}
 | 
						|
	sw = strlen(word) - ww;
 | 
						|
	word[ww] = '\0';
 | 
						|
 | 
						|
	/* if no spacing (that is, the word was at the end of the line) then
 | 
						|
	 * assume 1 space unless the last char of the word was punctuation
 | 
						|
	 */
 | 
						|
	if (sw == 0)
 | 
						|
	{
 | 
						|
		sw = 1;
 | 
						|
		if (word[ww - 1] == '.' || word[ww - 1] == '?' || word[ww - 1] == '!')
 | 
						|
			sw = 2;
 | 
						|
	}
 | 
						|
 | 
						|
	/* if this is the first word on the line... */
 | 
						|
	if (isblank)
 | 
						|
	{
 | 
						|
		/* output the indentation first */
 | 
						|
		fputs(ind, stdout);
 | 
						|
		tab = indent;
 | 
						|
	}
 | 
						|
	else /* text has already been written to this output line */
 | 
						|
	{
 | 
						|
		/* will the word fit on this line? */
 | 
						|
		if (psw + ww + tab <= width)
 | 
						|
		{
 | 
						|
			/* yes - so write the previous word's spacing */
 | 
						|
			for (i = 0; i < psw; i++)
 | 
						|
			{
 | 
						|
				putchar(' ');
 | 
						|
			}
 | 
						|
			tab += psw;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			/* no, so write a newline and the indentation */
 | 
						|
			putchar('\n');
 | 
						|
			fputs(ind, stdout);
 | 
						|
			tab = indent;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* write the word itself */
 | 
						|
	fputs(word, stdout);
 | 
						|
	tab += ww;
 | 
						|
 | 
						|
	/* remember this word's spacing */
 | 
						|
	psw = sw;
 | 
						|
 | 
						|
	/* this output line isn't blank anymore. */
 | 
						|
	isblank = FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/* This function reformats text. */
 | 
						|
void fmt(in)
 | 
						|
	FILE	*in;		/* the name of the input stream */
 | 
						|
{
 | 
						|
	int	ch;		/* character from input stream */
 | 
						|
	int	prevch;		/* the previous character in the loop */
 | 
						|
	int	i;		/* index into ind[] or word[] */
 | 
						|
	int	inword;		/* boolean: are we between indent & newline? */
 | 
						|
 | 
						|
 | 
						|
	/* for each character in the stream... */
 | 
						|
	for (indent = -1, isblank = TRUE, inword = FALSE, i = 0, prevch = '\n';
 | 
						|
	     (ch = getc(in)) != EOF;
 | 
						|
	     prevch = ch)
 | 
						|
	{
 | 
						|
		/* is this the end of a line? */
 | 
						|
		if (ch == '\n')
 | 
						|
		{
 | 
						|
			/* if end of last word in the input line */
 | 
						|
			if (inword)
 | 
						|
			{
 | 
						|
				/* if it really is a word */
 | 
						|
				if (i > 0)
 | 
						|
				{
 | 
						|
					/* output it */
 | 
						|
					word[i] = '\0';
 | 
						|
					putword();
 | 
						|
				}
 | 
						|
			}
 | 
						|
			else /* blank line in input */
 | 
						|
			{
 | 
						|
				/* finish the previous paragraph */
 | 
						|
				if (!isblank)
 | 
						|
				{
 | 
						|
					putchar('\n');
 | 
						|
					isblank = TRUE;
 | 
						|
				}
 | 
						|
 | 
						|
				/* output a blank line */
 | 
						|
				putchar('\n');
 | 
						|
			}
 | 
						|
 | 
						|
			/* continue with next input line... */
 | 
						|
			indent = -1;
 | 
						|
			i = 0;
 | 
						|
			inword = FALSE;
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		/* if we're expecting indentation now... */
 | 
						|
		if (indent < 0)
 | 
						|
		{
 | 
						|
			/* if this is part of the indentation... */
 | 
						|
			if (ch == ' ' || ch == '\t')
 | 
						|
			{
 | 
						|
				/* remember it */
 | 
						|
				ind[i++] = ch;
 | 
						|
			}
 | 
						|
			else /* end of indentation */
 | 
						|
			{
 | 
						|
				/* mark the end of the indentation string */
 | 
						|
				ind[i] = '\0';
 | 
						|
 | 
						|
				/* calculate the width of the indentation */
 | 
						|
				for (i = indent = 0; ind[i]; i++)
 | 
						|
				{
 | 
						|
					if (ind[i] == '\t')
 | 
						|
						indent = (indent | 7) + 1;
 | 
						|
					else
 | 
						|
						indent++;
 | 
						|
				}
 | 
						|
 | 
						|
				/* reset the word index */
 | 
						|
				i = 0;
 | 
						|
 | 
						|
				/* reprocess that last character */
 | 
						|
				ungetc(ch, in);
 | 
						|
			}
 | 
						|
 | 
						|
			/* continue in the for-loop */
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		/* if we get here, we're either in a word or in the space
 | 
						|
		 * after a word.
 | 
						|
		 */
 | 
						|
		inword = TRUE;
 | 
						|
 | 
						|
		/* is this the start of a new word? */
 | 
						|
		if (ch != ' ' && prevch == ' ')
 | 
						|
		{
 | 
						|
			/* yes!  output the previous word */
 | 
						|
			word[i] = '\0';
 | 
						|
			putword();
 | 
						|
 | 
						|
			/* reset `i' to the start of the word[] buffer */
 | 
						|
			i = 0;
 | 
						|
		}
 | 
						|
		word[i++] = ch;
 | 
						|
	}
 | 
						|
 | 
						|
	/* if necessary, write a final newline */
 | 
						|
	if (!isblank)
 | 
						|
	{
 | 
						|
		putchar('\n');
 | 
						|
		isblank = TRUE;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
int main(argc, argv)
 | 
						|
	int	argc;
 | 
						|
	char	**argv;
 | 
						|
{
 | 
						|
	FILE	*in;	/* an input stream */
 | 
						|
	int	error;	/* if non-zero, then an error occurred */
 | 
						|
	int	i;
 | 
						|
 | 
						|
 | 
						|
	/* handle the -width flag, if given */
 | 
						|
	if (argc > 1 && argv[1][0] == '-')
 | 
						|
	{
 | 
						|
		width = atoi(argv[1] + 1);
 | 
						|
		if (width <= 0)
 | 
						|
		{
 | 
						|
			usage();
 | 
						|
		}
 | 
						|
		argc--;
 | 
						|
		argv++;
 | 
						|
	}
 | 
						|
 | 
						|
	/* if no filenames given, then process stdin */
 | 
						|
	if (argc == 1)
 | 
						|
	{
 | 
						|
		fmt(stdin);
 | 
						|
	}
 | 
						|
	else /* one or more filenames given */
 | 
						|
	{
 | 
						|
		for (error = 0, i = 1; i < argc; i++)
 | 
						|
		{
 | 
						|
			in = fopen(argv[i], "r");
 | 
						|
			if (!in)
 | 
						|
			{
 | 
						|
				perror(argv[i]);
 | 
						|
				error = 3;
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				fmt(in);
 | 
						|
				fclose(in);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* exit, possibly indicating an error */
 | 
						|
	exit(error);
 | 
						|
	/*NOTREACHED*/
 | 
						|
}
 |