]> git.ipfire.org Git - thirdparty/man-pages.git/blob - Makefile
9beca11def333208d2ac7e84b6546c29c364e399
[thirdparty/man-pages.git] / Makefile
1 ########################################################################
2 # Copyright (C) 2021 Alejandro Colomar <alx.manpages@gmail.com>
3 # SPDX-License-Identifier: GPL-2.0 OR LGPL-2.0
4 ########################################################################
5 # Conventions:
6 #
7 # - Follow "Makefile Conventions" from the "GNU Coding Standards" closely.
8 # However, when something could be improved, don't follow those.
9 # - Uppercase variables, when referring files, refer to files in this repo.
10 # - Lowercase variables, when referring files, refer to system files.
11 # - Lowercase variables starting with '_' refer to absolute paths,
12 # including $(DESTDIR).
13 # - Uppercase variables starting with '_' refer to temporary files produced
14 # in $builddir.
15 # - Variables ending with '_' refer to a subdir of their parent dir, which
16 # is in a variable of the same name but without the '_'. The subdir is
17 # named after this project: <*/man>.
18 # - Variables ending in '_rm' refer to files that can be removed (exist).
19 # - Variables ending in '_rmdir' refer to dirs that can be removed (exist).
20 # - Targets of the form '%-rm' remove their corresponding file '%'.
21 # - Targets of the form '%/.-rmdir' remove their corresponding dir '%/'.
22 # - Targets of the form '%/.' create their corresponding directory '%/'.
23 # - Every file or directory to be created depends on its parent directory.
24 # This avoids race conditions caused by `mkdir -p`. Only the root
25 # directories are created with parents.
26 # - The 'FORCE' target is used to make phony some variables that can't be
27 # .PHONY to avoid some optimizations.
28 #
29 ########################################################################
30
31 SHELL := /bin/bash -Eeuo pipefail
32
33
34 MAKEFLAGS += --no-print-directory
35 MAKEFLAGS += --warn-undefined-variables
36
37
38 srcdir := .
39 builddir := tmp
40 DESTDIR :=
41 prefix := /usr/local
42
43 SYSCONFDIR := $(srcdir)/etc
44 TMACDIR := $(SYSCONFDIR)/groff/tmac
45 MANDIR := $(srcdir)
46 MAN0DIR := $(MANDIR)/man0
47 MAN1DIR := $(MANDIR)/man1
48 MAN2DIR := $(MANDIR)/man2
49 MAN3DIR := $(MANDIR)/man3
50 MAN4DIR := $(MANDIR)/man4
51 MAN5DIR := $(MANDIR)/man5
52 MAN6DIR := $(MANDIR)/man6
53 MAN7DIR := $(MANDIR)/man7
54 MAN8DIR := $(MANDIR)/man8
55
56 datarootdir := $(prefix)/share
57 docdir := $(datarootdir)/doc
58 mandir := $(datarootdir)/man
59 man0dir := $(mandir)/man0
60 man1dir := $(mandir)/man1
61 man2dir := $(mandir)/man2
62 man3dir := $(mandir)/man3
63 man4dir := $(mandir)/man4
64 man5dir := $(mandir)/man5
65 man6dir := $(mandir)/man6
66 man7dir := $(mandir)/man7
67 man8dir := $(mandir)/man8
68 manext := \.[0-9]\w*
69 man0ext := .0
70 man1ext := .1
71 man2ext := .2
72 man2type_ext:= .2type
73 man3ext := .3
74 man3type_ext:= .3type
75 man4ext := .4
76 man5ext := .5
77 man6ext := .6
78 man7ext := .7
79 man8ext := .8
80 htmldir := $(docdir)
81 htmlext := .html
82
83 htmldir_ := $(htmldir)/man
84
85 _LINTDIR := $(builddir)/lint
86 _HTMLDIR := $(builddir)/html
87 _SRCDIR := $(builddir)/src
88
89 _mandir := $(DESTDIR)$(mandir)
90 _htmldir := $(DESTDIR)$(htmldir_)
91
92
93 DEFAULT_CHECKPATCHFLAGS :=
94 EXTRA_CHECKPATCHFLAGS :=
95 CHECKPATCHFLAGS := $(DEFAULT_CHECKPATCHFLAGS) $(EXTRA_CHECKPATCHFLAGS)
96
97 clang-tidy_config := $(SYSCONFDIR)/clang-tidy/config.yaml
98 DEFAULT_CLANG-TIDYFLAGS := --config-file=$(clang-tidy_config)
99 DEFAULT_CLANG-TIDYFLAGS += --quiet
100 DEFAULT_CLANG-TIDYFLAGS += --use-color
101 EXTRA_CLANG-TIDYFLAGS :=
102 CLANG-TIDYFLAGS := $(DEFAULT_CLANG-TIDYFLAGS) $(EXTRA_CLANG-TIDYFLAGS)
103
104 DEFAULT_CPPLINTFLAGS :=
105 EXTRA_CPPLINTFLAGS :=
106 CPPLINTFLAGS := $(DEFAULT_CPPLINTFLAGS) $(EXTRA_CPPLINTFLAGS)
107
108 DEFAULT_IWYUFLAGS := -Xiwyu --no_fwd_decls
109 DEFAULT_IWYUFLAGS += -Xiwyu --error
110 EXTRA_IWYUFLAGS :=
111 IWYUFLAGS := $(DEFAULT_IWYUFLAGS) $(EXTRA_IWYUFLAGS)
112
113 DEFAULT_CPPFLAGS :=
114 EXTRA_CPPFLAGS :=
115 CPPFLAGS := $(DEFAULT_CPPFLAGS) $(EXTRA_CPPFLAGS)
116
117 DEFAULT_CFLAGS := -std=gnu17
118 DEFAULT_CFLAGS += -Wall
119 DEFAULT_CFLAGS += -Wextra
120 DEAFULT_CFLAGS += -Wstrict-prototypes
121 DEFAULT_CFLAGS += -Werror
122 DEFAULT_CFLAGS += -Wno-error=unused-parameter
123 DEFAULT_CFLAGS += -Wno-error=sign-compare
124 DEFAULT_CFLAGS += -Wno-error=format
125 DEFAULT_CFLAGS += -Wno-error=uninitialized
126 EXTRA_CFLAGS :=
127 CFLAGS := $(DEFAULT_CFLAGS) $(EXTRA_CFLAGS)
128
129 DEFAULT_LDFLAGS := -Wl,--as-needed
130 DEFAULT_LDFLAGS += -Wl,--no-allow-shlib-undefined
131 DEFAULT_LDFLAGS += -Wl,--no-copy-dt-needed-entries
132 DEFAULT_LDFLAGS += -Wl,--no-undefined
133 EXTRA_LDFLAGS :=
134 LDFLAGS := $(DEFAULT_LDFLAGS) $(EXTRA_LDFLAGS)
135
136 DEFAULT_LDLIBS := -lc
137 EXTRA_LDLIBS :=
138 LDLIBS := $(DEFAULT_LDLIBS) $(EXTRA_LDLIBS)
139
140 TMACFILES := $(sort $(shell find $(TMACDIR) -not -type d))
141 TMACNAMES := $(basename $(notdir $(TMACFILES)))
142 GROFF_CHECKSTYLE_LVL := 3
143 DEFAULT_GROFFFLAGS := -man
144 DEFAULT_GROFFFLAGS += -t
145 DEFAULT_GROFFFLAGS += -M $(TMACDIR)
146 DEFAULT_GROFFFLAGS += $(foreach x,$(TMACNAMES),-m $(x))
147 DEFAULT_GROFFFLAGS += -rCHECKSTYLE=$(GROFF_CHECKSTYLE_LVL)
148 DEFAULT_GROFFFLAGS += -ww
149 EXTRA_GROFFFLAGS :=
150 GROFFFLAGS := $(DEFAULT_GROFFFLAGS) $(EXTRA_GROFFFLAGS)
151
152 DEFAULT_MANDOCFLAGS := -man
153 DEFAULT_MANDOCFLAGS += -Tlint
154 EXTRA_MANDOCFLAGS :=
155 MANDOCFLAGS := $(DEFAULT_MANDOCFLAGS) $(EXTRA_MANDOCFLAGS)
156
157 DEFAULT_MAN2HTMLFLAGS :=
158 EXTRA_MAN2HTMLFLAGS :=
159 MAN2HTMLFLAGS := $(DEFAULT_MAN2HTMLFLAGS) $(EXTRA_MAN2HTMLFLAGS)
160
161
162 INSTALL := install
163 INSTALL_DATA := $(INSTALL) -m 644
164 INSTALL_DIR := $(INSTALL) -m 755 -d
165 MKDIR := mkdir -p
166 RM := rm
167 RMDIR := rmdir --ignore-fail-on-non-empty
168 CHECKPATCH := checkpatch
169 CLANG-TIDY := clang-tidy
170 CPPLINT := cpplint
171 IWYU := iwyu
172 CC := cc
173 LD := $(CC) $(CFLAGS)
174 GROFF := groff
175 MAN := man
176 MANDOC := mandoc
177 MAN2HTML := man2html
178
179
180 MAN_SECTIONS := 0 1 2 3 4 5 6 7 8
181
182
183 .PHONY: all
184 all:
185 $(MAKE) uninstall
186 $(MAKE) install
187
188 %/.:
189 $(info INSTALL $(@D)/)
190 $(INSTALL_DIR) $(@D)
191
192 %-rm:
193 $(info RM $*)
194 $(RM) $*
195
196 %-rmdir:
197 $(info RMDIR $(@D))
198 $(RMDIR) $(@D)
199
200
201 .PHONY: install
202 install: install-man | installdirs
203 @:
204
205 .PHONY: installdirs
206 installdirs: | installdirs-man
207 @:
208
209 .PHONY: uninstall remove
210 uninstall remove: uninstall-man
211 @:
212
213 .PHONY: clean
214 clean:
215 $(RM) -rf $(builddir)
216
217
218 ########################################################################
219 # man
220
221 MANPAGES := $(sort $(shell find $(MANDIR)/man?/ -type f | grep '$(manext)'))
222 LINTMAN := $(sort $(shell find $(MANDIR)/man?/ -type f | grep '$(manext)' \
223 | xargs grep -l '^\.TH '))
224 _HTMLPAGES := $(patsubst $(MANDIR)/%,$(_HTMLDIR)/%.html,$(MANPAGES))
225 _htmlpages := $(patsubst $(_HTMLDIR)/%,$(_htmldir)/%,$(_HTMLPAGES))
226 _manpages := $(patsubst $(MANDIR)/%,$(_mandir)/%,$(MANPAGES))
227 _man0pages := $(filter %$(man0ext),$(_manpages))
228 _man1pages := $(filter %$(man1ext),$(_manpages))
229 _man2pages := $(filter %$(man2ext),$(_manpages))
230 _man2pages += $(filter %$(man2type_ext),$(_manpages))
231 _man3pages := $(filter %$(man3ext),$(_manpages))
232 _man3pages += $(filter %$(man3type_ext),$(_manpages))
233 _man4pages := $(filter %$(man4ext),$(_manpages))
234 _man5pages := $(filter %$(man5ext),$(_manpages))
235 _man6pages := $(filter %$(man6ext),$(_manpages))
236 _man7pages := $(filter %$(man7ext),$(_manpages))
237 _man8pages := $(filter %$(man8ext),$(_manpages))
238 _LINT_groff :=$(patsubst $(MANDIR)/%,$(_LINTDIR)/%.lint.groff.touch,$(LINTMAN))
239 _LINT_mandoc:=$(patsubst $(MANDIR)/%,$(_LINTDIR)/%.lint.mandoc.touch,$(LINTMAN))
240 _SRCPAGEDIRS:=$(patsubst $(MANDIR)/%,$(_SRCDIR)/%.d,$(LINTMAN))
241 _UNITS_src :=$(sort $(patsubst $(MANDIR)/%,$(_SRCDIR)/%,$(shell \
242 find $(MANDIR)/man?/ -type f \
243 | grep '$(manext)$$' \
244 | xargs grep -l '^\.TH ' \
245 | while read m; do \
246 <$$m \
247 sed -n "s,^\... SRC BEGIN (\(.*.[ch]\))$$,$$m.d/\1,p"; \
248 done)))
249 _UNITS_h := $(filter %.h,$(_UNITS_src))
250 _UNITS_c := $(filter %.c,$(_UNITS_src))
251 _UNITS_o := $(patsubst %.c,%.o,$(_UNITS_c))
252 _UNITS_bin := $(patsubst %.c,%,$(_UNITS_c))
253 _LINT_checkpatch := $(patsubst %.c,%.lint.checkpatch.touch,$(_UNITS_c))
254 _LINT_clang-tidy := $(patsubst %.c,%.lint.clang-tidy.touch,$(_UNITS_c))
255 _LINT_cpplint := $(patsubst %.c,%.lint.cpplint.touch,$(_UNITS_c))
256 _LINT_iwyu := $(patsubst %.c,%.lint.iwyu.touch,$(_UNITS_c))
257
258 MANDIRS := $(sort $(shell find $(MANDIR)/man? -type d))
259 _HTMLDIRS := $(patsubst $(MANDIR)/%,$(_HTMLDIR)/%/.,$(MANDIRS))
260 _LINTDIRS := $(patsubst $(MANDIR)/%,$(_LINTDIR)/%/.,$(MANDIRS))
261 _SRCDIRS := $(patsubst $(MANDIR)/%,$(_SRCDIR)/%/.,$(MANDIRS))
262 _htmldirs := $(patsubst $(_HTMLDIR)/%,$(_htmldir)/%,$(_HTMLDIRS))
263 _mandirs := $(patsubst $(MANDIR)/%,$(_mandir)/%/.,$(MANDIRS))
264 _man0dir := $(filter %man0/.,$(_mandirs))
265 _man1dir := $(filter %man1/.,$(_mandirs))
266 _man2dir := $(filter %man2/.,$(_mandirs))
267 _man3dir := $(filter %man3/.,$(_mandirs))
268 _man4dir := $(filter %man4/.,$(_mandirs))
269 _man5dir := $(filter %man5/.,$(_mandirs))
270 _man6dir := $(filter %man6/.,$(_mandirs))
271 _man7dir := $(filter %man7/.,$(_mandirs))
272 _man8dir := $(filter %man8/.,$(_mandirs))
273
274 _htmlpages_rm := $(addsuffix -rm,$(wildcard $(_htmlpages)))
275 _man0pages_rm := $(addsuffix -rm,$(wildcard $(_man0pages)))
276 _man1pages_rm := $(addsuffix -rm,$(wildcard $(_man1pages)))
277 _man2pages_rm := $(addsuffix -rm,$(wildcard $(_man2pages)))
278 _man3pages_rm := $(addsuffix -rm,$(wildcard $(_man3pages)))
279 _man4pages_rm := $(addsuffix -rm,$(wildcard $(_man4pages)))
280 _man5pages_rm := $(addsuffix -rm,$(wildcard $(_man5pages)))
281 _man6pages_rm := $(addsuffix -rm,$(wildcard $(_man6pages)))
282 _man7pages_rm := $(addsuffix -rm,$(wildcard $(_man7pages)))
283 _man8pages_rm := $(addsuffix -rm,$(wildcard $(_man8pages)))
284
285 _htmldirs_rmdir := $(addsuffix -rmdir,$(wildcard $(_htmldirs)))
286 _mandirs_rmdir := $(addsuffix -rmdir,$(wildcard $(_mandirs)))
287 _man0dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man0dir)))
288 _man1dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man1dir)))
289 _man2dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man2dir)))
290 _man3dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man3dir)))
291 _man4dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man4dir)))
292 _man5dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man5dir)))
293 _man6dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man6dir)))
294 _man7dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man7dir)))
295 _man8dir_rmdir := $(addsuffix -rmdir,$(wildcard $(_man8dir)))
296 _mandir_rmdir := $(addsuffix -rmdir,$(wildcard $(_mandir)/.))
297 _htmldir_rmdir := $(addsuffix -rmdir,$(wildcard $(_htmldir)/.))
298
299 install_manX := $(foreach x,$(MAN_SECTIONS),install-man$(x))
300 installdirs_manX := $(foreach x,$(MAN_SECTIONS),installdirs-man$(x))
301 uninstall_manX := $(foreach x,$(MAN_SECTIONS),uninstall-man$(x))
302
303
304 .SECONDEXPANSION:
305 $(_manpages): $(_mandir)/man%: $(MANDIR)/man% | $$(@D)/.
306 $(info INSTALL $@)
307 $(INSTALL_DATA) -T $< $@
308
309 $(_mandirs): %/.: | $$(dir %). $(_mandir)/.
310
311 $(_mandirs_rmdir): $(_mandir)/man%/.-rmdir: $$(_man%pages_rm) FORCE
312 $(_mandir_rmdir): $(uninstall_manX) FORCE
313
314
315 .PHONY: $(install_manX)
316 $(install_manX): install-man%: $$(_man%pages) | installdirs-man%
317 @:
318
319 .PHONY: install-man
320 install-man: $(install_manX)
321 @:
322
323 .PHONY: $(installdirs_manX)
324 $(installdirs_manX): installdirs-man%: $$(_man%dir)
325 @:
326
327 .PHONY: installdirs-man
328 installdirs-man: $(installdirs_manX)
329 @:
330
331 .PHONY: $(uninstall_manX)
332 $(uninstall_manX): uninstall-man%: $$(_man%pages_rm) $$(_man%dir_rmdir)
333 @:
334
335 .PHONY: uninstall-man
336 uninstall-man: $(_mandir_rmdir) $(uninstall_manX)
337 @:
338
339
340 ########################################################################
341 # dist
342
343 DISTNAME := $(shell git describe 2>/dev/null)
344 DISTFILE := $(builddir)/$(DISTNAME).tar
345 compression := gz xz
346 dist := $(foreach x,$(compression),dist-$(x))
347
348
349 $(DISTFILE): $(shell git ls-files 2>/dev/null) | $$(@D)/.
350 $(info TAR $@)
351 tar cf $@ -T /dev/null
352 git ls-files \
353 | xargs tar rf $@
354
355 $(DISTFILE).gz: %.gz: % | $$(@D)/.
356 $(info GZIP $@)
357 gzip -knf $<
358
359 $(DISTFILE).xz: %.xz: % | $$(@D)/.
360 $(info XZ $@)
361 xz -kf $<
362
363
364 .PHONY: dist-tar
365 dist-tar: $(DISTFILE) | builddirs-dist
366 @:
367
368 .PHONY: $(dist)
369 $(dist): dist-%: $(DISTFILE).% | builddirs-dist
370 @:
371
372 .PHONY: builddirs-dist
373 builddirs-dist: $(builddir)/.
374 @:
375
376 .PHONY: dist
377 dist: $(dist)
378 @:
379
380
381 ########################################################################
382 # src
383
384 $(_SRCPAGEDIRS): $(_SRCDIR)/%.d: $(MANDIR)/% | $$(@D)/.
385 $(info MKDIR $@)
386 $(MKDIR) $@
387 touch $@
388
389 $(_UNITS_src): $$(patsubst $(_SRCDIR)/%.d,$(MANDIR)/%,$$(@D)) | $$(@D)
390 $(_UNITS_c): $$(filter $$(@D)/%.h,$(_UNITS_h))
391 $(_UNITS_src):
392 $(info SED $@)
393 <$< \
394 sed -n \
395 -e '/^\.TH/,/^\.SH/{/^\.SH/!p}' \
396 -e '/^\.SH EXAMPLES/p' \
397 -e "/^\... SRC BEGIN ($(@F))$$/,/^\... SRC END$$/p" \
398 | $(MAN) -P cat -l - \
399 | sed '/^[^ ]/d' \
400 | sed 's/^ //' \
401 >$@
402
403 $(_UNITS_o): $(_SRCDIR)/%.o: $(_SRCDIR)/%.c
404 $(info CC $@)
405 $(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $<
406
407 $(_UNITS_bin): $(_SRCDIR)/%: $(_SRCDIR)/%.o
408 $(info LD $@)
409 $(LD) $(LDFLAGS) -o $@ $< $(LDLIBS)
410
411 $(_SRCDIRS): %/.: | $$(dir %). $(_SRCDIR)/.
412
413
414 .PHONY: build-src src
415 build-src src: $(_UNITS_c) | builddirs-src
416 @:
417
418 .PHONY: build-cc
419 build-cc: $(_UNITS_o)
420 @:
421
422 .PHONY: build-ld
423 build-ld: $(_UNITS_bin)
424 @:
425
426 .PHONY: builddirs-src
427 builddirs-src: $(_SRCDIRS)
428 @:
429
430
431 ########################################################################
432 # lint
433
434 linters := checkpatch clang-tidy cpplint iwyu groff mandoc
435 lint := $(foreach x,$(linters),lint-$(x))
436
437 $(_LINT_checkpatch): %.lint.checkpatch.touch: %.c
438 $(info LINT (checkpatch) $@)
439 $(CHECKPATCH) $(CHECKPATCHFLAGS) -f $<
440 touch $@
441
442 $(_LINT_clang-tidy): %.lint.clang-tidy.touch: %.c
443 $(info LINT (clang-tidy) $@)
444 $(CLANG-TIDY) $(CLANG-TIDYFLAGS) $< -- $(CPPFLAGS) $(CFLAGS) 2>&1 \
445 | sed '/generated\.$$/d'
446 touch $@
447
448 $(_LINT_cpplint): %.lint.cpplint.touch: %.c
449 $(info LINT (cpplint) $@)
450 $(CPPLINT) $(CPPLINTFLAGS) $< >/dev/null
451 touch $@
452
453 $(_LINT_iwyu): %.lint.iwyu.touch: %.c
454 $(info LINT (iwyu) $@)
455 $(IWYU) $(IWYUFLAGS) $(CPPFLAGS) $(CFLAGS) $< 2>&1 \
456 | tac \
457 | sed '/correct/{N;d}' \
458 | tac
459 touch $@
460
461 $(_LINT_groff): $(_LINTDIR)/%.lint.groff.touch: $(MANDIR)/% | $$(@D)/.
462 $(info LINT (groff) $@)
463 $(GROFF) $(GROFFFLAGS) -z $<
464 touch $@
465
466 $(_LINT_mandoc): $(_LINTDIR)/%.lint.mandoc.touch: $(MANDIR)/% | $$(@D)/.
467 $(info LINT (mandoc) $@)
468 $(MANDOC) $(MANDOCFLAGS) $<
469 touch $@
470
471 $(_LINTDIRS): %/.: | $$(dir %). $(_LINTDIR)/.
472
473
474 .PHONY: $(lint)
475 $(lint): lint-%: $$(_LINT_%) | lintdirs
476 @:
477
478 .PHONY: lintdirs
479 lintdirs: $(_LINTDIRS) $(_SRCDIRS)
480 @:
481
482 .PHONY: lint
483 lint: $(lint)
484 @:
485
486
487 ########################################################################
488 # html
489
490 # Use with
491 # make MAN2HTMLFLAGS=whatever html
492 # The sed removes the lines "Content-type: text/html\n\n"
493 $(_HTMLPAGES): $(_HTMLDIR)/%.html: $(MANDIR)/% | $$(@D)/.
494 $(info MAN2HTML $@)
495 $(MAN2HTML) $(MAN2HTMLFLAGS) $< | sed -e 1,2d >$@
496
497 $(_HTMLDIRS): %/.: | $$(dir %). $(_HTMLDIR)/.
498
499 $(_htmlpages): $(_htmldir)/%: $(_HTMLDIR)/% | $$(@D)/.
500 $(info INSTALL $@)
501 $(INSTALL_DATA) -T $< $@
502
503 $(_htmldirs): %/.: | $$(dir %). $(_htmldir)/.
504
505
506 .PHONY: build-html html
507 build-html html: $(_HTMLPAGES) | builddirs-html
508 @:
509
510 .PHONY: builddirs-html
511 builddirs-html: $(_HTMLDIRS)
512 @:
513
514 .PHONY: install-html
515 install-html: $(_htmlpages) | installdirs-html
516 @:
517
518 .PHONY: installdirs-html
519 installdirs-html: $(_htmldirs)
520 @:
521
522 .PHONY: uninstall-html
523 uninstall-html: $(_htmldir_rmdir) $(_htmldirs_rmdir) $(_htmlpages_rm)
524 @:
525
526
527 ########################################################################
528
529 $(V).SILENT:
530 FORCE: