From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Mon, 2 Sep 2019 13:46:59 +0900 Subject: Initial commit --- support/dktools/printqdc.ctr | 1359 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1359 insertions(+) create mode 100644 support/dktools/printqdc.ctr (limited to 'support/dktools/printqdc.ctr') diff --git a/support/dktools/printqdc.ctr b/support/dktools/printqdc.ctr new file mode 100644 index 0000000000..35be526772 --- /dev/null +++ b/support/dktools/printqdc.ctr @@ -0,0 +1,1359 @@ +%% options + +copyright owner = Dirk Krause +copyright year = 2016-xxxx +license = bsd + + + +%% module + +#include "dk4conf.h" +#include "dk4types.h" + +#include + +#if DK4_HAVE_STDLIB_H +#ifndef STDLIB_H_INCLUDED +#include +#define STDLIB_H_INCLUDED 1 +#endif +#endif + +#if DK4_HAVE_UNISTD_H +#ifndef UNISTD_H_INCLUDED +#include +#define UNISTD_H_INCLUDED 1 +#endif +#endif + +#if DK4_HAVE_PROCESS_H +#ifndef PROCESS_H_INCLUDED +#include +#define PROCESS_H_INCLUDED 1 +#endif +#endif + + + +/** Requirements to build this program: + * Use 8 bit characters, so we can use the directories from dk4inst.c. + * Signal handling mechanism. +*/ +#if (DK4_CHAR_SIZE == 1) && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) + + + +#if DK4_HAVE_MATH_H +#ifndef MATH_H_INCLUDED +#include +#define MATH_H_INCLUDED 1 +#endif +#endif + +#if DK4_HAVE_ERRNO_H +#ifndef ERRNO_H_INCLUDED +#include +#define ERRNO_H_INCLUDED 1 +#endif +#endif + +#if DK4_HAVE_SYS_TYPES_H +#ifndef SYS_TYPES_H_INCLUDED +#include +#define SYS_TYPES_H_INCLUDED 1 +#endif +#endif + +#if DK4_HAVE_IO_H +#ifndef IO_H_INCLUDED +#include +#define IO_H_INCLUDED 1 +#endif +#endif + +#if DK4_HAVE_FCNTL_H +#ifndef FCNTL_H_INCLUDED +#include +#define FCNTL_H_INCLUDED 1 +#endif +#endif + +#if DK4_HAVE_STRING_H +#ifndef STRING_H_INCLUDED +#include +#define STRING_H_INCLUDED 1 +#endif +#endif + +#include "dk4inst.h" +#include "dk4mpl.h" +#include "dk4mem.h" +#include "dk4mai8ddu.h" +#include "dk4mai8dus.h" +#include "dk4str8.h" +#include "dk4lprng.h" +#include "dk4opt.h" +#include "dk4sock.h" +#include "dk4vers.h" + + + +$!trace-include + + + +/** Information from one LPRng accounting line. +*/ +typedef struct { + char *username; + char *queuename; + char *jobtitle; + char *pagecount; +} lprng_info_t; + + + + +/** Command line options for program. +*/ +static dk4_option_t options[] = { + { { dkT('i'), dkT("interactive"), DK4_OPT_ARG_NONE }, { NULL }, 0}, + { { dkT('s'), dkT("socket"), DK4_OPT_ARG_STRING }, { NULL }, 0}, + { { dkT('h'), dkT("host"), DK4_OPT_ARG_STRING }, { NULL }, 0}, + { { dkT('p'), dkT("port"), DK4_OPT_ARG_UNSIGNED }, { NULL }, 0}, + { { dkT('\0'),dkT("help"), DK4_OPT_ARG_NONE}, { NULL }, 0}, + { { dkT('\0'),dkT("version"), DK4_OPT_ARG_NONE}, { NULL }, 0}, + { { dkT('\0'),dkT("license"), DK4_OPT_ARG_NONE}, { NULL }, 0} +}; + + + +/** Help text. +*/ +static const char * const help_text[] = { +$!text + +Print quota daemon client + +printqdc [-i] [-s path] | [-h host -p port] + +-i Interactive mode instead of default pipe mode. +--interactive + +-s path Path name for local UNIX domain socket. +--socket=path + +-h host Host name of remote host. +--host=host + +-p port Port number at remote host. +--port=port + +--help Show this help text. +--version Show version number. +--license Show license information. + +$!end +}; + + + +/** License conditions text. +*/ +static const char * const license_text[] = { +$!text preprocessor + +This software uses code from the following projects, either directly or as +a library: + +dktools Dirk Krause's tools and libraries. + See http://dktools.sourceforge.net/ for more information. + +DK tools and libraries license +============================== +Copyright (c) 2015-2016, Dirk Krause +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* 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. +* Neither the name of the Dirk Krause nor the names of contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + +$!end +}; + + + +/** Version information. +*/ +static const char version_text[] = { DKT_VERSION_C8 }; + + + +/** Constant text keywords used by the module. +*/ +static const char * const pqdc_kw[] = { +$!string-table +# +# 0 Newline +# +\n +# +# 1 Space +# + +# +# 2 Default job name +# +x@x+1 +# +# 3 Default job title +# +Print job +# +# 4 +# +-> +# +# 5 6 +# +<- " +"\n +# +# 7 ... 10 +# +printqdc: +ERROR: Port number too large!\n +ERROR: Port number required!\n +ERROR: Host name required!\n +# +# 11 ... 15 +# +ERROR: Failed to shut down socket!\n +ERROR: Failed to send data!\n +ERROR: Failed to open connection to accounting system!\n +ERROR: Failed to construct request (would be too long)!\n +ERROR: Incomplete information to construct request!\n +# +# 16 ... 20 +# +ERROR: Pagecounter not numeric!\n +ERROR: Too few arguments for request!\n +ERROR: Unknown command keyword!\n +ERROR: Missing arguments!\n +ERROR: Interrupted by signal!\n +# +# 21 ... 22 +# +ERROR: Failed to install signal handlers!\n +ERROR: Failed to restore signal handlers!\n +# +# 23 +# +ERROR: Failed to initialize socket subsystem!\n +$!end +}; + + + +/** Default socket name. +*/ +static const char def_sock_name[] = { + DK4_INST_DIR_VAR "/run/printqd/printqd.sock" +}; + + + +/** Protocol keywords at start of accounting line. +*/ +static const char * const command_names[] = { +$!string-table +# +# 0 ... 5 LPRng +# +jobstart +jobend +start +end +filestart +fileend +# +# 6 ... 11 printqd +# +info +acct-check +acct-start +acct-end +acct-charge +control +$!end +}; + + +/** Responses from printqd +*/ +static const char * const responses[] = { +$!string-table +ACCEPT +HOLD +REMOVE +$!end +}; + +/** Data exchange buffer. +*/ +static char inbuf[4096]; + + +/** Copy for inspection. +*/ +static char cpbuf[sizeof(inbuf)]; + + +/** Host name for network connection. +*/ +static char *host_name = NULL; + + +/** Socket file name for local connection. +*/ +static char *sock_name = NULL; + + +/** Number of options. +*/ +static size_t szoptions = sizeof(options)/sizeof(dk4_option_t); + + +/** Normal exit value (when run as pipe). +*/ +static int exval = LPRNG_EXIT_SUCCESS; + + + +/** Help (1), version (2), license (4). +*/ +static int hvl = 0; + + + +/** Exit value for interactive mode. +*/ +static int iexval = EXIT_FAILURE; + + +/** Flag: Interactive mode (-i option). +*/ +static int intera = 0; + + +/** Flag: EOF was found. +*/ +static int eof_found = 0; + + +/** Flag: Response already found and processed. +*/ +static int found_response = 0; + + +#ifdef SIGPIPE +/** Indicator: SIGPIPE signal received. +*/ +static +DK4_VOLATILE +dk4_sig_atomic_t sig_had_pipe = 0; +#endif + + +/** Indicator: SIGINT signal received. +*/ +static +DK4_VOLATILE +dk4_sig_atomic_t sig_had_int = 0; + + +/** Indicator: SIGTERM signal received. +*/ +static +DK4_VOLATILE +dk4_sig_atomic_t sig_had_term = 0; + + + +/** Port number for network connection. +*/ +static unsigned short portno = 0U; + + +/** Pass pointer through. + Use of this function is recommended by CERT C coding standard + to avoid optimizing out. + @param ptr Pointer to pass through. + @return The ptr argument. +*/ +static +DK4_VOLATILE +dk4_sig_atomic_t * +sig_pass_pointer(DK4_VOLATILE dk4_sig_atomic_t *ptr) +{ + return ptr; +} + + +#ifdef SIGPIPE +/** Handler for SIGPIPE signal. + @param signo Signal number (always SIGPIPE, ignored). +*/ +static +void +sig_handler_pipe(int signo) +{ + *sig_pass_pointer(&sig_had_pipe) = 1; +} +#endif + + +/** Handler for SIGINT signal. + @param signo Signal number (always SIGINT, ignored). +*/ +static +void +sig_handler_int(int signo) +{ + *sig_pass_pointer(&sig_had_int) = 1; +} + + +/** Handler for SIGTERM signal. + @param signo Signal number (always SIGTERM, ignored). +*/ +static +void +sig_handler_term(int signo) +{ + *sig_pass_pointer(&sig_had_term) = 1; +} + + +/** Read value from volatile atomic type. + This function is necessary as some compilers mis-optimize + direct access to volatile variables (at least if you believe + one of the coding standards). + @param ap Pointer to volatile atomic variable. + @return Contents of the variable. +*/ +static +dk4_sig_atomic_t +sig_read_atomic(DK4_VOLATILE dk4_sig_atomic_t *ap) +{ + return (*ap); +} + + +/** Check whether we can continue or must abort due to signal. + @param dopipe Flag: Check for SIGPIPE too. + @return 1 if we can continue, 0 otherwise. +*/ +static +int +can_continue(void) +{ + int back = 1; + if (0 != sig_read_atomic(&sig_had_term)) { back = 0; } + if (0 != sig_read_atomic(&sig_had_int)) { back = 0; } + return back; +} + + + +/** Log one-component message to stderr. + @param kwind Index of text in printqdc_kw array. +*/ +static +void +log_1(size_t kwind) +{ + if (0 != intera) { + fputs(pqdc_kw[7], stderr); + fputs(pqdc_kw[kwind], stderr); + fflush(stderr); + } +} + + +/** Eat up input to avoid broken pipe in feeder. +*/ +static +void +eatup_input(void) +{ + size_t rdb; + int cc = 1; + do { + if (0 != can_continue()) { + rdb = fread(inbuf, 1, sizeof(inbuf), stdin); + if (0 == rdb) { + cc = 0; + } + } else { + cc = -1; + } + } while(1 == cc); +} + + + +/** Process command line arguments. + @param argc Number of command line arguments. + @param argv Command line arguments array. + @return 1 on success, 0 on error. +*/ +static +int +cmd_line_args(int argc, char *argv[]) +{ + dkChar *filenames[32]; + size_t szfilenames = DK4_SIZEOF(filenames,DK4_PDKCHAR); + int back; + back = dk4opt_process_argv( + options, szoptions, argc, argv, filenames, &szfilenames, + 1, 0, NULL + ); + if (1 == back) { + if (0 != options[4].found) { + hvl |= 1; + } + if (0 != options[5].found) { + hvl |= 2; + } + if (0 != options[6].found) { + hvl |= 4; + } + if (0 != options[0].found) { + intera = 1; + } + if (0 != options[1].found) { + sock_name = options[1].val.t; + } + if (0 != options[2].found) { + host_name = options[2].val.t; + } + if (0 != options[3].found) { + if ((dk4_um_t)0x0000FFFFUL >= options[3].val.u) { + portno = (unsigned short)(options[3].val.u); + } else { + back = 0; + /* ERROR: Port number to large! */ + log_1(8); + } + } + if ((NULL != host_name) && (0 == portno)) { + back = 0; + /* ERROR: Port number required */ + log_1(9); + } + if ((NULL == host_name) && (0 != portno)) { + back = 0; + /* ERROR: Host name required */ + log_1(10); + } + } else { + if (0 != options[0].found) { + (void)dk4opt_process_argv( + options, szoptions, argc, argv, filenames, &szfilenames, + 1, 1, NULL + ); + } + } + return back; +} + + + +/** Data exchange with accounting system. + @param pres Flag: Process response, 0=no, 1=acct-check, 2=info. +*/ +static +void +process_data(int pres) +{ + const char *ep; /* End pointer for text to number conversions */ + char *ptr; /* Traverse data line */ + dk4_um_t um; /* Number obtained from text */ + size_t sl; /* Input buffer string length */ + size_t wrb; /* Bytes we still have to write */ + dk4_socket_t sock; /* Connection to accounting/quota daemon */ + int res; /* Operation result */ + int cc; /* Flag: Can continue */ + $? "+ process_data %d", pres + wrb = sl = strlen(inbuf); + /* LOG/SHOW: Request */ + if (0 != intera) { $? ". log at start" + fputs(pqdc_kw[4], stdout); + if (0 < sl) { + fputs(inbuf, stdout); + if (0 < sl) { + if ('\n' != inbuf[sl - 1]) { + fputc('\n', stdout); + } + } else { + fputc('\n', stdout); + } + } + fflush(stdout); + } + if ((0 < sl) && (0 != can_continue())) { $? ". rq size ok" + if ((NULL != host_name) && (0 < portno)) { $? ". network" + sock = dk4socket_c8_tcp_client_host_port( + host_name, portno, 0, 0L, 0L, NULL + ); + } else { $? ". local" + sock = dk4socket_c8_unix_client( + ((NULL != sock_name) ? sock_name : def_sock_name), NULL + ); + } + if (INVALID_SOCKET != sock) { $? ". sock" + if (0 != can_continue()) { $? ". can continue" + res = dk4socket_send(sock, inbuf, &wrb, 0, 0L, 0L, NULL); + if ((DK4_SOCKET_RESULT_SUCCESS == res) && (wrb == sl)) { $? ". send" + if (0 != can_continue()) { $? ". can continue" + res = dk4socket_shutdown(sock, DK4_SOCKET_SHUT_WRITE, NULL); + if (DK4_SOCKET_RESULT_SUCCESS == res) { $? ". shutdown" + cc = 1; + do { + if (0 != can_continue()) { $? ". can continue" + wrb = sizeof(inbuf); + res = dk4socket_recv(sock, inbuf, &wrb, 0, 0L, 0L, NULL); + if ((DK4_SOCKET_RESULT_SUCCESS==res)&&(0 != can_continue())) { + if (0 < wrb) { $? ". data" + if (0 != pres) { $? ". process response" + if (sizeof(inbuf) > wrb) { + inbuf[wrb] = '\0'; + } else { + iexval = EXIT_FAILURE; + inbuf[sizeof(inbuf)-1] = '\0'; + } + dk4str8_normalize(inbuf, NULL); + /* LOG/SHOW: Response */ + if (0 != intera) { + fputs(pqdc_kw[5], stdout); + fputs(inbuf, stdout); + fputs(pqdc_kw[6], stdout); + fflush(stdout); + } + switch (pres) { + case 1: { + if (0 == found_response) { + switch (dk4str8_array_index(responses,inbuf,0)) { + case 1: { + exval = LPRNG_EXIT_HOLD; + } break; + case 2: { + exval = LPRNG_EXIT_REMOVE; + } break; + } + } + found_response = 1; + } break; + case 2: { + if (0 == found_response) { + um = (dk4_um_t)0UL; + ep = NULL; + ptr = dk4str8_start(inbuf, NULL); + if (NULL != ptr) { + ptr = dk4str8_next(ptr, NULL); + if (NULL != ptr) { + ptr = dk4str8_next(ptr, NULL); + if (NULL != ptr) { + ptr = dk4str8_next(ptr, NULL); + if (NULL != ptr) { + res = dk4ma_input_c8_dec_dk4_um_t( + &um, ptr, &ep, 1, NULL + ); + if (0 == res) { + um = (dk4_um_t)0UL; + } + } + } + } + } + if ((dk4_um_t)0UL == um) { + exval = LPRNG_EXIT_REMOVE; + } + } + found_response = 1; + } break; + } + } else { $? ". ignore response" + } + } else { $? ". 0 bytes response" + cc = 0; + } + } else { $? "! recv" + iexval = EXIT_FAILURE; + cc = -1; + } + } else { $? "! signal" + cc = -1; + } + } while (1 == cc); + } else { $? "! shutdown" + iexval = EXIT_FAILURE; + /* ERROR: Failed to shut down socket */ + log_1(11); + } + } else { $? "! signal" + } + } else { $? "! send" + iexval = EXIT_FAILURE; + /* ERROR: Sending data failed */ + log_1(12); + } + } else { $? "! signal" + } + dk4socket_close(sock, NULL); + } else { $? "! sock" + iexval = EXIT_FAILURE; + /* ERROR: Failed to open socket */ + log_1(13); + } + } else { $? "! rq size 0" + } + $? "- process_data" +} + + + +/** Find final quote for current word. + @param str String to find quote in. + @param quote Quote character. + @return Pointer to found quote on success, NULL on error. +*/ +static +char * +lprng_final_quote(char *str, char quote) +{ + char *back = NULL; + $? "+ lprng_final_quote %c %s", quote, TR_8STR(str) + while (('\0' != *str) && (NULL == back)) { + if (quote == *str) { + *str = '\0'; + back = str; + back++; + back = dk4str8_start(back, NULL); + } else { + if ('\\' == *str) { + if ('\0' != str[1]) { + dk4str8_cpy_to_left(str, &(str[1])); + str++; + } else { + *str = '\0'; + } + } else { + str++; + } + } + } + $? "- lprng_final_quote \"%s\"", TR_8STR(back) + return back; +} + + +/** Find start of next text word in LPRng accounting line. + @param str Current word (remaining text) in accounting line. + @return Pointer to next word if found, NULL on error. +*/ +static +char * +lprng_next_word(char *str) +{ + char *back = NULL; + $? "+ lprng_next_word \"%s\"", TR_8STR(str) + switch (*str) { + case '\'' : { + back = lprng_final_quote(&(str[1]), '\''); + } break; + case '"' : { + back = lprng_final_quote(&(str[1]), '"'); + } break; + default : { + back = dk4str8_next(str, NULL); + } break; + } $? "- lprng_next_word \"%s\"", TR_8STR(back) + return back; +} + + + +/** Fill data structure from LPRng accounting line. + @param liptr Structure to fill. + @param textline The text line to use. + @return 1 on success, 0 on error. +*/ +static +int +lprng_info_from_line(lprng_info_t *liptr, char *textline) +{ + char *next; + int back = 1; + $? "+ lprng_info_from_line \"%s\"", TR_8STR(textline) + DK4_MEMRES(liptr, sizeof(lprng_info_t)); + liptr->username = NULL; liptr->queuename = NULL; + liptr->jobtitle = NULL; liptr->pagecount = NULL; + while (NULL != textline) { + next = lprng_next_word(textline); + if (('\'' == *textline) || ('"' == *textline)) { + textline++; + } + if ('-' == *textline) { + textline++; + switch (*textline) { + case 'n': { + liptr->username = ++textline; + } break; + case 'P': { + liptr->queuename = ++textline; + } break; + case 'J': { + liptr->jobtitle = ++textline; + } break; + case 'p': { + liptr->pagecount = ++textline; + } break; + } + } else { $? "! not a minus" +#if 0 + /* 2016-02-15 Simply ignore non-option text. + */ + back = 0; + /* ERROR: No minus sign */ +#endif + } + textline = next; + } + $? "- lprng_info_from_line %d", back + return back; +} + + + +/** Process one line received from LPRng. + @param cmd Index in command names array. + @param args Further arguments in line. +*/ +static +void +lprng_line(int cmd, char *args) +{ + lprng_info_t li; /* All relevant components of line */ + const char *ep = NULL; /* End pointer for numeric conversion */ + const char *jt; /* Job title */ + dk4_um_t um; /* Text to number conversion result */ + int res; /* Operation result */ + int ok = 0; /* Flag: Can process line */ + $? "+ lprng_line %d \"%s\"", cmd, TR_8STR(args) + if (0 != lprng_info_from_line(&li, args)) { + switch (cmd) { + case 0: { + if ((NULL != li.username) && (NULL != li.queuename)) { + if (0 != dk4str8_cpy_s(inbuf,sizeof(inbuf),command_names[7], NULL)) { + if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),pqdc_kw[1], NULL)) { + if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),li.queuename, NULL)) { + if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),pqdc_kw[1], NULL)) { + if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),li.username, NULL)) { + if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),pqdc_kw[0], NULL)) { + ok = 1; $? ". new request constructed" + } + } + } + } + } + } + if (0 != ok) { + process_data(1); + } else { $? "! failed to construct" + iexval = EXIT_FAILURE; + /* ERROR: Failed to construct request */ + log_1(14); + } + } else { $? "! incomplete" + iexval = EXIT_FAILURE; + /* ERROR: Incomplete information */ + log_1(15); + } + } break; + default: { + if ((NULL != li.username) && (NULL != li.queuename)) { + if (NULL != li.pagecount) { + res = dk4ma_input_c8_dec_dk4_um_t(&um, li.pagecount, &ep, 1, NULL); + if (0 != res) { + cmd += 4; + if(0 !=dk4str8_cpy_s(inbuf,sizeof(inbuf),command_names[cmd],NULL)) + { + if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),pqdc_kw[1], NULL)) { + if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),li.queuename, NULL)) { + if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),pqdc_kw[1], NULL)) { + if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),li.username, NULL)) { + if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),pqdc_kw[1], NULL)) { + if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),li.pagecount, NULL)) { + if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),pqdc_kw[1], NULL)) { + if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),pqdc_kw[2], NULL)) { + if (0 != dk4str8_cat_s(inbuf,sizeof(inbuf),pqdc_kw[1], NULL)) { + jt = li.jobtitle; + if (NULL == jt) { jt = pqdc_kw[3]; } + if (0 != dk4str8_cat_s(inbuf, sizeof(inbuf), jt, NULL)) { + if (0 != dk4str8_cat_s(inbuf, sizeof(inbuf), pqdc_kw[0], NULL)) { + ok = 1; + } + } + } + } + } + } + } + } + } + } + } + } + if (0 != ok) { $? ". rq constructed" + process_data(0); + } else { $? "! failed to construct rq" + iexval = EXIT_FAILURE; + /* ERROR: Failed to construct request */ + log_1(14); + } + } else { $? "! pc not numeric" + iexval = EXIT_FAILURE; + /* ERROR: Pagecounter not numeric */ + log_1(16); + } + } else { $? "! incomplete rq" + iexval = EXIT_FAILURE; + /* ERROR: Incomplete information */ + log_1(15); + } + } else { $? "! incomplete rq" + iexval = EXIT_FAILURE; + /* ERROR: Incomplete information */ + log_1(15); + } + } break; + } + } else { + iexval = EXIT_FAILURE; + } + $? "- lprng_line" +} + + + +/** Process one line containing printqd style data. + @param args Arguments text. + @param minargs Minimum required number of arguments. + @param num3 Flag: Argument 3 must be unsigned number. + @param pres Flag: Process response. +*/ +static +void +printqd_line(char *args, size_t minargs, int num3, int pres) +{ + const char *ep = NULL; /* End of usable data */ + char *next; /* Start of arguments */ + dk4_um_t um = (dk4_um_t)0UL; /* Numeric conversion result */ + size_t i; /* Current argument index */ + int res; /* Operation result */ + int errnum = 0; /* Flag: Numeric error */ + $? "+ printqd_line" + dk4str8_normalize(args, NULL); + i = 0; $? ". line = \"%s\"", args + while(args) { + i++; + next = dk4str8_next(args, NULL); + if ((3 == i) && (0 != num3)) { $? ". check \"%s\"", args + res = dk4ma_input_c8_dec_dk4_um_t(&um, args, &ep, 1, NULL); + if (0 == res) { errnum = 1; } + } + args = next; + } $? ". i=%u errnum=%d", (unsigned)i, errnum + if ((i >= minargs) && (0 == errnum)) { + process_data(pres); + } else { + if (i < minargs) { + /* ERROR: Too few arguments */ + log_1(17); + } + if (0 != errnum) { + /* ERROR: Page counter not numeric */ + log_1(16); + } + iexval = EXIT_FAILURE; + } + $? "- printqd_line" +} + + + +/** Process input from stdin. +*/ +static +void +process_input(void) +{ + char *args; /* Start of arguments after command */ + int cc; /* Flag: Can continue */ + int res; /* Operation result for numeric conversions */ + $? "+ process_input" + cc = 1; + do { + if (0 != can_continue()) { $? ". can continue" + if (fgets(inbuf, sizeof(inbuf), stdin)) { $? ". data" + dk4str8_cpy_s(cpbuf, sizeof(cpbuf), inbuf, NULL); + dk4str8_delnl(cpbuf); + if (NULL != dk4str8_start(cpbuf, NULL)) { + if (0 != intera) { $? ". interactive" + fputs(cpbuf, stdout); + fputc('\n', stdout); + fflush(stdout); + } + args = dk4str8_next(cpbuf, NULL); + if (NULL != args) { $? ". arguments" + res = dk4str8_array_index(command_names, cpbuf, 0); + if (0 != can_continue()) { + switch (res) { + case 0: { + lprng_line(res, args); + } break; + case 1: { + } break; + case 2: { + } break; + case 3: { + } break; + case 4: { + lprng_line(res, args); + } break; + case 5: { + lprng_line(res, args); + } break; + case 6: { + printqd_line(args, 2, 0, 2); + } break; + case 7: { + printqd_line(args, 2, 0, 1); + } break; + case 8: { + printqd_line(args, 4, 1, 0); + } break; + case 9: { + printqd_line(args, 4, 1, 0); + } break; + case 10: { + printqd_line(args, 3, 1, 0); + } break; + case 11: { + printqd_line(args, 1, 0, 0); + } break; + default: { + iexval = EXIT_FAILURE; + /* ERROR: Unknown keyword */ + log_1(18); + } break; + } + } else { + cc = -1; + } + } else { $? "! no arguments" + /* ERROR: No arguments */ + log_1(19); + iexval = EXIT_FAILURE; + } + } else { $? ". skip empty line" + } + } else { $? ". end of data" + eof_found = 1; + cc = 0; + } + } else { $? "! aborted" + cc = -1; + } + } while (1 == cc); + $? "- process_input" +} + + + +/** Show a text paragraph on standard output. + @param txt Text to show. +*/ +static +void +show_text(const char * const *txt) +{ + if (NULL != txt) { + while (NULL != *txt) { + fputs(*(txt++), stdout); + fputc('\n', stdout); + } + fflush(stdout); + } +} + + + +/** Handle --help, --version, --license options. +*/ +static +void +run_hvl(void) +{ + if (0 != (1 & hvl)) { + show_text(help_text); + } + if (0 != (2 & hvl)) { + fputs(version_text, stdout); + fputc('\n', stdout); + fflush(stdout); + } + if (0 != (4 & hvl)) { + show_text(license_text); + } +} + + + +/** Main program. + @param argc Number of command line arguments. + @param argv Command line arguments array. + @return Exit status code. +*/ +int +main(int argc, char *argv[]) +{ +#ifdef DK4_HAVE_SIGACTION +#ifdef SIGPIPE + struct sigaction opipe; + struct sigaction npipe; +#endif + struct sigaction oint; + struct sigaction nint; + struct sigaction oterm; + struct sigaction nterm; +#else +#ifdef SIGPIPE + dk4_sig_handler_t *opipe = NULL; +#endif + dk4_sig_handler_t *oterm = NULL; + dk4_sig_handler_t *oint = NULL; +#endif +#ifdef DK4_HAVE_SIGACTION +#ifdef SIGPIPE + int spipe = 0; +#endif + int sint = 0; + int sterm = 0; + int s_f_i = 0; + int s_f_r = 0; +#endif + int socki = 0; + + /* Set signal handlers + */ +#if DK4_HAVE_SIGACTION +#ifdef SIGPIPE + DK4_MEMRES(&npipe, sizeof(npipe)); + npipe.sa_handler = sig_handler_pipe; + npipe.sa_flags = 0; + if (0 != sigemptyset(&npipe.sa_mask)) { + s_f_i = 1; + goto finished; + } + if (0 != sigaddset(&npipe.sa_mask, SIGPIPE)) { + s_f_i = 1; + goto finished; + } + if (0 != sigaction(SIGPIPE, &npipe, &opipe)) { + s_f_i = 1; + goto finished; + } + spipe = 1; +#endif + DK4_MEMRES(&nterm, sizeof(nterm)); + nterm.sa_handler = sig_handler_term; + nterm.sa_flags = 0; + if (0 != sigemptyset(&nterm.sa_mask)) { + s_f_i = 1; + goto finished; + } + if (0 != sigaddset(&nterm.sa_mask, SIGTERM)) { + s_f_i = 1; + goto finished; + } + if (0 != sigaction(SIGTERM, &nterm, &oterm)) { + s_f_i = 1; + goto finished; + } + sterm = 1; + DK4_MEMRES(&nint, sizeof(nint)); + nint.sa_handler = sig_handler_int; + nint.sa_flags = 0; + if (0 != sigemptyset(&nint.sa_mask)) { + s_f_i = 1; + goto finished; + } + if (0 != sigaddset(&nint.sa_mask, SIGINT)) { + s_f_i = 1; + goto finished; + } + if (0 != sigaction(SIGINT, &nint, &oint)) { + s_f_i = 1; + goto finished; + } + sint = 1; +#else +#ifdef SIGPIPE + opipe = sigset(SIGPIPE, sig_handler_pipe); +#endif + oint = sigset(SIGINT, sig_handler_int); + oterm = sigset(SIGTERM, sig_handler_term); +#endif + + /* Start tracing + */ + $!trace-init printqdc.deb + $? "+ main" + + /* Process command line arguments + */ + if (0 == cmd_line_args(argc, argv)) { goto finished; } + + /* Help, version, license. + */ + if (0 != hvl) { + run_hvl(); + goto finished; + } + + /* Bring socket subsystem up. + */ + if (DK4_SOCKET_RESULT_SUCCESS != dk4socket_up(NULL)) { + /* ERROR: Failed to initialize socket subsystem */ + log_1(23); + goto finished; + } + socki = 1; + + iexval = EXIT_SUCCESS; + $? ". comman line arguments processed, going to process input" + process_input(); + + finished: + + /* Eat up remaining input, if any. + */ + if ((0 == hvl) && (0 == eof_found)) { eatup_input(); } + + /* Log notification about abort by signal. + */ + if (0 == can_continue()) { + /* LOG: Interrupted by signal */ + log_1(20); + iexval = EXIT_FAILURE; + } + + /* Release socket subsystem. + */ + if (0 != socki) { (void)dk4socket_down(NULL); } + + /* Restore signal handlers + */ +#if DK4_HAVE_SIGACTION + if (0 != s_f_i) { + /* ERROR: Failed to install signal handlers */ + log_1(21); + iexval = EXIT_FAILURE; + } + if (0 != sint) { + if (0 != sigaction(SIGINT, &oint, NULL)) { + s_f_r = 1; + } + } + if (0 != sterm) { + if (0 != sigaction(SIGTERM, &oterm, NULL)) { + s_f_r = 1; + } + } +#ifdef SIGPIPE + if (0 != spipe) { + if (0 != sigaction(SIGPIPE, &opipe, NULL)) { + s_f_r = 1; + } + } +#endif + if (0 != s_f_r) { + /* ERROR: Failed to restore signal handlers */ + log_1(22); + iexval = EXIT_FAILURE; + } +#else + if (NULL != oterm) { sigset(SIGTERM, oterm); } + if (NULL != oint ) { sigset(SIGINT, oint ); } +#ifdef SIGPIPE + if (NULL != opipe) { sigset(SIGPIPE, opipe); } +#endif +#endif + + /* Set exit status code depending on interactive mode + */ + if (0 != intera) { exval = iexval; } + + /* Finish tracing + */ + $? "- main %d", exval + $!trace-end + + /* Exit + */ + exit(exval); return exval; +} + +#else +/* if (DK4_CHAR_SIZE == 1) && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) */ + +/** Main program. + @param argc Number of command line arguments. + @param argv Command line arguments array. + @return Exit status code. +*/ +int main(int argc, char *argv[]) +{ + fputs("printqdc: ERROR: Program can not be used on this system!\n", stderr); + fflush(stderr); + exit(EXIT_FAILURE); return EXIT_FAILURE; +} + +#endif +/* if (DK4_CHAR_SIZE == 1) && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) */ + -- cgit v1.2.3