/* * lpr.c - Windows NT lpr * * by Eric W. Brown * 28 October '92 */ #include #include #include #include #include #include #include #include #include #include #include #include "lp.h" /* * #defines */ #define HOSTNAME_LEN (MAX_COMPUTERNAME_LENGTH + 1) #define USERNAME_LEN (32 /* MAX_USERNAME_LENGTH */ + 1) /* * Global Variables */ int sockfd = -1; char hostname[HOSTNAME_LEN]; char username[USERNAME_LEN]; char *printer = NULL; char *server = NULL; char *temp_dir = NULL; char *class = NULL; char *job = NULL; char *title = NULL; char filter = 'f'; int noburst = 0; int debug = 0; int dosfilter = 0; int width = 0; int copies = 1; int indent = -1; int tcp_initialized = 0; int seqno; FILE *cf_file = NULL; char tmp_cfname[33] = ""; char tmp_dfname[33] = ""; char tmp_stdin_fname[33] = ""; /* * Prototypes */ int usage(); void lpr_start_protocol(); void lpr_finish_protocol(); void lpr_print_file(char *filename, char *name); void lpr_send_file(int fd, int length); void lpr_send(char *buf, int cnt); void lpr_check_ack(); void lpr_create_control_file(char *tmp_cfname); void lpr_capture_stdin(char *fname); long lpr_text_filelength(int fd); void lpr_filter_dos(char *infname, char *outfname); int lpr_get_seqno(char *seq_fname); void lpr_crash(int report, char *fmt, ...); #define NO_REPORT 0 #define REPORT_SOCKERR 1 #define REPORT_FILEERR 2 #define REPORT_ERR 3 int usage() { fprintf(stderr, "Usage: lpr [ -Pprinter ] [ -Sserver ] [ -#num ] [ -C class ] [ -J job ]\n"); fprintf(stderr, " [ -T title ] [ -i [ numcols ]] [ -wnum ] [ -pvcgdntlfhD ]\n"); fprintf(stderr, " [ -u ] [ -#n ] [ name ... ]\n"); fprintf(stderr, " -u : filter for unix (remove ^M and ^D characters)\n"); exit(1); return 0; } /* usage() */ void main(int argc, char **argv) { char *ptr; long fileHandle; struct _finddata_t fileinfo; int length; char filename[256]; /* * Get environment variables: * server, printer, hostname, username & temp_dir */ server = getenv("SERVER"); printer = getenv("PRINTER"); if (!printer) printer = DEFAULT_PRINTER; length = HOSTNAME_LEN; if (!GetComputerName(hostname, &length) || length == 0) lpr_crash(REPORT_ERR, "lpr: No hostname, name your computer!!!\n"); length = USERNAME_LEN; if (!GetUserName(username, &length)) strncpy(username, hostname, USERNAME_LEN); temp_dir = getenv("TEMP"); if (!temp_dir) { fprintf(stderr, "lpr: TEMP environment variable not set.\n"); exit(1); } else if (_access(temp_dir, 06) < 0) { fprintf(stderr, "lpr: TEMP directory, %s, does not exist or %s\n", temp_dir, "invalid permissions"); exit(1); } /* else */ /* * Parse command line */ #define NEXT_ARG() (argc--, argv++, argc ? *argv : (char *)(usage(), NULL)) #define ARG_ARG() (strlen(*argv) == 2 ? NEXT_ARG() : (*argv) + 2) while (argc > 1 && argv[1][0] == '-') { NEXT_ARG(); switch(argv[0][1]) { case 'P': printer = ARG_ARG(); break; case 'S': server = ARG_ARG(); break; case 'C': class = ARG_ARG(); break; case 'J': job = ARG_ARG(); break; case 'T': title = ARG_ARG(); break; case 'p': case 'v': case 'c': case 'g': case 'd': case 'n': case 't': case 'l': filter = argv[0][1]; break; case 'f': filter = 'r'; break; case 'h': noburst = 1; break; case 'D': debug = 1; break; case 'u': dosfilter = 1; break; case 'i': indent = (*argv[2] ? atoi(*argv + 2) : 8); if (indent < 0 || indent > 255) { fprintf(stderr, "lpr: %s: invalid indentation, %s", *argv, "please keep it between 0 and 255.\n"); usage(); } /* if */ break; case 'w': if (sscanf(ARG_ARG(), "%d", &width) != 1) { fprintf(stderr, "lpr: invalid width\n"); usage(); } /* if */ break; case '#': if (sscanf(ARG_ARG(), "%d", &copies) != 1) { fprintf(stderr, "lpr: # of copies must be an integer.\n"); exit(1); } /* if */ break; default: fprintf(stderr, "lpr: '%s' invalid option\n", *argv); usage(); } /* switch */ } /* while argc */ /* * Loop over remaining arguments, (Print these files ...) */ while (argc > 1) { NEXT_ARG(); /* For unix compatibility, replace '/'s with '\'s */ ptr = *argv; while(*ptr) if (*ptr++ == '/') ptr[-1] = '\\'; if ((fileHandle = _findfirst(*argv, &fileinfo)) != -1L) { do { if (!tcp_initialized) lpr_start_protocol(); strcpy(filename, *argv); if (ptr = strrchr(filename, '\\')) ptr[1] = '\0'; else if (ptr = strrchr(filename, ':')) ptr[1] = '\0'; else filename[0] = '\0'; strcat(filename, fileinfo.name); lpr_print_file(filename, filename); } while (_findnext(fileHandle, &fileinfo) == 0); _findclose(fileHandle); } else { fprintf(stderr, "lpr: %s: Invalid file spec.\n", *argv); if (tcp_initialized) break; else exit(1); } /* else */ } /* while argc */ /* * See if we should take input from stdin */ if (!tcp_initialized) { lpr_start_protocol(); sprintf(tmp_stdin_fname, "%s\\sin%03d.tmp", temp_dir, seqno); lpr_capture_stdin(tmp_stdin_fname); lpr_print_file(tmp_stdin_fname, "(standard input)"); unlink(tmp_stdin_fname); } /* if */ /* * Clean up */ if (tcp_initialized) { lpr_finish_protocol(); closesocket(sockfd); lp_tcp_shutdown(); fclose(cf_file); unlink(tmp_cfname); } /* if */ exit(0); } /* main() */ void lpr_start_protocol() { char seq_fname[33]; char message[80]; /* * Do a lookup in the printcap file if necessary */ if (!server) if (!(server = lp_printcap_server_lookup(printer))) { fprintf(stderr, "lpr: No server, %s, and no printer, %s, %s", server, printer, "in printcap file!\n"); usage(); } /* if */ /* * Initialize tcp */ if (lp_tcp_startup()) lpr_crash(REPORT_SOCKERR, "lpr: Couldn't initialize tcp/ip\n"); tcp_initialized = 1; if (debug) fprintf(stderr, "lpr: Initialized TCP/IP\n"); /* * Open a socket */ if ((sockfd = lp_tcp_open(server)) < 0) lpr_crash(REPORT_SOCKERR, "lpr: Couldn't open connection to server, %s\n", server); if (debug) fprintf(stderr, "lpr: Connected to server, %s\n", server); /* * Get sequence number and increment it */ sprintf(seq_fname, "%s\\lpr.seq", temp_dir); seqno = lpr_get_seqno(seq_fname); /* * Create control file */ sprintf(tmp_cfname, "%s\\cfA%03d.tmp", temp_dir, seqno); lpr_create_control_file(tmp_cfname); /* * Tell LPD to receive a job */ sprintf(message, "%c%s\n", LPD_PRINT_JOB, printer); lpr_send(message, strlen(message)); lpr_check_ack(); if (debug) fprintf(stderr, "lpr: Server is accepting job.\n"); } /* lpr_start_protocol() */ void lpr_finish_protocol() { int length; char message[80]; /* * Send control file receive message */ fflush(cf_file); length = _filelength(_fileno(cf_file)); sprintf(message, "%c%d cfA%03d%s\n", LPD_RECEIVE_CONTROL_FILE, length, seqno, hostname); lpr_send(message, strlen(message)); lpr_check_ack(); /* * Send control file */ if (debug) fprintf(stderr, "lpr: Sending control file to %s\n", server); _lseek(_fileno(cf_file), 0, SEEK_SET); lpr_send_file(_fileno(cf_file), length); } /* lpr_finish_protocol() */ void lpr_print_file(char *filename, char *name) { static int file_count = 0; static char *df_prefix = "df?"; char dfname[65]; int fd; int length; char message[80]; int i; int is_text; /* * Create df filename */ if (file_count++ % 26 == 0) df_prefix[2]++; else if (file_count == 27) df_prefix = "dfA@"; df_prefix[strlen(df_prefix) - 1]++; sprintf(dfname, "%s%03d%s", df_prefix, seqno, hostname); /* * See if we need to make a temporary file */ if (dosfilter) { sprintf(tmp_dfname, "%s\\%s%03d.tmp", temp_dir, df_prefix, seqno); lpr_filter_dos(filename, tmp_dfname); } /* if */ /* * Add to control file */ if (width) fprintf(cf_file, "W%d\n", width); if (indent != -1) fprintf(cf_file, "I%d\n", indent); if (filter == 'p') fprintf(cf_file, "T%s\n", (title ? title : name)); for(i = 0; i < copies; i++) fprintf(cf_file, "%c%s\n", filter, dfname); fprintf(cf_file, "U%s\n", dfname); fprintf(cf_file, "N%s\n", name); /* * Open the file */ is_text = (filter == 'f' || filter == 'p' || filter == 'r' || dosfilter); fd = open(dosfilter ? tmp_dfname : filename, O_RDONLY | (is_text ? O_TEXT : O_BINARY)); if (fd < 0) lpr_crash(REPORT_FILEERR, "lpr: Couldn't open file, %s\n", filename); /* * Try to send a file */ length = (is_text ? lpr_text_filelength(fd) : filelength(fd)); sprintf(message, "%c%ld %s\n", LPD_RECEIVE_DATA_FILE, length, dfname); lpr_send(message, strlen(message)); lpr_check_ack(); /* * Send the actual file */ if (debug) fprintf(stderr, "lpr: Sending %s to %s@%s.\n", name, printer, server); lpr_send_file(fd, length); close(fd); if (*tmp_dfname) { unlink(tmp_dfname); *tmp_dfname = 0; } /* if */ } /* lpr_print_file() */ void lpr_send_file(int fd, int length) { int cnt; char buf[1024]; /* * Send the file */ while(length) { cnt = min(length, 1024); if ((cnt = read(fd, buf, cnt)) <= 0) lpr_crash(REPORT_FILEERR, "lpr: Error reading file\n"); lpr_send(buf, cnt); length -= cnt; } /* while */ /* * Send end mark & get acknowledgement */ sprintf(buf, "%c", LPD_END_TRANSFER); lpr_send(buf, 1); lpr_check_ack(); } /* lpr_send_file() */ void lpr_send(char *buf, int cnt) { if (send(sockfd, buf, cnt, 0) != cnt) lpr_crash(REPORT_SOCKERR, "lpr: Error talking to server, %s\n", server); } /* lpr_send() */ void lpr_check_ack() { char buf[80]; int len; while(1) { len = recv(sockfd, buf, 79, 0); if (len <= 0) lpr_crash(REPORT_SOCKERR, "lpr: Server not responding.\n"); switch(buf[0]) { case LPD_OK: return; case LPD_ERROR: lpr_crash(NO_REPORT, "lpr: Server error\n"); case LPD_NO_SPOOL_SPACE: lpr_crash(NO_REPORT, "lpr: Unable to accept job at this time.\n%s", "Not enough spool space on server.\n"); default: /* An error message? */ buf[len] = 0; fprintf(stderr, "%s", buf); } /* switch */ } /* while */ } /* check_ack() */ void lpr_create_control_file(char *tmp_cfname) { /* * Open the control file */ cf_file = fopen(tmp_cfname, "w+b"); if (!cf_file) lpr_crash(REPORT_FILEERR, "lpr: Couldn't open file, %s\n", tmp_cfname); /* * Start building the control file */ fprintf(cf_file, "H%s\n", hostname); fprintf(cf_file, "P%s\n", username); if (!noburst) { if (job) fprintf(cf_file, "J%s\n", job); fprintf(cf_file, "C%s\n", class ? class : hostname); fprintf(cf_file, "L%s\n", username); } /* if */ } /* lpr_create_control_file() */ void lpr_capture_stdin(char *fname) { char buf[1024]; int len; int fd; if (debug) fprintf(stderr, "lpr: Capturing stdin to '%s'\n", fname); fd = open(fname, _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, 0666); if (fd < 0) lpr_crash(NO_REPORT, "lpr: Error creating temporary file, %s\n", fname); while((len = read(0, buf, 1024)) > 0) if (_write(fd, buf, len) != len) lpr_crash(NO_REPORT, "lpr: Error writing to tmp file, %s\n", fname); close(fd); } /* lpr_capture_stdin() */ int lpr_get_seqno(char *seq_fname) { int seqno; int fd; int cnt; char buf[80]; /* * Open the sequence file, if it doesn't exist, create it. */ if ((fd = _open(seq_fname, _O_RDWR | _O_CREAT | _O_TEXT)) < 0) lpr_crash(REPORT_FILEERR, "lpr: Couldn't open (or create) sequence file (%s)\n", seq_fname); /* * Lock the file (the first byte of it anyway) */ if (_locking(fd, _LK_LOCK, 1)) lpr_crash(REPORT_FILEERR, "lpr: Couldn't lock sequence file, %s\n", seq_fname); /* * Get the sequence number */ cnt = read(fd, buf, 79); buf[cnt] = 0; if (sscanf(buf, "%d", &seqno) != 1) seqno = 1; /* * Write new sequence number */ _lseek(fd, 0, SEEK_SET); sprintf(buf, "%03d\n", (seqno + 1) % 1000); write(fd, buf, strlen(buf)); /* * Unlock & close file */ _lseek(fd, 0, SEEK_SET); _locking(fd, _LK_UNLCK, 1); _close(fd); return (seqno); } /* lpr_get_seqno() */ void lpr_crash(int report, char *fmt, ...) { va_list marker; if (fmt) { va_start(marker, fmt); vfprintf(stderr, fmt, marker); va_end(marker); } /* if */ if (tcp_initialized) { if (sockfd != -1) closesocket(sockfd); lp_tcp_shutdown(); if (cf_file) fclose(cf_file); if (*tmp_dfname) unlink(tmp_dfname); if (*tmp_stdin_fname) unlink(tmp_stdin_fname); } /* if */ exit(1); } /* lpr_crash() */ long lpr_text_filelength(int fd) { char buf[1024]; int length = 0; int cnt; /* Read the whole file */ while((cnt = read(fd, buf, 1024)) > 0) length += cnt; /* Rewind to the beginning */ lseek(fd, 0, SEEK_SET); return length; } /* lpr_text_filelength() */ void lpr_filter_dos(char *infname, char *outfname) { FILE *in_file; FILE *out_file; char buf[1024]; char *ptr; if (debug) fprintf(stderr, "lpr: Filtering ^D's from '%s' to '%s'\n", infname, outfname); in_file = fopen(infname, "rt"); if (!in_file) lpr_crash(REPORT_FILEERR, "lpr: Couldn't open file '%s'\n", infname); out_file = fopen(outfname, "wt"); if (!out_file) lpr_crash(REPORT_FILEERR, "lpr: Couldn't create file '%s'\n", outfname); while(!feof(in_file)) { /* Try to get one line of text */ fgets(ptr = buf, 1023, in_file); /* Exclude lines that begin with ^D or ^Z */ if (buf[0] == 4 || buf[0] == 26) if (*++ptr == '\n') continue; while(ptr[0] && !feof(in_file)) { if (fputs(ptr, out_file) == EOF) lpr_crash(REPORT_FILEERR, "lpr: Couldn't write to file '%s'\n", outfname); if (ptr[strlen(ptr) - 1] == '\n') break; fgets(ptr = buf, 1023, in_file); } /* while */ } /* while */ fclose(in_file); fclose(out_file); } /* lpr_filter_dos() */