1 /******************************************************************************
2                                pamexec
3 *******************************************************************************
4   Split a Netpbm format input file into multiple Netpbm format output streams
5   with one image per output stream and pipe this into the specified command.
6 
7   By Bryan Henderson, Olympia WA; June 2000
8   and Michael Pot, New Zealand, August 2011
9 
10   Contributed to the public domain by its authors.
11 
12 ******************************************************************************/
13 
14 #define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
15 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
16 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
17 
18 #include <string.h>
19 #include <stdio.h>
20 #include <errno.h>
21 
22 #include "pm_c_util.h"
23 #include "shhopt.h"
24 #include "nstring.h"
25 #include "mallocvar.h"
26 #include "pam.h"
27 
28 struct cmdlineInfo {
29     /* All the information the user supplied in the command line,
30        in a form easy for the program to use.
31     */
32     const char * command;
33     const char * inputFileName;
34     unsigned int debug;
35     unsigned int check;
36 };
37 
38 
39 
40 
41 static void
parseCommandLine(int argc,const char ** argv,struct cmdlineInfo * const cmdlineP)42 parseCommandLine(int argc, const char ** argv,
43                  struct cmdlineInfo * const cmdlineP) {
44 /*----------------------------------------------------------------------------
45    Note that the pointers we place into *cmdlineP are sometimes to storage
46    in the argv array.
47 -----------------------------------------------------------------------------*/
48     optEntry *option_def;
49         /* Instructions to OptParseOptions3 on how to parse our options.
50          */
51     optStruct3 opt;
52 
53     unsigned int option_def_index;
54 
55     MALLOCARRAY_NOFAIL(option_def, 100);
56 
57     option_def_index = 0;   /* incremented by OPTENT3 */
58     OPTENT3(0,   "debug",   OPT_FLAG,   NULL,         &cmdlineP->debug, 0);
59     OPTENT3(0,   "check",   OPT_FLAG,   NULL,         &cmdlineP->check, 0);
60 
61     opt.opt_table = option_def;
62     opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */
63     opt.allowNegNum = FALSE;   /* We have no parms that are negative numbers */
64 
65     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
66         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
67 
68     if (argc-1 < 1)
69         pm_error("You must specify at least one argument: the shell command "
70                  "to execute");
71     else {
72         cmdlineP->command = argv[1];
73 
74         if (argc-1 < 2)
75             cmdlineP->inputFileName = "-";
76         else {
77             cmdlineP->inputFileName = argv[2];
78 
79             if (argc-1 > 2)
80                 pm_error("Too many arguments.  There are at most two: "
81                          "command and input file name");
82         }
83     }
84 }
85 
86 
87 
88 static void
pipeOneImage(FILE * const infileP,FILE * const outfileP)89 pipeOneImage(FILE * const infileP,
90              FILE * const outfileP) {
91 
92     struct pam inpam;
93     struct pam outpam;
94     enum pm_check_code checkRetval;
95 
96     unsigned int row;
97     tuple * tuplerow;
98 
99     pnm_readpaminit(infileP, &inpam, PAM_STRUCT_SIZE(tuple_type));
100 
101     pnm_checkpam(&inpam, PM_CHECK_BASIC, &checkRetval);
102 
103     outpam = inpam;
104     outpam.file = outfileP;
105 
106     pnm_writepaminit(&outpam);
107 
108     tuplerow = pnm_allocpamrow(&inpam);
109 
110     for (row = 0; row < inpam.height; ++row) {
111         pnm_readpamrow(&inpam, tuplerow);
112         pnm_writepamrow(&outpam, tuplerow);
113     }
114 
115     pnm_freepamrow(tuplerow);
116 }
117 
118 
119 
120 static void
doOneImage(FILE * const ifP,const char * const command,bool const check,const char ** const errorP)121 doOneImage(FILE *        const ifP,
122            const char *  const command,
123            bool          const check,
124            const char ** const errorP) {
125 /*----------------------------------------------------------------------------
126    Run command 'command' on the next image in stream *ifP.
127 
128    Return as *errorP a text explanation of how we failed, or NULL if we
129    didn't.
130 -----------------------------------------------------------------------------*/
131     FILE * ofP;
132 
133     ofP = popen(command, "w");
134 
135     if (ofP == NULL)
136         pm_asprintf(errorP,
137                     "Failed to start shell to run command '%s'.  "
138                     "errno = %d (%s)",
139                     command, errno, strerror(errno));
140     else {
141         int rc;
142 
143         pipeOneImage(ifP, ofP);
144 
145         rc = pclose(ofP);
146 
147         if (check && rc != 0)
148             pm_asprintf(errorP, "Command '%s' terminated abnormally "
149                         "or with nonzero exit status", command);
150         else
151             *errorP = NULL;
152     }
153 }
154 
155 
156 
157 int
main(int argc,const char * argv[])158 main(int argc, const char *argv[]) {
159 
160     struct cmdlineInfo cmdline;
161 
162     FILE *       ifP;         /* Input file pointer */
163     int          eof;         /* No more images in input */
164     unsigned int imageSeq;
165         /* Sequence number of current image in input file.  First = 0.
166            (Useful for tracking down problems).
167         */
168 
169     pm_proginit(&argc, argv);
170 
171     parseCommandLine(argc, argv, &cmdline);
172 
173     ifP = pm_openr(cmdline.inputFileName);
174 
175     for (eof = FALSE, imageSeq = 0; !eof; ++imageSeq) {
176         const char * error;
177 
178         doOneImage(ifP, cmdline.command, cmdline.check, &error);
179 
180         if (error) {
181             pm_error("Failed on image %u: %s", imageSeq, error);
182             pm_strfree(error);
183         }
184 
185         pnm_nextimage(ifP, &eof);
186     }
187     pm_close(ifP);
188 
189     return 0;
190 }
191 
192 
193 
194