Home | History | Annotate | Line # | Download | only in makeinfo
      1 /*	$NetBSD: files.c,v 1.2 2016/01/14 00:34:53 christos Exp $	*/
      2 
      3 /* files.c -- file-related functions for makeinfo.
      4    Id: files.c,v 1.5 2004/07/27 00:06:31 karl Exp
      5 
      6    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software
      7    Foundation, Inc.
      8 
      9    This program is free software; you can redistribute it and/or modify
     10    it under the terms of the GNU General Public License as published by
     11    the Free Software Foundation; either version 2, or (at your option)
     12    any later version.
     13 
     14    This program is distributed in the hope that it will be useful,
     15    but WITHOUT ANY WARRANTY; without even the implied warranty of
     16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17    GNU General Public License for more details.
     18 
     19    You should have received a copy of the GNU General Public License
     20    along with this program; if not, write to the Free Software Foundation,
     21    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
     22 
     23 #include "system.h"
     24 #include "files.h"
     25 #include "html.h"
     26 #include "index.h"
     27 #include "macro.h"
     28 #include "makeinfo.h"
     29 #include "node.h"
     30 
     31 FSTACK *filestack = NULL;
     32 
     33 static int node_filename_stack_index = 0;
     34 static int node_filename_stack_size = 0;
     35 static char **node_filename_stack = NULL;
     36 
     37 /* Looking for include files.  */
     39 
     40 /* Given a string containing units of information separated by colons,
     41    return the next one pointed to by INDEX, or NULL if there are no more.
     42    Advance INDEX to the character after the colon. */
     43 static char *
     44 extract_colon_unit (char *string, int *index)
     45 {
     46   int start;
     47   int path_sep_char = PATH_SEP[0];
     48   int i = *index;
     49 
     50   if (!string || (i >= strlen (string)))
     51     return NULL;
     52 
     53   /* Each call to this routine leaves the index pointing at a colon if
     54      there is more to the path.  If i > 0, then increment past the
     55      `:'.  If i == 0, then the path has a leading colon.  Trailing colons
     56      are handled OK by the `else' part of the if statement; an empty
     57      string is returned in that case. */
     58   if (i && string[i] == path_sep_char)
     59     i++;
     60 
     61   start = i;
     62   while (string[i] && string[i] != path_sep_char) i++;
     63   *index = i;
     64 
     65   if (i == start)
     66     {
     67       if (string[i])
     68         (*index)++;
     69 
     70       /* Return "" in the case of a trailing `:'. */
     71       return xstrdup ("");
     72     }
     73   else
     74     {
     75       char *value;
     76 
     77       value = xmalloc (1 + (i - start));
     78       memcpy (value, &string[start], (i - start));
     79       value [i - start] = 0;
     80 
     81       return value;
     82     }
     83 }
     84 
     85 /* Return the full pathname for FILENAME by searching along PATH.
     86    When found, return the stat () info for FILENAME in FINFO.
     87    If PATH is NULL, only the current directory is searched.
     88    If the file could not be found, return a NULL pointer. */
     89 char *
     90 get_file_info_in_path (char *filename, char *path, struct stat *finfo)
     91 {
     92   char *dir;
     93   int result, index = 0;
     94 
     95   if (path == NULL)
     96     path = ".";
     97 
     98   /* Handle absolute pathnames.  */
     99   if (IS_ABSOLUTE (filename)
    100       || (*filename == '.'
    101           && (IS_SLASH (filename[1])
    102               || (filename[1] == '.' && IS_SLASH (filename[2])))))
    103     {
    104       if (stat (filename, finfo) == 0)
    105         return xstrdup (filename);
    106       else
    107         return NULL;
    108     }
    109 
    110   while ((dir = extract_colon_unit (path, &index)))
    111     {
    112       char *fullpath;
    113 
    114       if (!*dir)
    115         {
    116           free (dir);
    117           dir = xstrdup (".");
    118         }
    119 
    120       fullpath = xmalloc (2 + strlen (dir) + strlen (filename));
    121       sprintf (fullpath, "%s/%s", dir, filename);
    122       free (dir);
    123 
    124       result = stat (fullpath, finfo);
    125 
    126       if (result == 0)
    127         return fullpath;
    128       else
    129         free (fullpath);
    130     }
    131   return NULL;
    132 }
    133 
    134 /* Prepend and append new paths to include_files_path.  */
    135 void
    136 prepend_to_include_path (char *path)
    137 {
    138   if (!include_files_path)
    139     {
    140       include_files_path = xstrdup (path);
    141       include_files_path = xrealloc (include_files_path,
    142           strlen (include_files_path) + 3); /* 3 for ":.\0" */
    143       strcat (strcat (include_files_path, PATH_SEP), ".");
    144     }
    145   else
    146     {
    147       char *tmp = xstrdup (include_files_path);
    148       include_files_path = xrealloc (include_files_path,
    149           strlen (include_files_path) + strlen (path) + 2); /* 2 for ":\0" */
    150       strcpy (include_files_path, path);
    151       strcat (include_files_path, PATH_SEP);
    152       strcat (include_files_path, tmp);
    153       free (tmp);
    154     }
    155 }
    156 
    157 void
    158 append_to_include_path (char *path)
    159 {
    160   if (!include_files_path)
    161     include_files_path = xstrdup (".");
    162 
    163   include_files_path = (char *) xrealloc (include_files_path,
    164         2 + strlen (include_files_path) + strlen (path));
    165   strcat (include_files_path, PATH_SEP);
    166   strcat (include_files_path, path);
    167 }
    168 
    169 /* Remove the first path from the include_files_path.  */
    170 void
    171 pop_path_from_include_path (void)
    172 {
    173   int i = 0;
    174   char *tmp;
    175 
    176   if (include_files_path)
    177     for (i = 0; i < strlen (include_files_path)
    178         && include_files_path[i] != ':'; i++);
    179 
    180   /* Advance include_files_path to the next char from ':'  */
    181   tmp = (char *) xmalloc (strlen (include_files_path) - i);
    182   strcpy (tmp, (char *) include_files_path + i + 1);
    183 
    184   free (include_files_path);
    185   include_files_path = tmp;
    186 }
    187 
    188 /* Find and load the file named FILENAME.  Return a pointer to
    190    the loaded file, or NULL if it can't be loaded.  If USE_PATH is zero,
    191    just look for the given file (this is used in handle_delayed_writes),
    192    else search along include_files_path.   */
    193 
    194 char *
    195 find_and_load (char *filename, int use_path)
    196 {
    197   struct stat fileinfo;
    198   long file_size;
    199   int file = -1, count = 0;
    200   char *fullpath, *result;
    201   int n, bytes_to_read;
    202 
    203   result = fullpath = NULL;
    204 
    205   fullpath
    206     = get_file_info_in_path (filename, use_path ? include_files_path : NULL,
    207                              &fileinfo);
    208 
    209   if (!fullpath)
    210     goto error_exit;
    211 
    212   filename = fullpath;
    213   file_size = (long) fileinfo.st_size;
    214 
    215   file = open (filename, O_RDONLY);
    216   if (file < 0)
    217     goto error_exit;
    218 
    219   /* Load the file, with enough room for a newline and a null. */
    220   result = xmalloc (file_size + 2);
    221 
    222   /* VMS stat lies about the st_size value.  The actual number of
    223      readable bytes is always less than this value.  The arcane
    224      mysteries of VMS/RMS are too much to probe, so this hack
    225     suffices to make things work.  It's also needed on Cygwin.  And so
    226     we might as well use it everywhere.  */
    227   bytes_to_read = file_size;
    228   while ((n = read (file, result + count, bytes_to_read)) > 0)
    229     {
    230       count += n;
    231       bytes_to_read -= n;
    232     }
    233   if (0 < count && count < file_size)
    234     result = xrealloc (result, count + 2); /* why waste the slack? */
    235   else if (n == -1)
    236 error_exit:
    237     {
    238       if (result)
    239         free (result);
    240 
    241       if (fullpath)
    242         free (fullpath);
    243 
    244       if (file != -1)
    245         close (file);
    246 
    247       return NULL;
    248     }
    249   close (file);
    250 
    251   /* Set the globals to the new file. */
    252   input_text = result;
    253   input_text_length = count;
    254   input_filename = fullpath;
    255   node_filename = xstrdup (fullpath);
    256   input_text_offset = 0;
    257   line_number = 1;
    258   /* Not strictly necessary.  This magic prevents read_token () from doing
    259      extra unnecessary work each time it is called (that is a lot of times).
    260      INPUT_TEXT_LENGTH is one past the actual end of the text. */
    261   input_text[input_text_length] = '\n';
    262   /* This, on the other hand, is always necessary.  */
    263   input_text[input_text_length+1] = 0;
    264   return result;
    265 }
    266 
    267 /* Pushing and popping files.  */
    269 static void
    270 push_node_filename (void)
    271 {
    272   if (node_filename_stack_index + 1 > node_filename_stack_size)
    273     node_filename_stack = xrealloc
    274     (node_filename_stack, (node_filename_stack_size += 10) * sizeof (char *));
    275 
    276   node_filename_stack[node_filename_stack_index] = node_filename;
    277   node_filename_stack_index++;
    278 }
    279 
    280 static void
    281 pop_node_filename (void)
    282 {
    283   node_filename = node_filename_stack[--node_filename_stack_index];
    284 }
    285 
    286 /* Save the state of the current input file. */
    287 void
    288 pushfile (void)
    289 {
    290   FSTACK *newstack = xmalloc (sizeof (FSTACK));
    291   newstack->filename = input_filename;
    292   newstack->text = input_text;
    293   newstack->size = input_text_length;
    294   newstack->offset = input_text_offset;
    295   newstack->line_number = line_number;
    296   newstack->next = filestack;
    297 
    298   filestack = newstack;
    299   push_node_filename ();
    300 }
    301 
    302 /* Make the current file globals be what is on top of the file stack. */
    303 void
    304 popfile (void)
    305 {
    306   FSTACK *tos = filestack;
    307 
    308   if (!tos)
    309     abort ();                   /* My fault.  I wonder what I did? */
    310 
    311   if (macro_expansion_output_stream)
    312     {
    313       maybe_write_itext (input_text, input_text_offset);
    314       forget_itext (input_text);
    315     }
    316 
    317   /* Pop the stack. */
    318   filestack = filestack->next;
    319 
    320   /* Make sure that commands with braces have been satisfied. */
    321   if (!executing_string && !me_executing_string)
    322     discard_braces ();
    323 
    324   /* Get the top of the stack into the globals. */
    325   input_filename = tos->filename;
    326   input_text = tos->text;
    327   input_text_length = tos->size;
    328   input_text_offset = tos->offset;
    329   line_number = tos->line_number;
    330   free (tos);
    331 
    332   /* Go back to the (now) current node. */
    333   pop_node_filename ();
    334 }
    335 
    336 /* Flush all open files on the file stack. */
    337 void
    338 flush_file_stack (void)
    339 {
    340   while (filestack)
    341     {
    342       char *fname = input_filename;
    343       char *text = input_text;
    344       popfile ();
    345       free (fname);
    346       free (text);
    347     }
    348 }
    349 
    350 /* Return the index of the first character in the filename
    351    which is past all the leading directory characters.  */
    352 static int
    353 skip_directory_part (char *filename)
    354 {
    355   int i = strlen (filename) - 1;
    356 
    357   while (i && !IS_SLASH (filename[i]))
    358     i--;
    359   if (IS_SLASH (filename[i]))
    360     i++;
    361   else if (filename[i] && HAVE_DRIVE (filename))
    362     i = 2;
    363 
    364   return i;
    365 }
    366 
    367 static char *
    368 filename_non_directory (char *name)
    369 {
    370   return xstrdup (name + skip_directory_part (name));
    371 }
    372 
    373 /* Return just the simple part of the filename; i.e. the
    374    filename without the path information, or extensions.
    375    This conses up a new string. */
    376 char *
    377 filename_part (char *filename)
    378 {
    379   char *basename = filename_non_directory (filename);
    380 
    381 #ifdef REMOVE_OUTPUT_EXTENSIONS
    382   /* See if there is an extension to remove.  If so, remove it. */
    383   {
    384     char *temp = strrchr (basename, '.');
    385     if (temp)
    386       *temp = 0;
    387   }
    388 #endif /* REMOVE_OUTPUT_EXTENSIONS */
    389   return basename;
    390 }
    391 
    392 /* Return the pathname part of filename.  This can be NULL. */
    393 char *
    394 pathname_part (char *filename)
    395 {
    396   char *result = NULL;
    397   int i;
    398 
    399   filename = expand_filename (filename, "");
    400 
    401   i = skip_directory_part (filename);
    402   if (i)
    403     {
    404       result = xmalloc (1 + i);
    405       strncpy (result, filename, i);
    406       result[i] = 0;
    407     }
    408   free (filename);
    409   return result;
    410 }
    411 
    412 /* Return the full path to FILENAME. */
    413 static char *
    414 full_pathname (char *filename)
    415 {
    416   int initial_character;
    417   char *result;
    418 
    419   /* No filename given? */
    420   if (!filename || !*filename)
    421     return xstrdup ("");
    422 
    423   /* Already absolute? */
    424   if (IS_ABSOLUTE (filename) ||
    425       (*filename == '.' &&
    426        (IS_SLASH (filename[1]) ||
    427         (filename[1] == '.' && IS_SLASH (filename[2])))))
    428     return xstrdup (filename);
    429 
    430   initial_character = *filename;
    431   if (initial_character != '~')
    432     {
    433       char *localdir = xmalloc (1025);
    434 #ifdef HAVE_GETCWD
    435       if (!getcwd (localdir, 1024))
    436 #else
    437       if (!getwd (localdir))
    438 #endif
    439         {
    440           fprintf (stderr, _("%s: getwd: %s, %s\n"),
    441                    progname, filename, localdir);
    442           xexit (1);
    443         }
    444 
    445       strcat (localdir, "/");
    446       strcat (localdir, filename);
    447       result = xstrdup (localdir);
    448       free (localdir);
    449     }
    450   else
    451     { /* Does anybody know why WIN32 doesn't want to support $HOME?
    452          If the reason is they don't have getpwnam, they should
    453          only disable the else clause below.  */
    454 #ifndef WIN32
    455       if (IS_SLASH (filename[1]))
    456         {
    457           /* Return the concatenation of the environment variable HOME
    458              and the rest of the string. */
    459           char *temp_home;
    460 
    461           temp_home = (char *) getenv ("HOME");
    462           result = xmalloc (strlen (&filename[1])
    463                                     + 1
    464                                     + (temp_home ? strlen (temp_home) : 0));
    465           *result = 0;
    466 
    467           if (temp_home)
    468             strcpy (result, temp_home);
    469 
    470           strcat (result, &filename[1]);
    471         }
    472       else
    473         {
    474           struct passwd *user_entry;
    475           int i, c;
    476           char *username = xmalloc (257);
    477 
    478           for (i = 1; (c = filename[i]); i++)
    479             {
    480               if (IS_SLASH (c))
    481                 break;
    482               else
    483                 username[i - 1] = c;
    484             }
    485           if (c)
    486             username[i - 1] = 0;
    487 
    488           user_entry = getpwnam (username);
    489 
    490           if (!user_entry)
    491             return xstrdup (filename);
    492 
    493           result = xmalloc (1 + strlen (user_entry->pw_dir)
    494                                     + strlen (&filename[i]));
    495           strcpy (result, user_entry->pw_dir);
    496           strcat (result, &filename[i]);
    497         }
    498 #endif /* not WIN32 */
    499     }
    500   return result;
    501 }
    502 
    503 /* Return the expansion of FILENAME. */
    504 char *
    505 expand_filename (char *filename, char *input_name)
    506 {
    507   int i;
    508 
    509   if (filename)
    510     {
    511       filename = full_pathname (filename);
    512       if (IS_ABSOLUTE (filename)
    513 	  || (*filename == '.' &&
    514 	      (IS_SLASH (filename[1]) ||
    515 	       (filename[1] == '.' && IS_SLASH (filename[2])))))
    516 	return filename;
    517     }
    518   else
    519     {
    520       filename = filename_non_directory (input_name);
    521 
    522       if (!*filename)
    523         {
    524           free (filename);
    525           filename = xstrdup ("noname.texi");
    526         }
    527 
    528       for (i = strlen (filename) - 1; i; i--)
    529         if (filename[i] == '.')
    530           break;
    531 
    532       if (!i)
    533         i = strlen (filename);
    534 
    535       if (i + 6 > (strlen (filename)))
    536         filename = xrealloc (filename, i + 6);
    537       strcpy (filename + i, html ? ".html" : ".info");
    538       return filename;
    539     }
    540 
    541   if (IS_ABSOLUTE (input_name))
    542     {
    543       /* Make it so that relative names work. */
    544       char *result;
    545 
    546       i = strlen (input_name) - 1;
    547 
    548       result = xmalloc (1 + strlen (input_name) + strlen (filename));
    549       strcpy (result, input_name);
    550 
    551       while (!IS_SLASH (result[i]) && i)
    552         i--;
    553       if (IS_SLASH (result[i]))
    554         i++;
    555 
    556       strcpy (&result[i], filename);
    557       free (filename);
    558       return result;
    559     }
    560   return filename;
    561 }
    562 
    563 char *
    564 output_name_from_input_name (char *name)
    565 {
    566   return expand_filename (NULL, name);
    567 }
    568 
    569 
    570 /* Modify the file name FNAME so that it fits the limitations of the
    571    underlying filesystem.  In particular, truncate the file name as it
    572    would be truncated by the filesystem.  We assume the result can
    573    never be longer than the original, otherwise we couldn't be sure we
    574    have enough space in the original string to modify it in place.  */
    575 char *
    576 normalize_filename (char *fname)
    577 {
    578   int maxlen;
    579   char orig[PATH_MAX + 1];
    580   int i;
    581   char *lastdot, *p;
    582 
    583 #ifdef _PC_NAME_MAX
    584   maxlen = pathconf (fname, _PC_NAME_MAX);
    585   if (maxlen < 1)
    586 #endif
    587     maxlen = PATH_MAX;
    588 
    589   i = skip_directory_part (fname);
    590   if (fname[i] == '\0')
    591     return fname;	/* only a directory name -- don't modify */
    592   strcpy (orig, fname + i);
    593 
    594   switch (maxlen)
    595     {
    596       case 12:	/* MS-DOS 8+3 filesystem */
    597 	if (orig[0] == '.')	/* leading dots are not allowed */
    598 	  orig[0] = '_';
    599 	lastdot = strrchr (orig, '.');
    600 	if (!lastdot)
    601 	  lastdot = orig + strlen (orig);
    602 	strncpy (fname + i, orig, lastdot - orig);
    603 	for (p = fname + i;
    604 	     p < fname + i + (lastdot - orig) && p < fname + i + 8;
    605 	     p++)
    606 	  if (*p == '.')
    607 	    *p = '_';
    608 	*p = '\0';
    609 	if (*lastdot == '.')
    610 	  strncat (fname + i, lastdot, 4);
    611 	break;
    612       case 14:	/* old Unix systems with 14-char limitation */
    613 	strcpy (fname + i, orig);
    614 	if (strlen (fname + i) > 14)
    615 	  fname[i + 14] = '\0';
    616 	break;
    617       default:
    618 	strcpy (fname + i, orig);
    619 	if (strlen (fname) > maxlen - 1)
    620 	  fname[maxlen - 1] = '\0';
    621 	break;
    622     }
    623 
    624   return fname;
    625 }
    626 
    627 /* Delayed writing functions.  A few of the commands
    629    needs to be handled at the end, namely @contents,
    630    @shortcontents, @printindex and @listoffloats.
    631    These functions take care of that.  */
    632 static DELAYED_WRITE *delayed_writes = NULL;
    633 int handling_delayed_writes = 0;
    634 
    635 void
    636 register_delayed_write (char *delayed_command)
    637 {
    638   DELAYED_WRITE *new;
    639 
    640   if (!current_output_filename || !*current_output_filename)
    641     {
    642       /* Cannot register if we don't know what the output file is.  */
    643       warning (_("`%s' omitted before output filename"), delayed_command);
    644       return;
    645     }
    646 
    647   if (STREQ (current_output_filename, "-"))
    648     {
    649       /* Do not register a new write if the output file is not seekable.
    650          Let the user know about it first, though.  */
    651       warning (_("`%s' omitted since writing to stdout"), delayed_command);
    652       return;
    653     }
    654 
    655   /* Don't complain if the user is writing /dev/null, since surely they
    656      don't care, but don't register the delayed write, either.  */
    657   if (FILENAME_CMP (current_output_filename, NULL_DEVICE) == 0
    658       || FILENAME_CMP (current_output_filename, ALSO_NULL_DEVICE) == 0)
    659     return;
    660 
    661   /* We need the HTML header in the output,
    662      to get a proper output_position.  */
    663   if (!executing_string && html)
    664     html_output_head ();
    665   /* Get output_position updated.  */
    666   flush_output ();
    667 
    668   new = xmalloc (sizeof (DELAYED_WRITE));
    669   new->command = xstrdup (delayed_command);
    670   new->filename = xstrdup (current_output_filename);
    671   new->input_filename = xstrdup (input_filename);
    672   new->position = output_position;
    673   new->calling_line = line_number;
    674   new->node = current_node ? xstrdup (current_node): "";
    675 
    676   new->node_order = node_order;
    677   new->index_order = index_counter;
    678 
    679   new->next = delayed_writes;
    680   delayed_writes = new;
    681 }
    682 
    683 void
    684 handle_delayed_writes (void)
    685 {
    686   DELAYED_WRITE *temp = (DELAYED_WRITE *) reverse_list
    687     ((GENERIC_LIST *) delayed_writes);
    688   int position_shift_amount, line_number_shift_amount;
    689   char *delayed_buf;
    690 
    691   handling_delayed_writes = 1;
    692 
    693   while (temp)
    694     {
    695       delayed_buf = find_and_load (temp->filename, 0);
    696 
    697       if (output_paragraph_offset > 0)
    698         {
    699           error (_("Output buffer not empty."));
    700           return;
    701         }
    702 
    703       if (!delayed_buf)
    704         {
    705           fs_error (temp->filename);
    706           return;
    707         }
    708 
    709       output_stream = fopen (temp->filename, "w");
    710       if (!output_stream)
    711         {
    712           fs_error (temp->filename);
    713           return;
    714         }
    715 
    716       if (fwrite (delayed_buf, 1, temp->position, output_stream) != temp->position)
    717         {
    718           fs_error (temp->filename);
    719           return;
    720         }
    721 
    722       {
    723         int output_position_at_start = output_position;
    724         int line_number_at_start = output_line_number;
    725 
    726         /* In order to make warnings and errors
    727            refer to the correct line number.  */
    728         input_filename = temp->input_filename;
    729         line_number = temp->calling_line;
    730 
    731         execute_string ("%s", temp->command);
    732         flush_output ();
    733 
    734         /* Since the output file is modified, following delayed writes
    735            need to be updated by this amount.  */
    736         position_shift_amount = output_position - output_position_at_start;
    737         line_number_shift_amount = output_line_number - line_number_at_start;
    738       }
    739 
    740       if (fwrite (delayed_buf + temp->position, 1,
    741             input_text_length - temp->position, output_stream)
    742           != input_text_length - temp->position
    743           || fclose (output_stream) != 0)
    744         fs_error (temp->filename);
    745 
    746       /* Done with the buffer.  */
    747       free (delayed_buf);
    748 
    749       /* Update positions in tag table for nodes that are defined after
    750          the line this delayed write is registered.  */
    751       if (!html && !xml)
    752         {
    753           TAG_ENTRY *node;
    754           for (node = tag_table; node; node = node->next_ent)
    755             if (node->order > temp->node_order)
    756               node->position += position_shift_amount;
    757         }
    758 
    759       /* Something similar for the line numbers in all of the defined
    760          indices.  */
    761       {
    762         int i;
    763         for (i = 0; i < defined_indices; i++)
    764           if (name_index_alist[i])
    765             {
    766               char *name = ((INDEX_ALIST *) name_index_alist[i])->name;
    767               INDEX_ELT *index;
    768               for (index = index_list (name); index; index = index->next)
    769                 if ((no_headers || STREQ (index->node, temp->node))
    770                     && index->entry_number > temp->index_order)
    771                   index->output_line += line_number_shift_amount;
    772             }
    773       }
    774 
    775       /* Shift remaining delayed positions
    776          by the length of this write.  */
    777       {
    778         DELAYED_WRITE *future_write = temp->next;
    779         while (future_write)
    780           {
    781             if (STREQ (temp->filename, future_write->filename))
    782               future_write->position += position_shift_amount;
    783             future_write = future_write->next;
    784           }
    785       }
    786 
    787       temp = temp->next;
    788     }
    789 }
    790