386BSD 0.1 development
[unix-history] / usr / src / libexec / uucp / sys4.unx
CommitLineData
af364716
WJ
1/* sys4.unx
2 The system dependent routines to read command files for Unix.
3 These routines are used by uucico to get the commands it should
4 execute.
5
6 Copyright (C) 1991, 1992 Ian Lance Taylor
7
8 This file is part of the Taylor UUCP package.
9
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License as
12 published by the Free Software Foundation; either version 2 of the
13 License, or (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23
24 The author of the program may be contacted at ian@airs.com or
25 c/o AIRS, P.O. Box 520, Waltham, MA 02254.
26
27 $Log: sys4.unx,v $
28 Revision 1.17 1992/03/12 19:56:10 ian
29 Debugging based on types rather than number
30
31 Revision 1.16 1992/03/11 00:18:50 ian
32 Save temporary file if file send fails
33
34 Revision 1.15 1992/03/04 21:34:12 ian
35 Johan Vromans: set *pbgrade correctly in fsysdep_has_work
36
37 Revision 1.14 1992/03/04 02:13:23 ian
38 Bob Denny: warn if we can't open a command file
39
40 Revision 1.13 1992/02/23 03:26:51 ian
41 Overhaul to use automatic configure shell script
42
43 Revision 1.12 1992/02/20 04:18:59 ian
44 Added uustat
45
46 Revision 1.11 1992/02/09 03:14:48 ian
47 Added HAVE_OLD_DIRECTORIES for systems without readdir routines
48
49 Revision 1.10 1992/02/08 20:11:47 ian
50 Work data was freed up incorrectly if a file transfer failed in the middle
51
52 Revision 1.9 1992/02/08 03:54:18 ian
53 Include <string.h> only in <uucp.h>, added 1992 copyright
54
55 Revision 1.8 1992/01/29 18:28:34 ian
56 Patrick Smith: fixed casts in iswork_cmp
57
58 Revision 1.7 1991/12/29 04:04:18 ian
59 Added a bunch of extern definitions
60
61 Revision 1.6 1991/12/20 00:10:39 ian
62 Sort the earlier files so that bsearch can work correctly
63
64 Revision 1.5 1991/12/19 01:09:38 ian
65 David Nugent: rescan work directory before quitting
66
67 Revision 1.4 1991/11/16 00:45:50 ian
68 If fsysdep_work frees qSwork_file, it must set it to NULL
69
70 Revision 1.3 1991/11/15 21:58:02 ian
71 Move ssline definition out of ssfile
72
73 Revision 1.2 1991/11/08 04:47:19 ian
74 Hannu Strang: don't check system name for BNU work files
75
76 Revision 1.1 1991/09/10 19:45:50 ian
77 Initial revision
78
79 */
80
81#include "uucp.h"
82
83#if USE_RCS_ID
84char sys4_unx_rcsid[] = "$Id: sys4.unx,v 1.17 1992/03/12 19:56:10 ian Rel $";
85#endif
86
87#include <ctype.h>
88#include <errno.h>
89
90#include "system.h"
91#include "sysdep.h"
92
93#if HAVE_OPENDIR
94#if HAVE_DIRENT_H
95#include <dirent.h>
96#else /* ! HAVE_DIRENT_H */
97#include <sys/dir.h>
98#define dirent direct
99#endif /* ! HAVE_DIRENT_H */
100#endif /* HAVE_OPENDIR */
101
102/* External functions. */
103extern int fclose ();
104\f
105/* Local functions. */
106
107static const char *zswork_directory P((const char *zsystem));
108static boolean fswork_file P((const char *zsystem, const char *zfile,
109 char *pbgrade));
110\f
111/* These functions can support multiple actions going on at once.
112 This allows the UUCP package to send and receive multiple files at
113 the same time. This is a very flexible feature, but I'm not sure
114 it will actually be used all that much.
115
116 The ssfile structure holds a command file name and all the lines
117 read in from that command file. The union within the ssline
118 structure initially holds a line from the file and then holds a
119 pointer back to the ssfile structure; a pointer to this union is
120 used as a sequence pointer. The ztemp entry of the ssline
121 structure holds the name of a temporary file to delete, if any. */
122
123#define CFILELINES (10)
124
125struct ssline
126{
127 char *zline;
128 struct ssfile *qfile;
129 char *ztemp;
130};
131
132struct ssfile
133{
134 char *zfile;
135 int clines;
136 int cdid;
137 struct ssline aslines[CFILELINES];
138};
139
140/* Static variables for the work scan. */
141
142static char **azSwork_files;
143static int cSwork_files;
144static int iSwork_file;
145static struct ssfile *qSwork_file;
146
147/* Given a system name, return a directory to search for work. */
148
149static const char *
150zswork_directory (zsystem)
151 const char *zsystem;
152{
153#if SPOOLDIR_V2
154 return ".";
155#endif /* SPOOLDIR_V2 */
156#if SPOOLDIR_BSD42 | SPOOLDIR_BSD43
157 return "C.";
158#endif /* SPOOLDIR_BSD42 | SPOOLDIR_BSD43 */
159#if SPOOLDIR_BNU
160 return zsystem;
161#endif /* SPOOLDIR_BNU */
162#if SPOOLDIR_ULTRIX
163 static int c;
164 static char *z;
165 int cwant;
166
167 cwant = strlen (zsystem);
168 if (cwant < sizeof "DEFAULT" - 1)
169 cwant = sizeof "DEFAULT" - 1;
170 cwant += sizeof "sys//C.";
171 if (c < cwant)
172 {
173 xfree ((pointer) z);
174 z = (char *) xmalloc (cwant);
175 c = cwant;
176 }
177
178 if (fsultrix_has_spool (zsystem))
179 sprintf (z, "sys/%s/C.", zsystem);
180 else
181 strcpy (z, "sys/DEFAULT/C.");
182 return z;
183#endif /* SPOOLDIR_ULTRIX */
184#if SPOOLDIR_TAYLOR
185 static int c;
186 static char *z;
187 int cwant;
188
189 cwant = strlen (zsystem) + sizeof "/C.";
190 if (c < cwant)
191 {
192 xfree ((pointer) z);
193 z = (char *) xmalloc (cwant);
194 c = cwant;
195 }
196 sprintf (z, "%s/C.", zsystem);
197 return z;
198#endif /* SPOOLDIR_TAYLOR */
199}
200
201/* See whether a file name from the directory returned by
202 zswork_directory is really a command for a particular system.
203 Return the command grade. */
204
205/*ARGSUSED*/
206static boolean
207fswork_file (zsystem, zfile, pbgrade)
208 const char *zsystem;
209 const char *zfile;
210 char *pbgrade;
211{
212#if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43 || SPOOLDIR_ULTRIX
213 int cfilesys, csys;
214
215 /* The file name should be C.ssssssgqqqq, where g is exactly one
216 letter and qqqq is exactly four numbers. The system name may be
217 truncated to six or seven characters. The system name of the
218 file must match the system name we're looking for, since there
219 could be work files for several systems in one directory. */
220 if (zfile[0] != 'C' || zfile[1] != '.')
221 return FALSE;
222 csys = strlen (zsystem);
223 cfilesys = strlen (zfile) - 7;
224 if (csys != cfilesys
225 && (csys < 6 || (cfilesys != 6 && cfilesys != 7)))
226 return FALSE;
227 *pbgrade = zfile[cfilesys + 2];
228 return strncmp (zfile + 2, zsystem, cfilesys) == 0;
229#endif /* V2 || BSD42 || BSD43 || ULTRIX */
230#if SPOOLDIR_BNU
231 int clen;
232
233 /* The file name should be C.ssssssgqqqq where g is exactly one
234 letter and qqqq is exactly four numbers or letters. We don't
235 check the system name, because it is guaranteed by the directory
236 we are looking in and AIX uucp sets it to the local system rather
237 than the remote one. */
238 if (zfile[0] != 'C' || zfile[1] != '.')
239 return FALSE;
240 clen = strlen (zfile);
241 if (clen < 7)
242 return FALSE;
243 *pbgrade = zfile[clen - 5];
244 return TRUE;
245#endif /* SPOOLDIR_BNU */
246#if SPOOLDIR_TAYLOR
247 /* We don't keep the system name in the file name, since that
248 forces truncation. Our file names are always C.gqqqq. */
249 *pbgrade = zfile[2];
250 return (zfile[0] == 'C'
251 && zfile[1] == '.'
252 && strlen (zfile) == 7);
253#endif /* SPOOLDIR_TAYLOR */
254}
255
256/* A comparison function to look through the list of file names. */
257
258static int iswork_cmp P((constpointer pkey, constpointer pdatum));
259
260static int
261iswork_cmp (pkey, pdatum)
262 constpointer pkey;
263 constpointer pdatum;
264{
265 const char * const *pzkey = (const char * const *) pkey;
266 const char * const *pzdatum = (const char * const *) pdatum;
267
268 return strcmp (*pzkey, *pzdatum);
269}
270
271/* See whether there is any work to do for a particular system. If
272 any work is found, *pbgrade is set to highest grade found. */
273
274/*ARGSUSED*/
275boolean
276fsysdep_has_work (qsys, pbgrade)
277 const struct ssysteminfo *qsys;
278 char *pbgrade;
279{
280 boolean fret;
281 const char *zdir;
282 DIR *qdir;
283 struct dirent *qentry;
284
285 fret = FALSE;
286 *pbgrade = BGRADE_LOW;
287
288 if (azSwork_files != NULL && iSwork_file < cSwork_files)
289 {
290 int i;
291
292 fret = TRUE;
293 for (i = iSwork_file; i < cSwork_files; i++)
294 {
295 char bgrade;
296
297 (void) fswork_file (qsys->zname, azSwork_files[i], &bgrade);
298 if (igradecmp (bgrade, *pbgrade) < 0)
299 *pbgrade = bgrade;
300 }
301 }
302
303 zdir = zswork_directory (qsys->zname);
304 if (zdir == NULL)
305 return FALSE;
306
307 qdir = opendir ((char *) zdir);
308 if (qdir == NULL)
309 return FALSE;
310 while ((qentry = readdir (qdir)) != NULL)
311 {
312 char bgrade;
313 char *zname;
314
315 /* If this is a work file and we haven't seen it before, return
316 TRUE. Also, determine the grade to return. */
317 zname = qentry->d_name;
318 if (fswork_file (qsys->zname, qentry->d_name, &bgrade)
319 && (azSwork_files == NULL
320 || bsearch ((pointer) &zname,
321 (pointer) azSwork_files,
322 cSwork_files, sizeof (char *),
323 iswork_cmp) == NULL))
324 {
325 fret = TRUE;
326 if (igradecmp (bgrade, *pbgrade) < 0)
327 *pbgrade = bgrade;
328 }
329 }
330 closedir (qdir);
331 return fret;
332}
333
334/* Initialize the work scan. We have to read all the files in the
335 work directory, so that we can sort them by work grade. The bgrade
336 argument is the minimum grade to consider. We don't want to return
337 files that we have already considered; usysdep_get_work_free will
338 clear the data out when we are done with the system. This returns
339 FALSE on error. */
340
341#define CWORKFILES (10)
342
343/*ARGSUSED*/
344boolean
345fsysdep_get_work_init (qsys, bgrade)
346 const struct ssysteminfo *qsys;
347 int bgrade;
348{
349 const char *zdir;
350 DIR *qdir;
351 struct dirent *qentry;
352 int chad;
353 int callocated;
354
355 zdir = zswork_directory (qsys->zname);
356 if (zdir == NULL)
357 return FALSE;
358
359 qdir = opendir ((char *) zdir);
360 if (qdir == NULL)
361 {
362 if (errno != ENOENT)
363 ulog (LOG_ERROR, "opendir (%s): %s", zdir, strerror (errno));
364 return FALSE;
365 }
366
367 chad = cSwork_files;
368 callocated = cSwork_files;
369
370 /* Sort the files we already know about so that we can check the new
371 ones with bsearch. It would be faster to use a hash table, and
372 the code should be probably be changed. The sort done at the end
373 of this function does not suffice because it only includes the
374 files added last time, and does not sort the entire array. Some
375 (bad) qsort implementations are very slow when given a sorted
376 array, which causes particularly bad effects here. */
377 if (chad > 0)
378 qsort ((pointer) azSwork_files, chad, sizeof (char *), iswork_cmp);
379
380 while ((qentry = readdir (qdir)) != NULL)
381 {
382 char bfilegrade;
383 char *zname;
384
385 zname = qentry->d_name;
386 if (fswork_file (qsys->zname, qentry->d_name, &bfilegrade)
387 && (azSwork_files == NULL
388 || bsearch ((pointer) &zname,
389 (pointer) azSwork_files,
390 chad, sizeof (char *),
391 iswork_cmp) == NULL))
392 {
393 if (igradecmp (bgrade, bfilegrade) < 0)
394 continue;
395
396 DEBUG_MESSAGE1 (DEBUG_SPOOLDIR,
397 "fsysdep_get_work_init: Found %s",
398 qentry->d_name);
399
400 if (cSwork_files >= callocated)
401 {
402 callocated += CWORKFILES;
403 azSwork_files =
404 (char **) xrealloc ((pointer) azSwork_files,
405 callocated * sizeof (char *));
406 }
407
408 azSwork_files[cSwork_files] = xstrdup (qentry->d_name);
409 ++cSwork_files;
410 }
411 }
412
413 closedir (qdir);
414
415 /* Sorting the files alphabetically will get the grades in the
416 right order, since all the file prefixes are the same. */
417
418 if (cSwork_files > chad)
419 qsort ((pointer) (azSwork_files + chad), cSwork_files - chad,
420 sizeof (char *), iswork_cmp);
421
422 return TRUE;
423}
424
425/* Get the next work entry for a system. This must parse the next
426 line in the next work file. The type of command is set into
427 qcmd->bcmd; if there are no more commands we call
428 fsysdep_get_work_init to rescan, in case any came in since the last
429 call. If there are still no commands, qcmd->bcmd is set to 'H'.
430 Each field in the structure is set to point to a spot in an
431 malloced string. The only time we use the grade here is when
432 calling fsysdep_get_work_init to rescan. */
433
434boolean
435fsysdep_get_work (qsys, bgrade, qcmd)
436 const struct ssysteminfo *qsys;
437 int bgrade;
438 struct scmd *qcmd;
439{
440 const char *zdir;
441
442 if (qSwork_file != NULL && qSwork_file->cdid >= qSwork_file->clines)
443 qSwork_file = NULL;
444
445 if (azSwork_files == NULL)
446 {
447 qcmd->bcmd = 'H';
448 return TRUE;
449 }
450
451 zdir = NULL;
452
453 /* This loop continues until a line is returned. */
454 while (TRUE)
455 {
456 /* This loop continues until a file is opened and read in. */
457 while (qSwork_file == NULL)
458 {
459 FILE *e;
460 struct ssfile *qfile;
461 int iline, callocated;
462 char *zline;
463 const char *zname;
464
465 /* Read all the lines of a command file into memory. */
466
467 do
468 {
469 if (iSwork_file >= cSwork_files)
470 {
471 /* Rescan the work directory. */
472 if (! fsysdep_get_work_init (qsys, bgrade))
473 return FALSE;
474 if (iSwork_file >= cSwork_files)
475 {
476 qcmd->bcmd = 'H';
477 return TRUE;
478 }
479 }
480
481 if (zdir == NULL)
482 {
483 zdir = zswork_directory (qsys->zname);
484 if (zdir == NULL)
485 return FALSE;
486 }
487
488 zname = zsappend (zdir, azSwork_files[iSwork_file]);
489
490 ++iSwork_file;
491
492 if (zname == NULL)
493 return FALSE;
494
495 e = fopen (zname, "r");
496 if (e == NULL)
497 ulog (LOG_ERROR, "fopen (%s): %s", zname,
498 strerror (errno));
499 }
500 while (e == NULL);
501
502 qfile = (struct ssfile *) xmalloc (sizeof (struct ssfile));
503 callocated = CFILELINES;
504 iline = 0;
505
506 while ((zline = zfgets (e, FALSE)) != NULL)
507 {
508 if (iline >= callocated)
509 {
510 /* The sizeof (struct ssfile) includes CFILELINES
511 entries already, so using callocated * sizeof
512 (struct ssline) will give us callocated *
513 CFILELINES entries. */
514 qfile =
515 ((struct ssfile *)
516 xrealloc ((pointer) qfile,
517 (sizeof (struct ssfile) +
518 (callocated * sizeof (struct ssline)))));
519 callocated += CFILELINES;
520 }
521 qfile->aslines[iline].zline = zline;
522 qfile->aslines[iline].qfile = NULL;
523 qfile->aslines[iline].ztemp = NULL;
524 iline++;
525 }
526
527 if (fclose (e) != 0)
528 ulog (LOG_ERROR, "fclose: %s", strerror (errno));
529
530 if (iline == 0)
531 {
532 /* There was nothing in the file; remove it and look
533 for the next one. */
534 xfree ((pointer) qfile);
535 if (remove (zname) != 0)
536 ulog (LOG_ERROR, "remove (%s): %s", zname,
537 strerror (errno));
538 }
539 else
540 {
541 qfile->zfile = xstrdup (zname);
542 qfile->clines = iline;
543 qfile->cdid = 0;
544 qSwork_file = qfile;
545 }
546 }
547
548 /* This loop continues until all the lines from the current file
549 are used up, or a line is returned. */
550 while (TRUE)
551 {
552 int iline;
553
554 if (qSwork_file->cdid >= qSwork_file->clines)
555 {
556 /* We don't want to free qSwork_file here, since it must
557 remain until all the lines have been completed. It
558 is freed in fsysdep_did_work. */
559 qSwork_file = NULL;
560 /* Go back to the main loop which finds another file. */
561 break;
562 }
563
564 iline = qSwork_file->cdid;
565 ++qSwork_file->cdid;
566
567 /* Now parse the line into a command. */
568
569 if (! fparse_cmd (qSwork_file->aslines[iline].zline, qcmd))
570 {
571 ulog (LOG_ERROR, "Bad line in command file %s",
572 qSwork_file->zfile);
573 xfree ((pointer) qSwork_file->aslines[iline].zline);
574 qSwork_file->aslines[iline].zline = NULL;
575 continue;
576 }
577
578 if (qcmd->bcmd == 'S')
579 {
580 const char *zreal;
581
582 zreal = zsysdep_spool_file_name (qsys, qcmd->ztemp);
583 if (zreal == NULL)
584 {
585 xfree ((pointer) qSwork_file->aslines[iline].zline);
586 qSwork_file->aslines[iline].zline = NULL;
587 return FALSE;
588 }
589
590 qSwork_file->aslines[iline].ztemp = xstrdup (zreal);
591 }
592
593 qSwork_file->aslines[iline].qfile = qSwork_file;
594 qcmd->pseq = (pointer)(&qSwork_file->aslines[iline]);
595
596 return TRUE;
597 }
598 }
599}
600
601/* When a command has been complete, fsysdep_did_work is called. The
602 sequence entry was set above to be the address of an aslines
603 structure whose pfile entry points to the ssfile corresponding to
604 this file. We can then check whether all the lines have been
605 completed (they will have been if the pfile entry is NULL) and
606 remove the file if they have been. This means that we only remove
607 a command file if we manage to complete every transfer it specifies
608 in a single UUCP session. I don't know if this is how regular UUCP
609 works. */
610
611boolean
612fsysdep_did_work (pseq)
613 pointer pseq;
614{
615 struct ssfile *qfile;
616 struct ssline *qline;
617 int i;
618
619 qline = (struct ssline *) pseq;
620
621 xfree ((pointer) qline->zline);
622 qline->zline = NULL;
623
624 qfile = qline->qfile;
625 qline->qfile = NULL;
626
627 /* Remove the temporary file, if there is one. It really doesn't
628 matter if this fails, and not checking the return value lets us
629 attempt to remove D.0 or whatever an unused temporary file is
630 called without complaining. */
631 if (qline->ztemp != NULL)
632 (void) remove (qline->ztemp);
633
634 /* If not all the lines have been returned from bsysdep_get_work,
635 we can't remove the file yet. */
636 if (qfile->cdid < qfile->clines)
637 return TRUE;
638
639 /* See whether all the commands have been completed. */
640 for (i = 0; i < qfile->clines; i++)
641 if (qfile->aslines[i].qfile != NULL)
642 return TRUE;
643
644 /* All commands have finished. */
645 if (remove (qfile->zfile) != 0)
646 {
647 ulog (LOG_ERROR, "remove (%s): %s", qfile->zfile,
648 strerror (errno));
649 return FALSE;
650 }
651
652 xfree ((pointer) qfile->zfile);
653 xfree ((pointer) qfile);
654
655 if (qfile == qSwork_file)
656 qSwork_file = NULL;
657
658 return TRUE;
659}
660
661/* Free up the results of a work scan, when we're done with this
662 system. */
663
664/*ARGSUSED*/
665void
666usysdep_get_work_free (qsys)
667 const struct ssysteminfo *qsys;
668{
669 if (azSwork_files != NULL)
670 {
671 int i;
672
673 for (i = 0; i < cSwork_files; i++)
674 xfree ((pointer) azSwork_files[i]);
675 xfree ((pointer) azSwork_files);
676 azSwork_files = NULL;
677 cSwork_files = 0;
678 iSwork_file = 0;
679 }
680 if (qSwork_file != NULL)
681 {
682 int i;
683
684 xfree ((pointer) qSwork_file->zfile);
685 for (i = 0; i < qSwork_file->cdid; i++)
686 {
687 xfree ((pointer) qSwork_file->aslines[i].zline);
688 xfree ((pointer) qSwork_file->aslines[i].ztemp);
689 }
690 for (i = qSwork_file->cdid; i < qSwork_file->clines; i++)
691 xfree ((pointer) qSwork_file->aslines[i].zline);
692 xfree ((pointer) qSwork_file);
693 qSwork_file = NULL;
694 }
695}
696\f
697/* Save the temporary file used by a send command, and return an
698 informative message to mail to the requestor. This is called when
699 a file transfer failed, to make sure that the potentially valuable
700 file is not completely lost. */
701
702const char *
703zsysdep_save_temp_file (pseq)
704 pointer pseq;
705{
706 struct ssline *qline = (struct ssline *) pseq;
707 char *zto, *zslash;
708 int cwant;
709 static char *zbuf;
710 static int cbuf;
711
712 if (! fsysdep_file_exists (qline->ztemp))
713 return NULL;
714
715 zslash = strrchr (qline->ztemp, '/');
716 if (zslash == NULL)
717 zslash = qline->ztemp;
718 else
719 ++zslash;
720
721 zto = (char *) alloca (sizeof PRESERVEDIR + 1 + strlen (zslash));
722 sprintf (zto, "%s/%s", PRESERVEDIR, zslash);
723
724 /* We must make sure that the PRESERVEDIR directory exists, since
725 fsysdep_move_file won't create it for us. */
726
727 if (! fsdirectory_exists (PRESERVEDIR))
728 {
729 if (mkdir (PRESERVEDIR, IDIRECTORY_MODE) != 0)
730 {
731 ulog (LOG_ERROR, "mkdir (%s): %s", PRESERVEDIR,
732 strerror (errno));
733 return "Could not create preservation directory";
734 }
735 }
736
737 if (! fsysdep_move_file (qline->ztemp, zto, 0, FALSE,
738 (const char *) NULL))
739 return "Could not move file to preservation directory";
740
741 cwant = sizeof "File saved as\n\t" + strlen (zSpooldir) + 1 + strlen (zto);
742 if (cwant > cbuf)
743 {
744 zbuf = (char *) xrealloc ((pointer) zbuf, cwant);
745 cbuf = cwant;
746 }
747
748 sprintf (zbuf, "File saved as\n\t%s/%s", zSpooldir, zto);
749 return zbuf;
750}
751\f
752/* Get the jobid of a work file. This is needed by uustat. */
753
754const char *
755zsysdep_jobid (qsys, pseq)
756 const struct ssysteminfo *qsys;
757 pointer pseq;
758{
759 return zsfile_to_jobid (qsys, ((struct ssline *) pseq)->qfile->zfile);
760}
761\f
762/*
763 Local variables:
764 mode:c
765 End:
766 */