home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / NCSATELN / TEL23SRC.ZIP / LPR / LPR.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-08-21  |  20.9 KB  |  608 lines

  1. /*  -------------------------------------------------------------------
  2.     lpr - line printer
  3.  
  4.     Used to print files on remote printers using the LPD protocol.
  5.     Built on top of the NCSA TCP/IP package (version 2.2tn for MS-DOS).
  6.  
  7.     Paul Hilchey   May 1989
  8.  
  9.     Copyright (C) 1989  The University of British Columbia
  10.     All rights reserved.
  11.  
  12.     history:
  13.      8/17/89    Relax option parsing to allow spaces
  14.      8/21/89    Expand wildcards in filenames
  15.     10/23/89    Default title to filename with -p format
  16.       1/6/90         Microsoft Port by Heeren Pathak (NCSA)
  17.     -------------------------------------------------------------------
  18. */
  19.  
  20. #ifdef __TURBOC__
  21. #include "turboc.h"
  22. #include <dir.h>
  23. #endif
  24. #include <stdio.h>
  25. #include <string.h>
  26. #include <stdlib.h>
  27. #include <dos.h>
  28. #include <stdarg.h>
  29. #include <io.h>
  30. #include <time.h>
  31. #ifdef MSC
  32. #define EXIT_SUCCESS 0
  33. #ifndef __TURBOC__
  34. #define ffblk find_t
  35. #define ff_name name
  36. #define findnext _dos_findnext
  37. #define findfirst(path,fileblock,attribute) _dos_findfirst((path),(attribute),(fileblock))
  38. #endif
  39. #include <signal.h>
  40. #ifndef __TURBOC__
  41. #include <direct.h>
  42. #endif
  43. #include <malloc.h>
  44. #else 
  45. #include <dir.h>
  46. #endif
  47.  
  48. #define WINMASTER
  49. #define LPR
  50.  
  51. #ifdef MEMORY_DEBUG
  52. #include "memdebug.h"
  53. #endif
  54. #include "whatami.h"
  55. #include "hostform.h"
  56. #include "windat.h"
  57. #include "lp.h"
  58. #include "externs.h"
  59.  
  60. #define MAX_COPIES          10
  61. #define DEFAULT_INDENT      8
  62. #define CONTROL_FILE_SIZE   1024    /* max size in bytes */
  63.  
  64. /*  Function prototypes  */
  65.  
  66. void main(int argc,char * *argv);
  67. static void start_protocol(char *host,char *rname);
  68. static void finish_protocol(void );
  69. static void print_file(char *filename);
  70. static void print_one_file(char *filename);
  71. static void send_file(int connection_id,FILE *data_file,char *spool_name,int is_text);
  72. static void check_ack(int connection_id);
  73. static long get_size(FILE *data_file,int is_text);
  74. #ifndef __TURBOC__
  75. static void randomize(void );
  76. #endif
  77.  
  78. /*  global variables */
  79.  
  80. char *remote_name = NULL, /* printer name on remote system */
  81.      *remote_host = NULL, /* address of remote host        */
  82.      *class = NULL,       /* job classification            */
  83.      *job = NULL,         /* job name                      */
  84.      *title = NULL,       /* job title                     */
  85.      filter = 'f';        /* default filter                */
  86. int copies = 1,           /* number of copies              */
  87.     indent = -1,          /* indent, -1 means not set      */
  88.     width = 0,            /* width, 0 means not set        */
  89.     noburst = 0;          /* 1 = skip burst page           */
  90.  
  91. int     connection_id;
  92. char    control_file[CONTROL_FILE_SIZE];  /* control file to be sent after data files, built in a string */
  93. int     cf_length = 0;      /* current length of control_file */
  94. int     sequence_number;    /* sequence number for spooled file names */
  95. struct config *cp;          /* configuration information */
  96. char    username[9];        /* name of user */
  97. int     debug = 0;          /* 1 = print debugging info; set with -D option */
  98.  
  99. int     ftppassword,        /* not used; just to avoid unresolved external */
  100.         bypass_passwd=0;    /* whether to bypass the password check */
  101.  
  102. unsigned char path_name[_MAX_DRIVE+_MAX_DIR],        /* character storage for the path name */
  103.     temp_str[20],s[_MAX_DIR],temp_data[30];
  104.  
  105.  
  106. /****************************************************************
  107.  *  Main program.                                               *
  108.  *     lpr  [option ...] file1 ...                              *
  109.  ****************************************************************/
  110. void main(int argc,char *argv[])
  111. {
  112.     int i,first_time, temp;
  113.     char *ptr;
  114.  
  115. #ifdef __TURBOC__
  116.     fnsplit(argv[0],path_name,s,temp_str,temp_data);    /* split the full path name of telbin.exe into it's components */
  117. #else
  118.     _splitpath(argv[0],path_name,s,temp_str,temp_data); /* split the full path name of telbin.exe into it's components */
  119. #endif
  120.     strcat(path_name,s);    /* append the real path name to the drive specifier */
  121.  
  122. #if defined(MSC) && !defined(__TURBOC__)
  123.     signal(SIGINT,breakstop);        /* Microsoft intercept of break */
  124. #else
  125.     ctrlbrk(breakstop);     /* set up ctrl-c handler */
  126. #endif
  127.  
  128.  
  129.     /* Do session initialization.  Snetinit reads config file. */
  130.     ptr = getenv("CONFIG.TEL");
  131.     if(ptr!=NULL)
  132.         Shostfile(ptr);
  133.  
  134.     if(argc>1) {
  135.         if(!(strcmp(argv[1],"-h")))
  136.             Shostfile(argv[2]);
  137.     }
  138.  
  139.     if(i=Snetinit()) {
  140.         if(i==-3)        /* check for BOOTP server not responding */
  141.             netshut();    /* release network */
  142.         crash("network initialization failed.");
  143.       }    /* end if */
  144.  
  145.     first_time = 1;          /* reset once we've found something to print */
  146.  
  147.     /* select default printer */
  148.     remote_name = getenv("PRINTER");
  149.     if (remote_name == NULL) remote_name = DEFAULT_PRINTER;
  150.  
  151.     remote_host = getenv("SERVER");
  152.  
  153.     /* Files sent to the remote system need to have a name of the
  154.        form   df<letter><sequence number><host name>  (data files)
  155.          or   cf<letter><sequence number><host name>  (control files).
  156.        It isn't convenient for to keep track of the sequence number
  157.        from when lpr was last used, so we just use a random number.
  158.        It is unlikely but possible that we could get into trouble
  159.        doing this.                                                    */
  160.     randomize();
  161.     sequence_number = rand() % 1000;
  162.  
  163.     /* get info from configuration file */
  164.     cp = (struct config *)malloc(sizeof(struct config));
  165.     Sgetconfig(cp);
  166.  
  167.     /* check that the machine name was set in the configuration file */
  168.     if (0 == strlen(cp->me)) crash("`myname' not set in config file.");
  169.  
  170.     /* set user name.  use first part of machine name if nothing else. */
  171.     ptr = getenv("USER");
  172.     if (NULL != ptr) {
  173.         strncpy(username,ptr,8);
  174.         username[8]='\0';
  175.     }
  176.     else {
  177.         i = min(strcspn(cp->me,"."),sizeof(username)-1);
  178.         strncpy(username,cp->me,i);
  179.         username[i]='\0';
  180.     }
  181.  
  182.     /* Loop through command line arguments */
  183.     for (i=1; i<argc; ++i)
  184.  
  185.         if (argv[i][0] == '-')        /* setting an option */
  186.  
  187.             switch(argv[i][1]) {
  188.  
  189.             case '#':                   /* set number of copies */
  190.                 if (argv[i][2])
  191.                     temp = atoi(&argv[i][2]);
  192.                 else if (i+1 < argc)
  193.                     temp = atoi(argv[++i]);
  194.                 else
  195.                     temp = 1;
  196.  
  197.                 if (temp < 1 || temp > MAX_COPIES) {
  198.                     fprintf(stderr,"Unreasonable number of copies requested.  Reset to 1.\n");
  199.                     temp = 1;
  200.                 }
  201.                 copies = temp;
  202.                 break;
  203.  
  204.             case 'P':                   /* set printer name */
  205.                 if (argv[i][2])
  206.                     remote_name = &argv[i][2];
  207.                 else if (i+1 < argc)
  208.                     remote_name = argv[++i];
  209.                 break;
  210.  
  211.             case 'S':                   /* select server */
  212.                 if (argv[i][2])
  213.                     remote_host = &argv[i][2];
  214.                 else if (i+1 < argc)
  215.                     remote_host = argv[++i];
  216.                 break;
  217.  
  218.             case 'C':                   /* set job classification */
  219.                 if (argv[i][2])
  220.                     class = &argv[i][2];
  221.                 else if (i+1 < argc)
  222.                     class = argv[++i];
  223.                 break;
  224.  
  225.             case 'J':                   /* set job name */
  226.                 if (argv[i][2])
  227.                     job = &argv[i][2];
  228.                 else if (i+1 < argc)
  229.                     job = argv[++i];
  230.                 break;
  231.  
  232.             case 'T':                   /* set job title */
  233.                 if (argv[i][2])
  234.                     title = &argv[i][2];
  235.                 else if (i+1 < argc)
  236.                     title = argv[++i];
  237.                 break;
  238.  
  239.             case 'i':                   /* set indent */
  240.                 temp = argv[i][2] ? atoi(&argv[i][2]) : DEFAULT_INDENT;
  241.                 if (temp < -1 || temp > 255)
  242.                     fprintf(stderr,"Unreasonable indent requested.  Indent not changed.\n");
  243.                 else
  244.                     indent = temp;
  245.                 break;
  246.  
  247.             case 'w':                   /* set page width */
  248.                 if (argv[i][2])
  249.                     temp = atoi(&argv[i][2]);
  250.                 else if (i+1 < argc)
  251.                     temp = atoi(argv[++i]);
  252.                 else
  253.                     break;
  254.  
  255.                 if (temp < 0 || temp > 512)
  256.                     fprintf(stderr,"Unreasonable page width requested.  Width not changed.\n");
  257.                 else
  258.                     width = temp;
  259.                 break;
  260.  
  261.             case 'p':   /* pr */
  262.             case 'v':   /* raster */
  263.             case 'c':   /* cifplot */
  264.             case 'g':   /* graph */
  265.             case 'd':   /* dvi */
  266.             case 'n':   /* ditroff */
  267.             case 't':   /* troff */
  268.             case 'l':   /* long (with control characters) */
  269.                 filter = argv[i][1];
  270.                 break;
  271.  
  272.             case 'f':                   /* fortran */
  273.                 filter = 'r';  /* f selects filter r */
  274.                 break;
  275.  
  276.             case 'h':                   /* suppress burst page */
  277.                 noburst = 1;
  278.                 break;
  279.  
  280.             case 'D':                   /* turn debugging output on */
  281.                 debug = 1;
  282.                 break;
  283.  
  284.             default:
  285.                 fprintf (stderr,"Unrecognized option: %s ignored.\n",argv[i]);
  286.                 break;
  287.             }
  288.         else {                          /* name of file to print */
  289.             if(!n_findfirst(argv[i],0)) {    /* check whether the file exists before trying to print it */
  290.                 if (first_time) {           /* open connection on first job */
  291.                     if (NULL == remote_host) crash("server not specified.");
  292.                     if (NULL == job) job=argv[i];  /* default job name */
  293.                     start_protocol(remote_host,remote_name);
  294.                     first_time = 0;
  295.                 }
  296.                 print_file(argv[i]);
  297.               }    /* end if */
  298.             else {
  299.                 fprintf (stderr,"Cannot access: %s.\n",argv[i]);
  300.               }    /* end else */
  301.         }
  302.  
  303.     if (first_time) {
  304.         puts("Name:  lpr - send a job to a remote printer; version 1.3");
  305.         puts("Usage: lpr  [ options ... ]  file1 ... ");
  306.         puts("Options are:");
  307.         puts("  -P<printer>     -S<server>      -C<class>      -J<job>");
  308.         puts("  -T<title>       -i<indent>      -w<width>      -#<copies>");
  309.         puts("  -p              -l              -d             -f");
  310.         puts("  -h");
  311.     }
  312.     else {
  313.         finish_protocol();
  314.         puts("Done.");
  315.     }
  316.     netshut();
  317.     exit(EXIT_SUCCESS);
  318. }
  319.  
  320. /*****************************************************************
  321.  *  start_protocol                                               *
  322.  *  Open the TCP connection and start building the control file. *
  323.  *  Aborts with an error message if someting goes wrong.         *
  324.  *  parameters: null terminated name or IP address of the server *
  325.  *              name of the printer on the server                *
  326.  *****************************************************************/
  327. static void start_protocol(char *host,char *rname)
  328. {
  329.     struct machinfo *server_info_record;
  330.  
  331.     server_info_record = lookup(host);
  332.     if (0 == server_info_record) crash("domain lookup failed for %s.",host);
  333.  
  334.     netfromport(rand() % 1023);  /* source should be a privileged port */
  335.     connection_id = open_connection(server_info_record, rand() % MAX_PRIV_PORT, PRINTER_PORT);
  336.     if (0 > connection_id)
  337.         crash("unable to open connection.");
  338.  
  339.     /* Tell LPD to receive a job. */
  340.     nprintf(connection_id,"%c%s\n", LPD_PRINT_JOB, rname);
  341.  
  342.     if (debug) puts("told LPD to receive job, awaiting ack");
  343.     check_ack(connection_id);
  344.  
  345.     cf_length = 0;
  346.                cf_length += sprintf(control_file+cf_length,"H%s\n",cp->me);
  347.                cf_length += sprintf(control_file+cf_length,"P%s\n",username);
  348. #ifdef OLD_WAY
  349.     if (job)   cf_length += sprintf(control_file+cf_length,"J%s\n",job);
  350.                cf_length += sprintf(control_file+cf_length,"C%s\n",class ? class : cp->me);
  351.                cf_length += sprintf(control_file+cf_length,"L%s\n",username);
  352. #else
  353.     if(!noburst) {
  354.         if (job) 
  355.             cf_length += sprintf(control_file+cf_length,"J%s\n",job);
  356.         cf_length += sprintf(control_file+cf_length,"C%s\n",class ? class : cp->me);
  357.         cf_length += sprintf(control_file+cf_length,"L%s\n",username);
  358.       }    /* end if */
  359. #endif
  360. }
  361.  
  362. /******************************************************************
  363.  * finish_protocol                                                *
  364.  * Called after last data file has been sent.  Sends the control  *
  365.  * file then closes the network connection.                       *
  366.  ******************************************************************/
  367. static void finish_protocol(void )
  368. {
  369.     nprintf(connection_id,"%c%d cfA%03d%s\n", LPD_RECEIVE_CONTROL_FILE, cf_length,
  370.             sequence_number, cp->me);
  371.     if(debug)
  372.         puts("Told LPD we're sending control file, awaiting ack.");
  373.     check_ack(connection_id);
  374.     netwrite(connection_id,control_file,cf_length);
  375.     nprintf(connection_id,"%c",LPD_END_TRANSFER);
  376.     if(debug)
  377.         puts("Sent control file, awaiting ack.");
  378.     check_ack(connection_id);
  379.  
  380.     netclose(connection_id);
  381. }
  382.  
  383. /**************************************************************** *
  384.  * print_file                                                     *
  385.  * Changes any forward slashes in the filename to backwards ones, *
  386.  * and then expands any wildcards before calling print_one_file.  *
  387.  ******************************************************************/
  388. static void print_file(char *filename)
  389. {
  390.     int done;
  391.     struct ffblk ffblk;
  392.     char *p;
  393.     char *start_of_name;
  394.     char *expanded_name;
  395.  
  396.     /* change forward slashes in filenames to suit backwards MS-DOS */
  397.     p=filename;
  398.     while (*p != '\0') {
  399.         if (*p == '/') *p = '\\';
  400.         p++;
  401.     }
  402.  
  403.     /* make a copy of the path portion of the filename with room
  404.        to substitute any expanded wildcard filenames at the end  */
  405.     expanded_name = (char *)malloc(strlen(filename)+13);
  406.     strcpy(expanded_name,filename);
  407.     start_of_name = strrchr(expanded_name,'\\');
  408. #ifdef OLD_WAY
  409.     if (start_of_name == NULL)
  410.        start_of_name = expanded_name;
  411.     else
  412.        start_of_name++;
  413. #else
  414.     if (start_of_name == NULL)
  415.         if((start_of_name =strrchr(expanded_name,':'))!=NULL)    /* skip name past drive specifier */
  416.             start_of_name++;
  417.         else
  418.             start_of_name=expanded_name;
  419.     else
  420.        start_of_name++;
  421. #endif
  422.  
  423.     /* expand wildcards */
  424.     done = findfirst(filename, &ffblk, 0);
  425.     if (done)
  426.         fprintf(stderr,"%s doesn't exist.\n",filename);
  427.     else
  428.         do {
  429.             strcpy(start_of_name,ffblk.ff_name);
  430.             print_one_file(expanded_name);
  431.         } while (0 == findnext(&ffblk));
  432.  
  433.     free(expanded_name);
  434. }
  435.  
  436.  
  437. /******************************************************************
  438.  *  Print a file                                                  *
  439.  *  Adds records to the control file as appropriate for the       *
  440.  *  various option settings, then sends the data file.            *
  441.  ******************************************************************/
  442. static void print_one_file(char *filename)
  443. {
  444.     char spool_file_name[40];
  445.     int is_text,i;
  446.     FILE *data_file;
  447.  
  448.     if (debug) printf("printing file %s\n",filename);
  449.  
  450.     /* for `non-binary' filters, we do a little bit of filtering beforehand */
  451. #ifdef OLD_WAY
  452.     is_text = (filter == 'f' || filter == 'l' || filter == 'p' || filter == 'r');
  453. #else
  454.     is_text = (filter == 'f' || filter == 'p' || filter == 'r');
  455. #endif
  456.  
  457.     /* open the file in the appropriate mode (text or binary) */
  458.     if (is_text)
  459.         data_file = fopen(filename,"rt");
  460.     else
  461.         data_file = fopen(filename,"rb");
  462.  
  463.     if (data_file == NULL) {
  464.         fprintf(stderr,"Trouble opening file %s.\n",filename);
  465.         return;
  466.     }
  467.     printf("Sending %s ",filename);
  468.  
  469.     /* make up name for spooled data file */
  470.     sequence_number = (sequence_number + 1) % 1000;
  471.     sprintf(spool_file_name,"dfA%03d%s",sequence_number,cp->me);
  472.  
  473.     /* add records in control file describing job */
  474.     if (width != 0)   cf_length += sprintf(control_file+cf_length,"W%d\n",width);
  475.     if (indent != -1) cf_length += sprintf(control_file+cf_length,"I%d\n",indent);
  476.     if (filter == 'p') cf_length += sprintf(control_file+cf_length,"T%s\n", (title != NULL) ? title : filename);
  477.     for (i=1; i <= copies; i++)
  478.         cf_length += sprintf(control_file+cf_length,"%c%s\n",filter,spool_file_name);
  479.     cf_length += sprintf(control_file+cf_length,"U%s\n",spool_file_name);
  480.     cf_length += sprintf(control_file+cf_length,"N%s\n",filename);
  481.  
  482.     send_file(connection_id, data_file, spool_file_name, is_text);
  483. }
  484.  
  485. /****************************************************************
  486.  * send_file                                                    *
  487.  * Does the actual work of sending the file to the daemon.      *
  488.  * parameters: the connection id, as returned from Snetopen     *
  489.  *             a file handle for the data file to be sent       *
  490.  *             the name of the spool file on the remote system  *
  491.  *             a flag indicating if this is a text file         *
  492.  ****************************************************************/
  493. static void send_file(int connection_id, FILE *data_file, char *spool_name, int is_text)
  494. {
  495. /*    char buf[1024]; */
  496.     char *buf;
  497.     long length, step;
  498.     int xp, towrite, i;
  499.     int file_handle;
  500.  
  501.     if ((buf = (char *)malloc(1024)) == NULL) crash("Out of heap...");
  502.     length = get_size(data_file, is_text);
  503.     nprintf(connection_id, "%c%ld %s\n", LPD_RECEIVE_DATA_FILE, length, spool_name);
  504.     check_ack(connection_id);
  505.  
  506.     file_handle = fileno(data_file);
  507.     towrite = xp = 0;
  508.     step = max(length / 5, 1);  /* print one dot for each 1/5 of the file sent */
  509.  
  510.     while(1) {
  511.         if (towrite <= xp) {
  512.             towrite = read(file_handle, buf, 1024);
  513.             if (towrite == 0) break;
  514.             xp = 0;
  515.         }
  516.  
  517.         i = netwrite(connection_id,&buf[xp], towrite-xp);
  518.         if (debug && i) printf("send_file sent %d bytes\n",i);
  519.         Stask();
  520.         checkerr();
  521.         if (i < 0) crash("transfer failed.");
  522.  
  523.         if (i > 0) {
  524.             int j;
  525.  
  526.             /* print out dots indicating how much sent */
  527.             for (j=1; j<=(int)(((length+step-5L)/step - (length+step-(long)i-5L)/step)); j++)
  528.                 putchar('.');
  529.             xp += i;
  530.             length -= i;
  531.         }
  532.     }
  533.     putchar('\n');
  534.     if (length != 0)
  535.         crash ("file length discrepancy.");
  536.  
  537.     /* send end mark */
  538.     nprintf(connection_id, "%c", LPD_END_TRANSFER);
  539.     check_ack(connection_id);
  540. }
  541.  
  542. /************************************************************************
  543.  * Return the size of a file.  For text files, we have to read through  *
  544.  * the whole thing, as we are going to send LF as line terminator,      *
  545.  * rather than CRLF.                                                    *
  546.  ************************************************************************/
  547. static long get_size(FILE *data_file, int is_text)
  548. {
  549.     char buf[1024];
  550.     long total;
  551.     int handle;
  552.     int count;
  553.  
  554.     handle = fileno(data_file);
  555.     if (is_text) {
  556.         total = 0;
  557.         while ((count = read(handle, buf, 1024)) > 0)
  558.             total += count;
  559.         rewind(data_file);
  560.         return(total);
  561.     }
  562.     else
  563.         return(filelength(handle));
  564. }
  565.  
  566. /*********************************************************
  567.  *  Wait for a one character reply from LPD.  Abort with *
  568.  *  an error message if it not an OK acknowledgement.    *
  569.  *********************************************************/
  570. static void check_ack(int connection_id)
  571. {
  572.     char ack_buff[80];
  573.     int len;
  574.  
  575.     while(1) {
  576.        len = nread(connection_id, ack_buff, 80);
  577.        if (len <= 0)
  578.            crash("connection closed by server.");
  579.        switch (ack_buff[0]) {
  580.            case LPD_OK :
  581.                return;
  582.            case LPD_ERROR :
  583.                crash("error code from server.");
  584.            case LPD_NO_SPOOL_SPACE :
  585.                crash("server unable to accept job at this time.");
  586.            default :   /* may be an error message */
  587.                fprintf(stderr,"%.*s",len,ack_buff);
  588.         }
  589.      }
  590. }
  591.  
  592. #if defined(MSC) && !defined(__TURBOC__)
  593. /******************************************************************
  594. *
  595. * randomize()
  596. *
  597. * replicates the randomize function of Turbo C
  598. * MSC 5.1 does not contain it so we have to write it ourselves.
  599. *
  600. */
  601.  
  602. static void randomize(void )
  603. {
  604.     srand((unsigned)time(NULL));
  605. }
  606. #endif
  607.  
  608.