1 /*
2  * Red Hat package gateway for the ESP Package Manager (EPM).
3  *
4  * Copyright © 1999-2020 by Michael R Sweet
5  * Copyright © 1999-2010 by Easy Software Products.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  */
17 
18 /*
19  * Include necessary headers...
20  */
21 
22 #include "epm.h"
23 
24 
25 /*
26  * Local functions...
27  */
28 
29 static int	move_rpms(const char *prodname, const char *directory,
30 		          const char *platname, dist_t *dist,
31 			  struct utsname *platform,
32 			  const char *rpmdir, const char *subpackage,
33 			  const char *release);
34 static int	write_spec(int format, const char *prodname, dist_t *dist,
35 		           FILE *fp, const char *subpackage);
36 
37 
38 /*
39  * 'make_rpm()' - Make a Red Hat software distribution package.
40  */
41 
42 int					/* O - 0 = success, 1 = fail */
make_rpm(int format,const char * prodname,const char * directory,const char * platname,dist_t * dist,struct utsname * platform,const char * setup,const char * types)43 make_rpm(int            format,		/* I - Subformat */
44          const char     *prodname,	/* I - Product short name */
45          const char     *directory,	/* I - Directory for distribution files */
46          const char     *platname,	/* I - Platform name */
47          dist_t         *dist,		/* I - Distribution information */
48 	 struct utsname *platform,	/* I - Platform information */
49          const char     *setup,		/* I - Setup GUI image */
50          const char     *types)		/* I - Setup GUI install types */
51 {
52   int		i;			/* Looping var */
53   FILE		*fp;			/* Spec file */
54   tarf_t	*tarfile;		/* Distribution tar file */
55   char		specname[1024];		/* Spec filename */
56   char		name[1024],		/* Product filename */
57 		filename[1024];		/* Destination filename */
58   file_t	*file;			/* Current distribution file */
59   char		absdir[1024];		/* Absolute directory */
60   char		rpmdir[1024];		/* RPMDIR env var */
61   char		release[256];		/* Release: number */
62   const char	*build_option;		/* Additional rpmbuild option */
63 
64 
65   if (Verbosity)
66     puts("Creating RPM distribution...");
67 
68   if (directory[0] != '/')
69   {
70     char	current[1024];		/* Current directory */
71 
72 
73     getcwd(current, sizeof(current));
74 
75     snprintf(absdir, sizeof(absdir), "%s/%s", current, directory);
76   }
77   else
78     strlcpy(absdir, directory, sizeof(absdir));
79 
80  /*
81   * Write the spec file for RPM...
82   */
83 
84   if (Verbosity)
85     puts("Creating spec file...");
86 
87   snprintf(specname, sizeof(specname), "%s/%s.spec", directory, prodname);
88 
89   if ((fp = fopen(specname, "w")) == NULL)
90   {
91     fprintf(stderr, "epm: Unable to create spec file \"%s\": %s\n", specname, strerror(errno));
92     return (1);
93   }
94 
95   if (dist->release[0])
96     strlcpy(release, dist->release, sizeof(release));
97   else
98     strlcpy(release, "0", sizeof(release));
99 
100   fprintf(fp, "Name: %s\n", prodname);
101   fprintf(fp, "Version: %s\n", dist->version);
102   if (dist->epoch)
103     fprintf(fp, "Epoch: %d\n", dist->epoch);
104   fprintf(fp, "Release: %s\n", release);
105   fprintf(fp, "License: %s\n", dist->copyright);
106   fprintf(fp, "Packager: %s\n", dist->packager);
107   fprintf(fp, "Vendor: %s\n", dist->vendor);
108 
109  /*
110   * Tell RPM to put the distributions in the output directory...
111   */
112 
113 #ifdef EPM_RPMTOPDIR
114   fprintf(fp, "%%define _topdir %s\n", absdir);
115   strlcpy(rpmdir, absdir, sizeof(rpmdir));
116 #else
117   if (getenv("RPMDIR"))
118     strlcpy(rpmdir, getenv("RPMDIR"), sizeof(rpmdir));
119   else if (!access("/usr/src/redhat", 0))
120     strlcpy(rpmdir, "/usr/src/redhat", sizeof(rpmdir));
121   else if (!access("/usr/src/Mandrake", 0))
122     strlcpy(rpmdir, "/usr/src/Mandrake", sizeof(rpmdir));
123   else
124     strlcpy(rpmdir, "/usr/src/RPM", sizeof(rpmdir));
125 #endif /* EPM_RPMTOPDIR */
126 
127   snprintf(filename, sizeof(filename), "%s/BUILD", directory);
128 
129   make_directory(filename, 0, getuid(), getgid());
130 
131   snprintf(filename, sizeof(filename), "%s/RPMS", directory);
132 
133   make_directory(filename, 0, getuid(), getgid());
134 
135   snprintf(filename, sizeof(filename), "%s/rpms", directory);
136   symlink("RPMS", filename);
137 
138   if (!strcmp(platform->machine, "intel"))
139     snprintf(filename, sizeof(filename), "%s/RPMS/i386", directory);
140   else if (!strcmp(platform->machine, "ppc"))
141     snprintf(filename, sizeof(filename), "%s/RPMS/ppc", directory);
142   else
143     snprintf(filename, sizeof(filename), "%s/RPMS/%s", directory,
144              platform->machine);
145 
146   make_directory(filename, 0, getuid(), getgid());
147 
148  /*
149   * Now list all of the subpackages...
150   */
151 
152   write_spec(format, prodname, dist, fp, NULL);
153   for (i = 0; i < dist->num_subpackages; i ++)
154     write_spec(format, prodname, dist, fp, dist->subpackages[i]);
155 
156  /*
157   * Close the spec file...
158   */
159 
160   fclose(fp);
161 
162  /*
163   * Copy the files over...
164   */
165 
166   if (Verbosity)
167     puts("Copying temporary distribution files...");
168 
169   for (i = dist->num_files, file = dist->files; i > 0; i --, file ++)
170   {
171    /*
172     * Copy the file or make the directory or make the symlink as needed...
173     */
174 
175     switch (tolower(file->type))
176     {
177       case 'c' :
178       case 'f' :
179           snprintf(filename, sizeof(filename), "%s/buildroot%s", directory, file->dst);
180 
181 	  if (Verbosity > 1)
182 	    printf("%s -> %s...\n", file->src, filename);
183 
184 	  if (copy_file(filename, file->src, 0, -1, -1))
185 	    return (1);
186           break;
187       case 'i' :
188 	  snprintf(filename, sizeof(filename), "%s/buildroot%s/init.d/%s", directory, SoftwareDir, file->dst);
189 
190 	  if (Verbosity > 1)
191 	    printf("%s -> %s...\n", file->src, filename);
192 
193 	  if (copy_file(filename, file->src, 0, -1, -1))
194 	    return (1);
195           break;
196       case 'd' :
197           snprintf(filename, sizeof(filename), "%s/buildroot%s", directory, file->dst);
198 
199 	  if (Verbosity > 1)
200 	    printf("Directory %s...\n", filename);
201 
202           make_directory(filename, 0755, -1, -1);
203           break;
204       case 'l' :
205           snprintf(filename, sizeof(filename), "%s/buildroot%s", directory, file->dst);
206 
207 	  if (Verbosity > 1)
208 	    printf("%s -> %s...\n", file->src, filename);
209 
210           make_link(filename, file->src);
211           break;
212     }
213   }
214 
215  /*
216   * Build the distribution from the spec file...
217   */
218 
219   if (Verbosity)
220     puts("Building RPM binary distribution...");
221 
222   if (format == PACKAGE_RPM_SIGNED)
223     build_option = "-signed ";
224   else
225     build_option = "";
226 
227   if (!strcmp(platform->machine, "intel"))
228   {
229     if (run_command(NULL, EPM_RPMBUILD " -bb --buildroot \"%s/buildroot\" "
230                           EPM_RPMARCH "i386 %s%s", absdir, build_option,
231 			  specname))
232       return (1);
233   }
234   else if (!strcmp(platform->machine, "ppc"))
235   {
236     if (run_command(NULL, EPM_RPMBUILD " -bb --buildroot \"%s/buildroot\" "
237                           EPM_RPMARCH "ppc %s%s", absdir, build_option,
238 			  specname))
239       return (1);
240   }
241   else if (run_command(NULL, EPM_RPMBUILD " -bb --buildroot \"%s/buildroot\" "
242                        EPM_RPMARCH "%s %s%s", absdir, platform->machine,
243 		       build_option, specname))
244     return (1);
245 
246  /*
247   * Move the RPMs to the local directory and rename the RPMs using the
248   * product name specified by the user...
249   */
250 
251   move_rpms(prodname, directory, platname, dist, platform, rpmdir, NULL,
252             release);
253 
254   for (i = 0; i < dist->num_subpackages; i ++)
255     move_rpms(prodname, directory, platname, dist, platform, rpmdir,
256               dist->subpackages[i], release);
257 
258  /*
259   * Build a compressed tar file to hold all of the subpackages...
260   */
261 
262   if (dist->num_subpackages || setup)
263   {
264    /*
265     * Figure out the full name of the distribution...
266     */
267 
268     if (dist->release[0])
269       snprintf(name, sizeof(name), "%s-%s-%s", prodname, dist->version,
270                dist->release);
271     else
272       snprintf(name, sizeof(name), "%s-%s", prodname, dist->version);
273 
274     if (platname[0])
275     {
276       strlcat(name, "-", sizeof(name));
277       strlcat(name, platname, sizeof(name));
278     }
279 
280    /*
281     * Create a compressed tar file...
282     */
283 
284     snprintf(filename, sizeof(filename), "%s/%s.rpm.tgz", directory, name);
285 
286     if ((tarfile = tar_open(filename, 1)) == NULL)
287       return (1);
288 
289    /*
290     * Archive the setup and uninst GUIs and their data files...
291     */
292 
293     if (setup)
294     {
295      /*
296       * Include the ESP Software Installation Wizard (setup)...
297       */
298 
299       const char	*setup_img;	/* Setup image name */
300       struct stat	srcstat;	/* File information */
301 
302 
303       if (stat(SetupProgram, &srcstat))
304       {
305 	fprintf(stderr, "epm: Unable to stat GUI setup program %s: %s\n",
306 		SetupProgram, strerror(errno));
307 	tar_close(tarfile);
308 	return (-1);
309       }
310 
311       if (tar_header(tarfile, TAR_NORMAL, 0555, srcstat.st_size,
312 	             srcstat.st_mtime, "root", "root", "setup", NULL) < 0)
313       {
314 	tar_close(tarfile);
315 	return (-1);
316       }
317 
318       if (tar_file(tarfile, SetupProgram) < 0)
319       {
320 	tar_close(tarfile);
321 	return (-1);
322       }
323 
324       if (Verbosity)
325 	printf("    %7.0fk setup\n", (srcstat.st_size + 1023) / 1024.0);
326 
327      /*
328       * And the image file...
329       */
330 
331       stat(setup, &srcstat);
332 
333       if (strlen(setup) > 4 && !strcmp(setup + strlen(setup) - 4, ".gif"))
334 	setup_img = "setup.gif";
335       else if (strlen(setup) > 4 && !strcmp(setup + strlen(setup) - 4, ".jpg"))
336 	setup_img = "setup.jpg";
337       else if (strlen(setup) > 4 && !strcmp(setup + strlen(setup) - 4, ".png"))
338 	setup_img = "setup.png";
339       else
340 	setup_img = "setup.xpm";
341 
342       if (tar_header(tarfile, TAR_NORMAL, 0444, srcstat.st_size,
343 	             srcstat.st_mtime, "root", "root", setup_img, NULL) < 0)
344       {
345 	tar_close(tarfile);
346 	return (-1);
347       }
348 
349       if (tar_file(tarfile, setup) < 0)
350       {
351 	tar_close(tarfile);
352 	return (-1);
353       }
354 
355       if (Verbosity)
356 	printf("    %7.0fk %s\n", (srcstat.st_size + 1023) / 1024.0, setup_img);
357 
358      /*
359       * And the types file...
360       */
361 
362       if (types)
363       {
364 	stat(types, &srcstat);
365 
366 	if (tar_header(tarfile, TAR_NORMAL, 0444, srcstat.st_size,
367 		       srcstat.st_mtime, "root", "root", "setup.types", NULL) < 0)
368 	{
369           tar_close(tarfile);
370 	  return (-1);
371 	}
372 
373 	if (tar_file(tarfile, types) < 0)
374 	{
375           tar_close(tarfile);
376 	  return (-1);
377 	}
378 
379 	if (Verbosity)
380           printf("    %7.0fk setup.types\n", (srcstat.st_size + 1023) / 1024.0);
381       }
382 
383      /*
384       * Include the ESP Software Removal Wizard (uninst)...
385       */
386 
387       if (stat(UninstProgram, &srcstat))
388       {
389 	fprintf(stderr, "epm: Unable to stat GUI uninstall program %s: %s\n",
390 		UninstProgram, strerror(errno));
391 	tar_close(tarfile);
392 	return (-1);
393       }
394 
395       if (tar_header(tarfile, TAR_NORMAL, 0555, srcstat.st_size,
396 	             srcstat.st_mtime, "root", "root", "uninst", NULL) < 0)
397       {
398 	tar_close(tarfile);
399 	return (-1);
400       }
401 
402       if (tar_file(tarfile, UninstProgram) < 0)
403       {
404 	tar_close(tarfile);
405 	return (-1);
406       }
407 
408       if (Verbosity)
409 	printf("    %7.0fk uninst\n", (srcstat.st_size + 1023) / 1024.0);
410     }
411 
412    /*
413     * Archive the main package and subpackages...
414     */
415 
416     if (tar_package(tarfile, "rpm", prodname, directory, platname, dist, NULL))
417     {
418       tar_close(tarfile);
419       return (1);
420     }
421 
422     for (i = 0; i < dist->num_subpackages; i ++)
423     {
424       if (tar_package(tarfile, "rpm", prodname, directory, platname, dist,
425                       dist->subpackages[i]))
426       {
427 	tar_close(tarfile);
428 	return (1);
429       }
430     }
431 
432     tar_close(tarfile);
433   }
434 
435  /*
436   * Remove temporary files...
437   */
438 
439   if (!KeepFiles)
440   {
441     if (Verbosity)
442       puts("Removing temporary distribution files...");
443 
444     snprintf(filename, sizeof(filename), "%s/BUILD", directory);
445     unlink_directory(filename);
446 
447     snprintf(filename, sizeof(filename), "%s/RPMS", directory);
448     unlink_directory(filename);
449 
450     snprintf(filename, sizeof(filename), "%s/rpms", directory);
451     unlink(filename);
452 
453     unlink(specname);
454 
455     if (dist->num_subpackages)
456     {
457      /*
458       * Remove .rpm files since they are now in a .tgz file...
459       */
460 
461       unlink_package("rpm", prodname, directory, platname, dist, NULL);
462 
463       for (i = 0; i < dist->num_subpackages; i ++)
464 	unlink_package("rpm", prodname, directory, platname, dist,
465                        dist->subpackages[i]);
466     }
467   }
468 
469   return (0);
470 }
471 
472 
473 /*
474  * 'move_rpms()' - Move RPM packages to the build directory...
475  */
476 
477 static int				/* O - 0 = success, 1 = fail */
move_rpms(const char * prodname,const char * directory,const char * platname,dist_t * dist,struct utsname * platform,const char * rpmdir,const char * subpackage,const char * release)478 move_rpms(const char     *prodname,	/* I - Product short name */
479           const char     *directory,	/* I - Directory for distribution files */
480           const char     *platname,	/* I - Platform name */
481           dist_t         *dist,		/* I - Distribution information */
482 	  struct utsname *platform,	/* I - Platform information */
483 	  const char     *rpmdir,	/* I - RPM directory */
484           const char     *subpackage,	/* I - Subpackage name */
485 	  const char     *release)	/* I - Release: value */
486 {
487   char		rpmname[1024];		/* RPM name */
488   char		prodfull[1024];		/* Full product name */
489   struct stat	rpminfo;		/* RPM file info */
490 
491 
492  /*
493   * Move the RPMs to the local directory and rename the RPMs using the
494   * product name specified by the user...
495   */
496 
497   if (subpackage)
498     snprintf(prodfull, sizeof(prodfull), "%s-%s", prodname, subpackage);
499   else
500     strlcpy(prodfull, prodname, sizeof(prodfull));
501 
502   if (dist->release[0])
503     snprintf(rpmname, sizeof(rpmname), "%s/%s-%s-%s", directory, prodfull,
504              dist->version, dist->release);
505   else
506     snprintf(rpmname, sizeof(rpmname), "%s/%s-%s", directory, prodfull,
507              dist->version);
508 
509   if (platname[0])
510   {
511     strlcat(rpmname, "-", sizeof(rpmname));
512     strlcat(rpmname, platname, sizeof(rpmname));
513   }
514 
515   strlcat(rpmname, ".rpm", sizeof(rpmname));
516 
517   if (!strcmp(platform->machine, "intel"))
518     run_command(NULL, "/bin/mv %s/RPMS/i386/%s-%s-%s.i386.rpm %s",
519 		rpmdir, prodfull, dist->version, release,
520 		rpmname);
521   else if (!strcmp(platform->sysname, "aix") && !strcmp(platform->machine, "ppc"))
522     run_command(NULL, "/bin/mv %s/RPMS/ppc/%s-%s-%s.%s%s.ppc.rpm %s",
523 		rpmdir, prodfull, dist->version, release, platform->sysname, platform->release,
524 		rpmname);
525   else if (!strcmp(platform->machine, "ppc"))
526     run_command(NULL, "/bin/mv %s/RPMS/powerpc/%s-%s-%s.powerpc.rpm %s",
527 		rpmdir, prodfull, dist->version, release,
528 		rpmname);
529   else
530     run_command(NULL, "/bin/mv %s/RPMS/%s/%s-%s-%s.%s.rpm %s",
531 		rpmdir, platform->machine, prodfull, dist->version,
532 		release, platform->machine, rpmname);
533 
534   if (Verbosity)
535   {
536     stat(rpmname, &rpminfo);
537 
538     printf("    %7.0fk  %s\n", rpminfo.st_size / 1024.0, rpmname);
539   }
540 
541   return (0);
542 }
543 
544 
545 /*
546  * 'write_spec()' - Write the subpackage-specific parts of the RPM spec file.
547  */
548 
549 static int				/* O - 0 on success, -1 on error */
write_spec(int format,const char * prodname,dist_t * dist,FILE * fp,const char * subpackage)550 write_spec(int        format,		/* I - Subformat */
551            const char *prodname,	/* I - Product name */
552 	   dist_t     *dist,		/* I - Distribution */
553            FILE       *fp,		/* I - Spec file */
554            const char *subpackage)	/* I - Subpackage name */
555 {
556   int		i;			/* Looping var */
557   char		name[1024];		/* Full product name */
558   const char	*product;		/* Product to depend on */
559   file_t	*file;			/* Current distribution file */
560   command_t	*c;			/* Current command */
561   depend_t	*d;			/* Current dependency */
562   const char	*runlevels;		/* Run levels */
563   int		number;			/* Start/stop number */
564   int		have_commands;		/* Have commands in current section? */
565 
566 
567  /*
568   * Get the name we'll use for the subpackage...
569   */
570 
571   if (subpackage)
572     snprintf(name, sizeof(name), " %s", subpackage);
573   else
574     name[0] = '\0';
575 
576  /*
577   * Common stuff...
578   */
579 
580   if (subpackage)
581   {
582     fprintf(fp, "%%package%s\n", name);
583     fprintf(fp, "Summary: %s", dist->product);
584 
585     for (i = 0; i < dist->num_descriptions; i ++)
586       if (dist->descriptions[i].subpackage == subpackage)
587 	break;
588 
589     if (i < dist->num_descriptions)
590     {
591       char	line[1024],		/* First line of description... */
592 		*ptr;			/* Pointer into line */
593 
594 
595       strlcpy(line, dist->descriptions[i].description, sizeof(line));
596       if ((ptr = strchr(line, '\n')) != NULL)
597         *ptr = '\0';
598 
599       fprintf(fp, " - %s", line);
600     }
601     fputs("\n", fp);
602   }
603   else
604     fprintf(fp, "Summary: %s\n", dist->product);
605 
606   fputs("Group: Applications\n", fp);
607 
608  /*
609   * List all of the dependencies...
610   */
611 
612   for (i = dist->num_depends, d = dist->depends; i > 0; i --, d ++)
613   {
614     if (d->subpackage != subpackage)
615       continue;
616 
617     if (!strcmp(d->product, "_self"))
618       product = prodname;
619     else
620       product = d->product;
621 
622     if (d->type == DEPEND_REQUIRES)
623       fprintf(fp, "Requires: %s", product);
624     else if (d->type == DEPEND_PROVIDES)
625       fprintf(fp, "Provides: %s", product);
626     else if (d->type == DEPEND_REPLACES)
627       fprintf(fp, "Obsoletes: %s", product);
628     else
629       fprintf(fp, "Conflicts: %s", product);
630 
631     if (d->vernumber[0] == 0)
632     {
633       if (d->vernumber[1] < INT_MAX)
634         fprintf(fp, " <= %s\n", d->version[1]);
635       else
636         putc('\n', fp);
637     }
638     else if (d->vernumber[0] && d->vernumber[1] < INT_MAX)
639     {
640       if (d->vernumber[0] < INT_MAX && d->vernumber[1] < INT_MAX)
641         fprintf(fp, " >= %s, %s <= %s\n", d->version[0], product,
642 	        d->version[1]);
643     }
644     else if (d->vernumber[0] != d->vernumber[1])
645       fprintf(fp, " >= %s\n", d->version[0]);
646     else
647       fprintf(fp, " = %s\n", d->version[0]);
648   }
649 
650   for (i = dist->num_commands, c = dist->commands; i > 0; i --, c ++)
651     if (c->type == COMMAND_LITERAL && c->subpackage == subpackage &&
652         !strcmp(c->section, "spec"))
653       break;
654 
655   if (i > 0)
656   {
657     for (; i > 0; i --, c ++)
658       if (c->type == COMMAND_LITERAL && c->subpackage == subpackage &&
659 	  !strcmp(c->section, "spec"))
660 	fprintf(fp, "%s\n", c->command);
661   }
662 
663  /*
664   * Pre/post install commands...
665   */
666 
667   for (i = dist->num_commands, c = dist->commands; i > 0; i --, c ++)
668     if (c->type == COMMAND_PRE_INSTALL && c->subpackage == subpackage)
669       break;
670 
671   if (i > 0)
672   {
673     fprintf(fp, "%%pre%s\n", name);
674     for (; i > 0; i --, c ++)
675       if (c->type == COMMAND_PRE_INSTALL && c->subpackage == subpackage)
676 	fprintf(fp, "%s\n", c->command);
677   }
678 
679   for (i = dist->num_commands, c = dist->commands; i > 0; i --, c ++)
680     if (c->type == COMMAND_POST_INSTALL && c->subpackage == subpackage)
681       break;
682 
683   if (i > 0)
684   {
685     have_commands = 1;
686 
687     fprintf(fp, "%%post%s\n", name);
688     for (; i > 0; i --, c ++)
689       if (c->type == COMMAND_POST_INSTALL && c->subpackage == subpackage)
690 	fprintf(fp, "%s\n", c->command);
691 
692   }
693   else
694     have_commands = 0;
695 
696   for (i = dist->num_files, file = dist->files; i > 0; i --, file ++)
697     if (tolower(file->type) == 'i' && file->subpackage == subpackage)
698       break;
699 
700   if (i)
701   {
702     if (!have_commands)
703       fprintf(fp, "%%post%s\n", name);
704 
705     fputs("if test \"x$1\" = x1; then\n", fp);
706     fputs("	echo Setting up init scripts...\n", fp);
707 
708    /*
709     * Find where the frigging init scripts go...
710     */
711 
712     fputs("	rcdir=\"\"\n", fp);
713     fputs("	for dir in /sbin/rc.d /sbin /etc/rc.d /etc ; do\n", fp);
714     fputs("		if test -d $dir/rc3.d -o -h $dir/rc3.d; then\n", fp);
715     fputs("			rcdir=\"$dir\"\n", fp);
716     fputs("		fi\n", fp);
717     fputs("	done\n", fp);
718     fputs("	if test \"$rcdir\" = \"\" ; then\n", fp);
719     fputs("		echo Unable to determine location of startup scripts!\n", fp);
720     fputs("	else\n", fp);
721     for (; i > 0; i --, file ++)
722     {
723       if (tolower(file->type) == 'i' && file->subpackage == subpackage)
724       {
725 	fputs("		if test -d $rcdir/init.d; then\n", fp);
726 	qprintf(fp, "			/bin/rm -f $rcdir/init.d/%s\n", file->dst);
727 	qprintf(fp, "			/bin/ln -s %s/init.d/%s "
728 		    "$rcdir/init.d/%s\n", SoftwareDir, file->dst, file->dst);
729 	fputs("		else\n", fp);
730 	fputs("			if test -d /etc/init.d; then\n", fp);
731 	qprintf(fp, "				/bin/rm -f /etc/init.d/%s\n", file->dst);
732 	qprintf(fp, "				/bin/ln -s %s/init.d/%s "
733 		    "/etc/init.d/%s\n", SoftwareDir, file->dst, file->dst);
734 	fputs("			fi\n", fp);
735 	fputs("		fi\n", fp);
736 
737 	for (runlevels = get_runlevels(dist->files + i, "0123456");
738 	     isdigit(*runlevels & 255);
739 	     runlevels ++)
740 	{
741 	  if (*runlevels == '0')
742 	    number = get_stop(file, 0);
743 	  else
744 	    number = get_start(file, 99);
745 
746 	  qprintf(fp, "		/bin/rm -f $rcdir/rc%c.d/%c%02d%s\n", *runlevels,
747 		  (*runlevels == '0' || *runlevels == '1' ||
748 		   *runlevels == '6') ? 'K' : 'S', number, file->dst);
749 	  qprintf(fp, "		/bin/ln -s %s/init.d/%s "
750 		      "$rcdir/rc%c.d/%c%02d%s\n", SoftwareDir, file->dst,
751 		  *runlevels,
752 		  (*runlevels == '0' || *runlevels == '1' ||
753 		   *runlevels == '6') ? 'K' : 'S', number, file->dst);
754 	}
755 
756 	qprintf(fp, "		%s/init.d/%s start\n", SoftwareDir, file->dst);
757       }
758     }
759 
760     fputs("	fi\n", fp);
761 
762     fputs("fi\n", fp);
763   }
764 
765   for (i = dist->num_files, file = dist->files; i > 0; i --, file ++)
766     if (tolower(file->type) == 'i' && file->subpackage == subpackage)
767       break;
768 
769   if (i)
770   {
771     have_commands = 1;
772 
773     fprintf(fp, "%%preun%s\n", name);
774     fputs("if test \"x$1\" = x0; then\n", fp);
775     fputs("	echo Cleaning up init scripts...\n", fp);
776 
777    /*
778     * Find where the frigging init scripts go...
779     */
780 
781     fputs("	rcdir=\"\"\n", fp);
782     fputs("	for dir in /sbin/rc.d /sbin /etc/rc.d /etc ; do\n", fp);
783     fputs("		if test -d $dir/rc3.d -o -h $dir/rc3.d; then\n", fp);
784     fputs("			rcdir=\"$dir\"\n", fp);
785     fputs("		fi\n", fp);
786     fputs("	done\n", fp);
787     fputs("	if test \"$rcdir\" = \"\" ; then\n", fp);
788     fputs("		echo Unable to determine location of startup scripts!\n", fp);
789     fputs("	else\n", fp);
790     for (; i > 0; i --, file ++)
791     {
792       if (tolower(file->type) == 'i' && file->subpackage == subpackage)
793       {
794 	qprintf(fp, "		%s/init.d/%s stop\n", SoftwareDir, file->dst);
795 
796 	fputs("		if test -d $rcdir/init.d; then\n", fp);
797 	qprintf(fp, "			/bin/rm -f $rcdir/init.d/%s\n", file->dst);
798 	fputs("		else\n", fp);
799 	fputs("			if test -d /etc/init.d; then\n", fp);
800 	qprintf(fp, "				/bin/rm -f /etc/init.d/%s\n", file->dst);
801 	fputs("			fi\n", fp);
802 	fputs("		fi\n", fp);
803 
804 	for (runlevels = get_runlevels(dist->files + i, "0123456");
805 	     isdigit(*runlevels & 255);
806 	     runlevels ++)
807 	{
808 	  if (*runlevels == '0')
809 	    number = get_stop(file, 0);
810 	  else
811 	    number = get_start(file, 99);
812 
813 	  qprintf(fp, "		/bin/rm -f $rcdir/rc%c.d/%c%02d%s\n", *runlevels,
814 		  (*runlevels == '0' || *runlevels == '1' ||
815 		   *runlevels == '6') ? 'K' : 'S', number, file->dst);
816 	}
817       }
818     }
819 
820     fputs("	fi\n", fp);
821 
822     fputs("fi\n", fp);
823   }
824   else
825     have_commands = 0;
826 
827   for (i = dist->num_commands, c = dist->commands; i > 0; i --, c ++)
828     if (c->type == COMMAND_PRE_REMOVE && c->subpackage == subpackage)
829       break;
830 
831   if (i > 0)
832   {
833     if (!have_commands)
834       fprintf(fp, "%%preun%s\n", name);
835 
836     for (; i > 0; i --, c ++)
837       if (c->type == COMMAND_PRE_REMOVE && c->subpackage == subpackage)
838 	fprintf(fp, "%s\n", c->command);
839   }
840 
841   for (i = dist->num_commands, c = dist->commands; i > 0; i --, c ++)
842     if (c->type == COMMAND_POST_REMOVE && c->subpackage == subpackage)
843       break;
844 
845   if (i > 0)
846   {
847     fprintf(fp, "%%postun%s\n", name);
848     for (; i > 0; i --, c ++)
849       if (c->type == COMMAND_POST_REMOVE && c->subpackage == subpackage)
850 	fprintf(fp, "%s\n", c->command);
851   }
852 
853  /*
854   * Description...
855   */
856 
857   for (i = 0; i < dist->num_descriptions; i ++)
858     if (dist->descriptions[i].subpackage == subpackage)
859       break;
860 
861   if (i < dist->num_descriptions)
862   {
863     fprintf(fp, "%%description %s\n", name);
864 
865     for (; i < dist->num_descriptions; i ++)
866       if (dist->descriptions[i].subpackage == subpackage)
867 	fprintf(fp, "%s\n", dist->descriptions[i].description);
868   }
869 
870  /*
871   * Files...
872   */
873 
874   fprintf(fp, "%%files%s\n", name);
875   for (i = dist->num_files, file = dist->files; i > 0; i --, file ++)
876     if (file->subpackage == subpackage)
877       switch (tolower(file->type))
878       {
879 	case 'c' :
880             fprintf(fp, "%%attr(%04o,%s,%s) %%config(noreplace) \"%s\"\n",
881 	            file->mode, file->user, file->group, file->dst);
882             break;
883 	case 'd' :
884             fprintf(fp, "%%attr(%04o,%s,%s) %%dir \"%s\"\n", file->mode,
885 	            file->user, file->group, file->dst);
886             break;
887 	case 'f' :
888 	case 'l' :
889             fprintf(fp, "%%attr(%04o,%s,%s) \"%s\"\n", file->mode, file->user,
890 	            file->group, file->dst);
891             break;
892 	case 'i' :
893 	    fprintf(fp, "%%attr(0555,root,root) \"%s/init.d/%s\"\n", SoftwareDir, file->dst);
894             break;
895       }
896 
897   return (0);
898 }
899