188004Sgad/* 288004Sgad * ------+---------+---------+---------+---------+---------+---------+---------* 388004Sgad * Copyright (c) 2001 - Garance Alistair Drosehn <gad@FreeBSD.org>. 488004Sgad * All rights reserved. 588004Sgad * 688004Sgad * Redistribution and use in source and binary forms, with or without 788004Sgad * modification, are permitted provided that the following conditions 888004Sgad * are met: 988004Sgad * 1. Redistributions of source code must retain the above copyright 1088004Sgad * notice, this list of conditions and the following disclaimer. 1188004Sgad * 2. Redistributions in binary form must reproduce the above copyright 1288004Sgad * notice, this list of conditions and the following disclaimer in the 1388004Sgad * documentation and/or other materials provided with the distribution. 1488004Sgad * 1588004Sgad * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1688004Sgad * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1788004Sgad * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1888004Sgad * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1988004Sgad * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2088004Sgad * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2188004Sgad * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2288004Sgad * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2388004Sgad * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2488004Sgad * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2588004Sgad * SUCH DAMAGE. 2688004Sgad * 2788004Sgad * The views and conclusions contained in the software and documentation 2888004Sgad * are those of the authors and should not be interpreted as representing 2988004Sgad * official policies, either expressed or implied, of the FreeBSD Project. 3088004Sgad * 3188004Sgad * ------+---------+---------+---------+---------+---------+---------+---------* 3288004Sgad */ 3388004Sgad 34117623Sgad#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 35117623Sgad__FBSDID("$FreeBSD: releng/10.3/usr.sbin/lpr/chkprintcap/skimprintcap.c 117623 2003-07-15 08:48:30Z gad $"); 3688004Sgad 3788004Sgad#include <sys/types.h> 3888004Sgad 3988004Sgad#include <ctype.h> 4088004Sgad#include <err.h> 4188004Sgad#include <errno.h> 4288004Sgad#include <grp.h> 4388004Sgad#include <stdio.h> 4488004Sgad#include <string.h> 4588004Sgad#include <stdlib.h> 4688004Sgad#include <unistd.h> 4788004Sgad 4888004Sgad#include <sys/param.h> /* needed for lp.h but not used here */ 4988004Sgad#include <dirent.h> /* ditto */ 5088004Sgad#include "lp.h" 5188004Sgad#include "lp.local.h" 5288004Sgad#include "skimprintcap.h" 5388004Sgad 5488004Sgad/* 5588004Sgad * Save the canonical queue name of the entry that is currently being 5688004Sgad * scanned, in case a warning message is printed for the current queue. 5788004Sgad * Only the first 'QENTRY_MAXLEN' characters will be saved, since this 5888004Sgad * is only for warning messages. The variable includes space for the 5988004Sgad * string " (entry " and a trailing ")", when the scanner is in the 6088004Sgad * middle of an entry. When the scanner is not in a specific entry, 6188004Sgad * the variable will be the a null string. 6288004Sgad */ 6388004Sgad#define QENTRY_MAXLEN 30 6488004Sgad#define QENTRY_PREFIX " (entry " 6588004Sgadstatic char skim_entryname[sizeof(QENTRY_PREFIX) + QENTRY_MAXLEN + 2]; 6688004Sgad 6788004Sgad/* 6888004Sgad * isgraph is defined to work on an 'int', in the range 0 to 255, plus EOF. 6988004Sgad * Define a wrapper which can take 'char', either signed or unsigned. 7088004Sgad */ 7188004Sgad#define isgraphch(Anychar) isgraph(((int) Anychar) & 255) 7288004Sgad 7388004Sgadstruct skiminfo * 7488004Sgadskim_printcap(const char *pcap_fname, int verbosity) 7588004Sgad{ 7688004Sgad struct skiminfo *skinf; 7788004Sgad char buff[BUFSIZ]; 7888004Sgad char *ch, *curline, *endfield, *lastchar; 7988004Sgad FILE *pc_file; 8088004Sgad int missing_nl; 8188004Sgad enum {NO_CONTINUE, WILL_CONTINUE, BAD_CONTINUE} is_cont, had_cont; 8288004Sgad enum {CMNT_LINE, ENTRY_LINE, TAB_LINE, TABERR_LINE} is_type, had_type; 8388004Sgad 8488004Sgad skinf = malloc(sizeof(struct skiminfo)); 8588004Sgad memset(skinf, 0, sizeof(struct skiminfo)); 8688004Sgad 8788004Sgad pc_file = fopen(pcap_fname, "r"); 8888004Sgad if (pc_file == NULL) { 8988004Sgad warn("fopen(%s)", pcap_fname); 9088004Sgad skinf->fatalerr++; 9188004Sgad return (skinf); /* fatal error */ 9288004Sgad } 9388004Sgad 9488004Sgad skim_entryname[0] = '0'; 9588004Sgad 9688004Sgad is_cont = NO_CONTINUE; 9788004Sgad is_type = CMNT_LINE; 9888004Sgad errno = 0; 9988004Sgad curline = fgets(buff, sizeof(buff), pc_file); 10088004Sgad while (curline != NULL) { 10188004Sgad skinf->lines++; 10288004Sgad 10388004Sgad /* Check for the expected newline char, and remove it */ 10488004Sgad missing_nl = 0; 10588004Sgad lastchar = strchr(curline, '\n'); 10688004Sgad if (lastchar != NULL) 10788004Sgad *lastchar = '\0'; 10888004Sgad else { 10988004Sgad lastchar = strchr(curline, '\0'); 11088004Sgad missing_nl = 1; 11188004Sgad } 11288004Sgad if (curline < lastchar) 11388004Sgad lastchar--; 11488004Sgad 11588004Sgad /* 11688004Sgad * Check for `\' (continuation-character) at end of line. 11788004Sgad * If there is none, then trim off spaces and check again. 11888004Sgad * This would be a bad line because it looks like it is 11988004Sgad * continued, but it will not be treated that way. 12088004Sgad */ 12188004Sgad had_cont = is_cont; 12288004Sgad is_cont = NO_CONTINUE; 12388004Sgad if (*lastchar == '\\') { 12488004Sgad is_cont = WILL_CONTINUE; 12588004Sgad lastchar--; 12688004Sgad } else { 12788004Sgad while ((curline < lastchar) && !isgraphch(*lastchar)) 12888004Sgad lastchar--; 12988004Sgad if (*lastchar == '\\') 13088004Sgad is_cont = BAD_CONTINUE; 13188004Sgad } 13288004Sgad 13388004Sgad had_type = is_type; 13488004Sgad is_type = CMNT_LINE; 13588004Sgad switch (*curline) { 13688004Sgad case '\0': /* treat zero-length line as comment */ 13788004Sgad case '#': 13888004Sgad skinf->comments++; 13988004Sgad break; 14088004Sgad case ' ': 14188004Sgad case '\t': 14288004Sgad is_type = TAB_LINE; 14388004Sgad break; 14488004Sgad default: 14588004Sgad is_type = ENTRY_LINE; 14688004Sgad skinf->entries++; 14788004Sgad 14888004Sgad /* pick up the queue name, to use in warning messages */ 14988004Sgad ch = curline; 15088004Sgad while ((ch <= lastchar) && (*ch != ':') && (*ch != '|')) 15188004Sgad ch++; 15288004Sgad ch--; /* last char of queue name */ 15388004Sgad strcpy(skim_entryname, QENTRY_PREFIX); 15488004Sgad if ((ch - curline) > QENTRY_MAXLEN) { 15588004Sgad strncat(skim_entryname, curline, QENTRY_MAXLEN 15688004Sgad - 1); 15788004Sgad strcat(skim_entryname, "+"); 15888004Sgad } else { 15988004Sgad strncat(skim_entryname, curline, (ch - curline 16088004Sgad + 1)); 16188004Sgad } 16288004Sgad strlcat(skim_entryname, ")", sizeof(skim_entryname)); 16388004Sgad break; 16488004Sgad } 16588004Sgad 16688004Sgad /* 16788004Sgad * Check to see if the previous line was a bad contination 16888004Sgad * line. The check is delayed until now so a warning message 16988004Sgad * is not printed when a "bad continuation" is on a comment 17088004Sgad * line, and it just "continues" into another comment line. 17188004Sgad */ 17288004Sgad if (had_cont == BAD_CONTINUE) { 17388004Sgad if ((had_type != CMNT_LINE) || (is_type != CMNT_LINE) || 17488004Sgad (verbosity > 1)) { 17588004Sgad skinf->warnings++; 17688004Sgad warnx("Warning: blanks after trailing '\\'," 17788004Sgad " at line %d%s", skinf->lines - 1, 17888004Sgad skim_entryname); 17988004Sgad } 18088004Sgad } 18188004Sgad 18288004Sgad /* If we are no longer in an entry, then forget the name */ 18388004Sgad if ((had_cont != WILL_CONTINUE) && (is_type != ENTRY_LINE)) { 18488004Sgad skim_entryname[0] = '\0'; 18588004Sgad } 18688004Sgad 18788004Sgad /* 18888004Sgad * Print out warning for missing newline, done down here 18988004Sgad * so we are sure to have the right entry-name for it. 19088004Sgad */ 19188004Sgad if (missing_nl) { 19288004Sgad skinf->warnings++; 19388004Sgad warnx("Warning: No newline at end of line %d%s", 19488004Sgad skinf->lines, skim_entryname); 19588004Sgad } 19688004Sgad 19788004Sgad /* 19888004Sgad * Check for start-of-entry lines which do not include a 19988004Sgad * ":" character (to indicate the end of the name field). 20088004Sgad * This can cause standard printcap processing to ignore 20188004Sgad * ALL of the following lines. 20288004Sgad * XXXXX - May need to allow for the list-of-names to 20388004Sgad * continue on to the following line... 20488004Sgad */ 20588004Sgad if (is_type == ENTRY_LINE) { 20688004Sgad endfield = strchr(curline, ':'); 20788004Sgad if (endfield == NULL) { 20888004Sgad skinf->warnings++; 20988004Sgad warnx("Warning: No ':' to terminate name-field" 21088004Sgad " at line %d%s", skinf->lines, 21188004Sgad skim_entryname); 21288004Sgad } 21388004Sgad } 21488004Sgad 21588004Sgad /* 21688004Sgad * Now check for cases where this line is (or is-not) a 21788004Sgad * continuation of the previous line, and a person skimming 21888004Sgad * the file would assume it is not (or is) a continuation. 21988004Sgad */ 22088004Sgad switch (had_cont) { 22188004Sgad case NO_CONTINUE: 22288004Sgad case BAD_CONTINUE: 22388004Sgad if (is_type == TAB_LINE) { 22488004Sgad skinf->warnings++; 22588004Sgad warnx("Warning: values-line after line with" 22688004Sgad " NO trailing '\\', at line %d%s", 22788004Sgad skinf->lines, skim_entryname); 22888004Sgad } 22988004Sgad break; 23088004Sgad 23188004Sgad case WILL_CONTINUE: 23288004Sgad if (is_type == ENTRY_LINE) { 23388004Sgad skinf->warnings++; 23488004Sgad warnx("Warning: new entry starts after line" 23588004Sgad " with trailing '\\', at line %d%s", 23688004Sgad skinf->lines, skim_entryname); 23788004Sgad } 23888004Sgad break; 23988004Sgad } 24088004Sgad 24188004Sgad /* get another line from printcap and repeat loop */ 24288004Sgad curline = fgets(buff, sizeof(buff), pc_file); 24388004Sgad } 24488004Sgad 24588004Sgad if (errno != 0) { 24688004Sgad warn("fgets(%s)", pcap_fname); 24788004Sgad skinf->fatalerr++; /* fatal error */ 24888004Sgad } 24988004Sgad 25088004Sgad if (skinf->warnings > 0) 25188004Sgad warnx("%4d warnings from skimming %s", skinf->warnings, 25288004Sgad pcap_fname); 25388004Sgad 25488004Sgad if (verbosity) 25588004Sgad warnx("%4d lines (%d comments), %d entries for %s", 25688004Sgad skinf->lines, skinf->comments, skinf->entries, pcap_fname); 25788004Sgad 25888004Sgad fclose(pc_file); 25988004Sgad return (skinf); 26088004Sgad} 261