1/*
2 * Copyright (c) 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1.  Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 * 2.  Redistributions in binary form must reproduce the above copyright
13 *     notice, this list of conditions and the following disclaimer in the
14 *     documentation and/or other materials provided with the distribution.
15 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
16 *     contributors may be used to endorse or promote products derived from
17 *     this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * Portions of this software have been released under the following terms:
31 *
32 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC.
33 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY
34 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION
35 *
36 * To anyone who acknowledges that this file is provided "AS IS"
37 * without any express or implied warranty:
38 * permission to use, copy, modify, and distribute this file for any
39 * purpose is hereby granted without fee, provided that the above
40 * copyright notices and this notice appears in all source code copies,
41 * and that none of the names of Open Software Foundation, Inc., Hewlett-
42 * Packard Company or Digital Equipment Corporation be used
43 * in advertising or publicity pertaining to distribution of the software
44 * without specific, written prior permission.  Neither Open Software
45 * Foundation, Inc., Hewlett-Packard Company nor Digital
46 * Equipment Corporation makes any representations about the suitability
47 * of this software for any purpose.
48 *
49 * Copyright (c) 2007, Novell, Inc. All rights reserved.
50 * Redistribution and use in source and binary forms, with or without
51 * modification, are permitted provided that the following conditions
52 * are met:
53 *
54 * 1.  Redistributions of source code must retain the above copyright
55 *     notice, this list of conditions and the following disclaimer.
56 * 2.  Redistributions in binary form must reproduce the above copyright
57 *     notice, this list of conditions and the following disclaimer in the
58 *     documentation and/or other materials provided with the distribution.
59 * 3.  Neither the name of Novell Inc. nor the names of its contributors
60 *     may be used to endorse or promote products derived from this
61 *     this software without specific prior written permission.
62 *
63 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
64 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
65 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
66 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
67 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
68 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
69 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
70 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
71 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
72 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
73 *
74 * @APPLE_LICENSE_HEADER_END@
75 */
76
77/*
78**
79**  NAME
80**
81**      SYSDEP.C
82**
83**  FACILITY:
84**
85**      Interface Definition Language (IDL) Compiler
86**
87**  ABSTRACT:
88**
89**      Operating system dependencies.
90**
91**  VERSION: DCE 1.0
92**
93*/
94
95#include <nidl.h>
96
97#include <sys/types.h>
98#include <sys/stat.h>
99
100#ifndef MAX_INCLUSION_DEPTH
101# define MAX_INCLUSION_DEPTH 10
102#endif
103
104#ifndef HASPOPEN
105static int temp_count = 0;
106static char *temp_names[MAX_INCLUSION_DEPTH];
107
108char *sysdep_save_temp
109(
110    char *old_name
111)
112{
113        char new_name[64];
114        char *new_name_ptr;
115        sprintf(new_name,"IDL%02d.TMP", temp_count);
116        new_name_ptr = temp_names[temp_count] = NEW_VEC (char, strlen(new_name) + 1);
117        strlcpy(temp_names[temp_count++], new_name, strlen(new_name) + 1);
118        unlink(new_name);
119        if(rename(old_name, new_name))
120        {
121                error(NIDL_RENAMEFAILED,old_name,new_name);
122        }
123        return(new_name_ptr);
124}
125
126void sysdep_cleanup_temp
127()
128{
129        int i;
130        char *name;
131
132        for(i = 0; i < temp_count; i++)
133        {
134                name = temp_names[i];
135                free(temp_names[i]);
136                temp_names[i] = (char *) 0;
137                if(name)
138                {
139                        unlink(temp_names[i]);
140                }
141        }
142        temp_count = 0;
143}
144
145#endif
146
147#if defined(IDL_USE_OUTPUT_LINE)
148#undef fprintf
149
150/*
151** isidchar - tests whether a character is a valid identifier character:
152**            alphanumeric or an underscore.
153*/
154#define isidchar(c) \
155    (  (c) == '_' \
156    || ((c) >= 'a' && (c) <= 'z') \
157    || ((c) >= 'A' && (c) <= 'Z') \
158    || ((c) >= '0' && (c) <= '9') )
159
160/*
161** Choose an arbitrary large limit for the longest line length.
162** Sorry, but we have to have one.  Note that (LINE_BUFF_SIZE - MAX_C_LINE_LEN)
163** is the largest size output string that is guaranteed to fit in the buffer.
164*/
165#define LINE_BUFF_SIZE 2048
166#ifdef DUMPERS
167#define MAX_C_LINE_LEN   80
168#else
169#define MAX_C_LINE_LEN  132
170#endif
171#define MAX_FORMAT_LEN  255
172#define INDENT_SP_PER_LVL 2
173#include <stdarg.h>
174#include <stdio.h>
175
176static char out_buffer[LINE_BUFF_SIZE]; /* Buffer for outputs */
177static char new_format[MAX_FORMAT_LEN]; /* Buffer for modified format string */
178static int buff_len = 0;                /* Length of current data in buffer */
179static int indent_sp = 0;               /* Number of spaces to indent */
180static FILE * previous_fid = NULL;      /* File id from previous call */
181
182/*
183**  f l u s h _ o u t p u t _ l i n e
184**
185**  Routine that must be called after all output_line calls to a file are
186**  complete, to write any leftover data to the given file.
187**  It can be called at other times if desired.
188**
189**  Implicit inputs:  out_buffer, buff_len, previous_fid
190*/
191
192void flush_output_line
193(
194    FILE * fid                  /* [in] File handle */
195)
196{
197    /*
198    ** If fid does not match fid from last call to output_line, this is a noop
199    ** since output_line always calls this routine on a fid switch so that
200    ** data isn't lost for a previous fid.
201    */
202    if (fid != previous_fid)
203        return;
204
205    /*
206    ** Data always starts at out_buffer[0]; buff_len is length of data.
207    */
208    if (fid != NULL && buff_len > 0)
209    {
210        out_buffer[buff_len] = '\0';
211        fprintf (fid, "%s", out_buffer);
212        buff_len = 0;
213    }
214}
215
216/*
217**  o u t p u t _ l i n e
218**
219**  Replacement routine for fprintf() for use by the backend on platforms
220**  whose C compiler has a limit of N characters per source line.  A call to
221**  fprintf, or a series of calls to fprintf without a newline character, can
222**  cause source lines greater than N characters.  This routine assures that
223**  no source lines over N characters will be output by breaking long lines
224**  up into more than one line when necessary.
225**
226**  Implicit inputs:  out_buffer, buff_len, previous_fid
227*/
228
229int output_line
230(
231    FILE * fid,                 /* [in] File handle */
232    char *format,               /* [in] Format string */
233    ...                         /* [in] 0-N format arguments */
234)
235{
236    va_list args;
237    char *buff, *obuff, *cp, *pcp;
238    char temp;
239    int i, j, len, new_len;
240
241    va_start (args, format);
242
243    /*
244    ** If not the same fid as the last call, flush the output buffer for the
245    ** previous fid if it has any data.  This makes us lose track of that fid,
246    ** and a illegally long line becomes possible.
247    */
248    if (fid != previous_fid)
249    {
250        if (buff_len > 0)
251            flush_output_line(previous_fid);
252        previous_fid = fid;
253    }
254
255    /*
256    ** When data is buffered across calls, it always begins at out_buffer[0].
257    ** buff points to the next chunk of the data to be output.
258    ** buff_len counts the remaining number of bytes to be output.
259    */
260    buff = obuff = out_buffer;
261
262    /*
263    ** Munge the format string for indentation.
264    */
265    new_len = 0;
266
267    for (pcp = cp = format ; *cp ; cp++)
268    {
269        switch (*cp)
270        {
271        case '\n':
272            /*
273            ** Copy format string through newline [and insert indentation].
274            */
275            len = cp - pcp + 1;
276            strncpy(&new_format[new_len], pcp, len);
277            pcp = cp + 1;
278            new_len += len;
279            /*
280            ** Lookahead: special case logic for certain next characters.
281            */
282            if (cp[1] == '\0' || cp[1] == '#')
283                break;  /* Don't insert indentation */
284            j = ((cp[1] == '}') ? indent_sp - INDENT_SP_PER_LVL : indent_sp);
285            for (i = 0 ; i < j ; i++)
286                new_format[new_len++] = ' ';
287            break;
288
289        case '{':
290            indent_sp += INDENT_SP_PER_LVL;
291            break;
292
293        case '}':
294            /*
295            ** Reduce indent level in buf if '}' first char of format string.
296            */
297            if (cp == format && buff_len == indent_sp)
298                buff_len -= INDENT_SP_PER_LVL;
299            indent_sp -= INDENT_SP_PER_LVL;
300            break;
301
302        case '#':
303            /*
304            ** Remove indentation in buf if '#' first char of format string.
305            */
306            if (cp == format && buff_len == indent_sp)
307                buff_len = 0;
308            break;
309
310        default:
311            break;
312        }
313    }
314
315    strlcpy(&new_format[new_len], pcp, sizeof (new_format) - new_len);  /* Copy rest of format string */
316    buff_len += vsprintf (&buff[buff_len], new_format, args);
317
318    j = 0;  /* Counts quote characters in logic below */
319    while (buff_len > MAX_C_LINE_LEN)
320    {
321        /*
322        ** The buffer could contain embedded Newlines, in which case it is not
323        ** necessarily too long to output in a single fprintf.  To simplify the
324        ** logic, though, scan for Newlines within the line limit and if found,
325        ** do a separate fprintf and update pointers.
326        */
327        cp = strchr(buff, '\n');
328        if (cp != 0 && (cp - buff) <= MAX_C_LINE_LEN)
329        {
330            temp = *++cp;
331            *cp = '\0';
332            fprintf (fid, "%s", buff);
333        }
334        else
335        {
336#ifdef CONTINUATION_METHOD
337            /*
338            ** VAX C only allows the '\' continuation character in #defines,
339            ** tokens, and character strings.  Since we have no way of knowing
340            ** whether we're in a #define or a character string, we want to make
341            ** sure we break the line in the middle of a token.  The code below
342            ** assumes that we are guaranteed to find two consecutive token
343            ** characters when we scan back in the buffer, and does the line
344            ** break in between those two characters.
345            */
346            for (cp = &buff[MAX_C_LINE_LEN-1] ;; cp--)
347                if (isidchar(*cp) && isidchar(cp[-1]))
348                    break;
349#else
350            /*
351            ** Scan backwards for a "break" char that is not in a literal string
352            ** and output up through that break char followed by a newline.
353            */
354            char *dpos = NULL, *odpos, *endpos;
355
356            /*
357             * Find the rightmost delimiter and count quotes.
358             */
359            endpos = &buff[MAX_C_LINE_LEN-1];
360            temp = *endpos;             /* Save end char */
361            *endpos = '\0';             /* Temp make eos */
362
363            cp = buff;
364            while ((cp = strpbrk(cp, " .,-\"()")) != NULL)
365            {
366                if (*cp == '"')
367                {
368                    j++;
369                    odpos = dpos;       /* Save delim pos before this quote */
370                    dpos = cp;          /* Save as possible delimiter */
371                }
372                else if (*cp == '-' && *++cp != '>')
373                    continue;
374                else
375                    dpos = cp;
376                cp++;
377            }
378
379            *endpos = temp;             /* Restore end char */
380
381            /*
382             * Got the rightmost delimiter but if quote count is odd then it
383             * is within a quoted string so back off to the previous delimiter
384             * before the quote.
385             */
386            if (j%2 == 1 && odpos != NULL)
387                dpos = odpos;
388
389            if (dpos == NULL)
390                /* Didn't find a break char; punt and output entire line. */
391                cp = &buff[buff_len];
392            else
393                cp = dpos + 1;
394#endif
395
396            /*
397            ** cp now points at the character after where we want to split.
398            ** Put a temporary break in the buffer so we can print this section.
399            */
400            temp = *cp;
401            *cp = '\0';
402#ifdef CONTINUATION_METHOD
403            fprintf (fid, "%s\\\n", buff);
404#else
405            fprintf (fid, "%s\n", buff);
406#endif
407        }
408
409        /*
410        ** Restore the saved character, update length and pointers, try again.
411        */
412        *cp = temp;
413        buff_len = buff_len - ((uintptr_t)cp - (uintptr_t)buff);
414        buff = cp;
415    }
416
417    /*
418    ** Output the final part of the line.  buff points somewhere within
419    ** out_buffer, at the start of the final part of the line.
420    */
421    if (buff[buff_len-1] == '\n')
422    {
423        /*
424        ** The last character in the buffer is a newline.
425        ** Output the buffer now and reinitialize buffer and index.
426        */
427        fprintf (fid, "%s", buff);
428        for (buff_len = 0 ; buff_len < indent_sp ; obuff[buff_len++] = ' ')
429            ;
430    }
431    else
432    {
433        /*
434        ** The last character in the buffer is not a newline.
435        ** If the current data in the buffer starts at other than
436        ** out_buffer[0], shift it in the buffer so that it does.
437        */
438        if (buff != obuff)
439        {
440            i = buff_len;
441            while (i-- > 0)
442                *obuff++ = *buff++;
443        }
444    }
445}
446
447#endif
448