Module: environment-project-wizard Author: Hugh Greene Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND /// ---------------------------------------------------------------------- /// PROCESSING INTERCHANGE FORMAT FILES /// Single header pairs // Writing define inline function write-single-value-header-pair (stream :: , format-string :: , key :: , value :: ) format(stream, format-string, key, value); end function; define method write-header-pair (stream :: , format-string :: , key :: , value :: ) when (value) write-single-value-header-pair(stream, format-string, key, value); end; end method; define method write-header-pair (stream :: , format-string :: , key :: , values :: ) unless (empty?(values)) format(stream, format-string, key, values[0]); for (index from 1 below size(values)) format(stream, "\t%s\n", values[index]); end; end; end method; // This method is here to override the one on "values :: ". define method write-header-pair (stream :: , format-string :: , key :: , value :: ) write-single-value-header-pair(stream, format-string, key, value); end method; define function write-headers (stream :: , headers :: /* of: */, #key header-order :: false-or( /* of: */)) => () // Collect the key-value pairs from the header
s into one // , obeying any supplied header-order. // The header-order, when non-#f, is a vector of keys, whose key-value // pairs must appear in that order, before any other keys. let headers-vector = make(); if (header-order & ~empty?(header-order)) // This sort algorithm keeps a separate "bin" for each key in the // header-order, plus one for all other keys, which are to go to // the end. We just iterate over all the key-value pairs copying // them into the appropriate bin, and then concatenate all the bins // in order. It's sort[;)] of a "two-space insertion sort". // First initialize the "bins" to empty s. let order-bins = make(
, size: size(header-order) + 1); for (key in header-order) when (key) // #f is invalid, and reserved for the "others" bin below. order-bins[key] := make(); end; end; order-bins[#f] := make(); // Now copy the key-value pairs into the appropriate bins. for (header in headers) for (value keyed-by key in header) let bin = element(order-bins, key, default: order-bins[#f]); add!(bin, pair(key, value)); end; end; // Now flatten all the bins into the single headers-vector. for (key in header-order) when (key) // #f is invalid, and reserved for the "others" bin. headers-vector := concatenate!(headers-vector, order-bins[key]); end; end; headers-vector := concatenate!(headers-vector, order-bins[#f]); else for (header in headers) for (value keyed-by key in header) add!(headers-vector, pair(key, value)); end; end; end; // We measure the longest key for pretty-printing, and collect the key-value // pairs into a vector after stringifying and capitalizing the keys. let key-size = 0; for (header-pair in headers-vector) let string-key = concatenate(string-capitalize(as(, head(header-pair))), ":"); key-size := max(key-size, size(string-key)); head(header-pair) := string-key; end; let format-string = format-to-string("%%-%ds %%s\n", key-size); for (header-pair in headers-vector) write-header-pair(stream, format-string, head(header-pair), tail(header-pair)); end; new-line(stream); end function; /// Whole files // 'headers' will not be destructively modified by this operation. define function process-interchange-file-headers (stream :: , #key direction, headers :: false-or( /* of:
*/), header-order, #all-keys) => (header :: false-or(
)) select (direction) #"output" => when (headers) write-headers(stream, headers, header-order: header-order); #f end; end; end function; define macro with-open-interchange-file { with-open-interchange-file (?stream:variable = ?locator:expression, ?headers:variable = ?headers-val:expression, #rest ?keys:*, #key, #all-keys) ?:body end } => { begin with-open-file (?stream = ?locator, ?keys) let ?headers = process-interchange-file-headers (?stream, headers: ?headers-val, ?keys); ?body end end } end macro; define sealed method write-interchange-file (location :: , header-groups :: /* of:
*/, lines :: false-or() /* of: */, #rest keys, #key, #all-keys) => () // --- I'd like to use with-open-[interchange-]file here, but // I can't work out a way to pass in the "options". let _stream = #f; block () _stream := apply(make, , locator: location, direction: #"output", keys); let stream :: = _stream; let headers = apply(process-interchange-file-headers, stream, headers: header-groups, direction: #"output", keys); when (lines) for (line in lines) write-line(stream, line); end; end; cleanup if (_stream & stream-open?(_stream)) close(_stream) end; end; end method;